数据结构_第十关:二叉树的顺序结构——堆

news2024/11/24 19:45:21

目录

1. 二叉树的顺序结构

2.堆的概念及结构

3.堆的实现

3.1堆向下调整算法

 3.2堆的创建

3.3堆的插入

3.4建堆的复杂度

3.5堆的删除

4.堆的代码实现

4.1堆的定义

4.2堆的函数实现

1)堆的初始化

2)堆的销毁

3)堆的插入

4)堆的删除

5)取堆顶的数据

6)堆的数据个数

7)堆的判空

8)堆的构建(给你一个数组,用给定的数组建堆)(不用push建堆)

4.3堆的源代码和测试(vs2022下编译)

5.堆排序

6.TOP-K问题


1. 二叉树的顺序结构

普通的二叉树是不适合用数组来存储的,因为可能会存在大量的空间浪费。而完全二叉树更适合使用顺序结 构存储。现实中我们通常把堆(一种二叉树)使用顺序结构的数组来存储,需要注意的是  这里的堆  和  操作系统虚拟进程地址空间中的堆  是两回事,一个是数据结构,一个是操作系统中管理内存的一块区域分段

2.堆的概念及结构

如果有一个关键码的集合K = { k0,k1 ,k2 ,…,kn-1},把它的所有元素按完全二叉树的顺序存储方式存储 在一个一维数组中,并满足:i=0,1,2...

则称为小堆(或大堆)。将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。

人话说就是:
大堆:树中所有 父节点  都  大于或等于  子节点
小堆:树中所有 父节点  都  小于或等于  子节点

孩子和小标之前有一个关系:

  • leftchild = parent*2+1
  • rightchild = parent*2+2
  • parent = (child-1)/2

 选择题

1.下列关键字序列为堆的是:()
A 100,60,70,50,32,65
B 60,70,65,50,32,100
C 65,100,70,32,50,60
D 70,65,100,32,50,60
E 32,50,100,70,65,60
F 50,100,70,65,60,32


2.已知小根堆为8,15,10,21,34,16,12,删除关键字 8 之后需重建堆,在此过程中,关键字之间的比较次
数是()。
A 1
B 2
C 3
D 4


3.一组记录排序码为(5 11 7 2 3 17),则利用堆排序方法建立的初始堆为
A(11 5 7 2 3 17)
B(11 5 7 2 17 3)
C(17 11 7 2 3 5)
D(17 11 7 5 3 2)
E(17 7 11 3 5 2)
F(17 7 11 3 2 5)


4.最小堆[0,3,2,5,7,4,6,8],在删除堆顶元素0之后,其结果是()
A[3,2,5,7,4,6,8]
B[2,3,5,7,4,6,8]
C[2,3,4,5,7,8,6]
D[2,3,4,5,6,7,8]

答案:

A

C

C

C

3.堆的实现

3.1堆向下调整算法

现在我们给出一个数组,逻辑上看做一颗完全二叉树。

我们通过从根节点开始的向下调整算法可以把它调整成一个小堆。

向下调整算法:

  • 小堆:父节点 比 孩子节点 小,往下换,换孩子中大的那个
  • 大堆:父节点 比 孩子结点 大,往下换,换孩子中小的那个

int array[] = {27,15,19,18,28,34,65,49,25,37};

 3.2堆的创建

下面我们给出一个数组,这个数组逻辑上可以看做一颗完全二叉树,但是还不是一个堆,现在我们通过算法,把它构建成一个堆。

这里我们插入每插入一个数,就和父节点进行比较,建立一个大堆如下:

3.3堆的插入

先插入一个10到数组的尾上,再进行向上调整算法,直到满足堆。

3.4建堆的复杂度

因为堆是完全二叉树,而满二叉树也是完全二叉树,此处为了简化使用满二叉树来证明
(时间复杂度本来看的 就是近似值,多几个节点不影响最终结果)

向上调整建堆的时间复杂度为O(N*logN)。

向下调整建堆的时间复杂度为O(N)。

所以堆的创建一般选取像下调整建堆

最少为0,
最多为:二叉树的层数H

3.5堆的删除

删除堆是删除堆顶的数据

堆的删除不能往前挪动覆盖,因为首先数组的挪动覆盖 比较慢,而后,堆的关系会乱。

删最后一个就不会改变堆的关系所以,将堆顶的数据根最后一个数据一换,然后删除数组最后一个数据,再进行向下调整算法。

4.堆的代码实现

4.1堆的定义

typedef int HPDataType;
typedef struct Heap
{
	HPDataType* a;
	int size;
	int capacity;
}HP;

//打印
void HeapPrint(HP* hp);

// 堆的初始化
void HeapInit(HP* hp);

// 堆的销毁
void HeapDestory(HP* hp);

// 堆的插入
void HeapPush(HP* hp, HPDataType x);

// 堆的删除
void HeapPop(HP* hp);

// 取堆顶的数据
HPDataType HeapTop(HP* hp);

// 堆的数据个数
int HeapSize(HP* hp);

// 堆的判空
bool HeapEmpty(HP* hp);

//堆的构建(给你一个数组,用给定的数组建堆)
void HeapCreate(Heap* hp, HPDataType* a, int n);

4.2堆的函数实现

1)堆的初始化

void HeapInit(HP* hp)
{
	assert(hp);
	hp->a = NULL;
	hp->size = hp->capacity = 0;
}

2)堆的销毁

void HeapDestory(HP* hp)
{
	assert(hp);
	free(hp->a);
	hp->a = NULL;
	hp->size = hp->capacity = 0;
}

3)堆的插入

void AdjustUp(HPDataType* a, int child)
{
	int parent = (child - 1) / 2;

	while (child > 0)
	{
		//这里换为 < 是小堆的创建
		if (a[child] > a[parent])
		{
			//交换
			HPDataType temp = a[child];
			a[child] = a[parent];
			a[parent] = temp;

			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}
}
// 堆的插入
void HeapPush(HP* hp, HPDataType x)
{
	assert(hp);
	
	//扩容
	if (hp->size == hp->capacity)
	{
		int newcapacity = hp->capacity == 0 ? 4 : hp->capacity * 2;
		HPDataType* tmp = (HPDataType*)realloc(hp->a, sizeof(HPDataType) * newcapacity);
		if (tmp == NULL)
		{
			perror("realloc fail");
			exit(-1);
		}

		hp->a = tmp;
		hp->capacity = newcapacity;
	}
	hp->a[hp->size] = x;
	hp->size++;
	// 向上调整
	AdjustUp(hp->a, hp->size - 1);
}

原理可参考 3.3

先循环判断子节点和父节点的大小,然后做向上调整操作,

4)堆的删除

//交换
void swap(HPDataType* p1, HPDataType* p2)
{
	HPDataType temp = *p1;
	*p1 = *p2;
	*p2 = temp;
}
//像下调整
void AdjustDown(HPDataType* a, int n, int parent)
{
	//默认认为左孩子大
	int child = parent * 2 + 1;
	//超过数组大小
	while (child < n)
	{
		//确认child指向大的孩子
		if (child < n && a[child + 1] > a[child])
		{
			++child;
		}
		//孩子大于父亲,交换,继续调整
		if (a[child] > a[parent])
		{
			swap(&a[parent], &a[child]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}
// 堆的删除
void HeapPop(HP* hp)
{
	assert(hp);
	assert(hp->size > 0);

	//交换
	swap(&hp->a[0], &hp->a[hp->size - 1]);

	hp->size--;

	AdjustDown(hp->a, hp->size, 0);
}

5)取堆顶的数据

HPDataType HeapTop(HP* hp)
{
	assert(hp);
	assert(hp->size > 0);
	return hp->a[0];
}

6)堆的数据个数

int HeapSize(HP* hp)
{
	assert(hp);
	return hp->size;
}

7)堆的判空

bool HeapEmpty(HP* hp)
{
	assert(hp);
	return hp->size == 0;
}

8)堆的构建(给你一个数组,用给定的数组建堆)(不用push建堆)

// 堆的构建(给你一个数组,用给定的数组建堆)(不用push建堆)
void HeapCreate(Heap* hp, HPDataType* a, int n)
{
	assert(hp);
	hp->a = (HPDataType*)malloc(sizeof(HPDataType) * n);
	if (hp->a == NULL)
	{
		perror("malloc fail");
		exit(-1);
	}
	//把传过来的数组a,拷贝给hp->a,
	memcpy(hp->a, a, sizeof(HPDataType) * n);
	hp->size = hp->capacity = n;

	//建堆算法(向下调整)
	for (int i = (n - 1 - 1) / 2;i >= 0;--i)
	{
		AdjustDown(hp->a, n, i);
	}
}

4.3堆的源代码和测试(vs2022下编译)

#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<string.h>
#include<stdbool.h>






//堆的声明

typedef int HPDataType;
typedef struct Heap
{
	HPDataType* a;
	int size;
	int capacity;
}HP;

//打印
void HeapPrint(HP* hp);

// 堆的初始化
void HeapInit(HP* hp);

// 堆的销毁
void HeapDestory(HP* hp);

// 堆的插入
void HeapPush(HP* hp, HPDataType x);

// 堆的删除(删除堆顶元素)
void HeapPop(HP* hp);

// 取堆顶的数据
HPDataType HeapTop(HP* hp);

// 堆的数据个数
int HeapSize(HP* hp);

// 堆的判空
bool HeapEmpty(HP* hp);

// 堆的构建(给你一个数组,用给定的数组建堆)(不用push建堆),n是数组大小
void HeapCreate(Heap * hp, HPDataType * a, int n);






//堆的定义
#include"heap.h"

//交换
void swap(HPDataType* p1, HPDataType* p2)
{
	HPDataType temp = *p1;
	*p1 = *p2;
	*p2 = temp;
}

// 打印
void HeapPrint(HP* hp)
{
	assert(hp);
	for (int i = 0;i < hp->size;i++)
	{
		printf("%d ", hp->a[i]);
	}
	printf("\n");
}

// 堆的初始化
void HeapInit(HP* hp)
{
	assert(hp);
	hp->a = NULL;
	hp->size = hp->capacity = 0;
}

// 堆的销毁
void HeapDestory(HP* hp)
{
	assert(hp);
	free(hp->a);
	hp->a = NULL;
	hp->size = hp->capacity = 0;
}

// 向上调整
void AdjustUp(HPDataType* a, int child)
{
	int parent = (child - 1) / 2;

	while (child > 0)
	{
		//这里换为 < 是小堆的创建
		if (a[child] > a[parent])
		{
			//交换
			swap(&a[child],&a[parent]);

			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}
}
// 堆的插入
void HeapPush(HP* hp, HPDataType x)
{
	assert(hp);
	
	//扩容
	if (hp->size == hp->capacity)
	{
		int newcapacity = hp->capacity == 0 ? 4 : hp->capacity * 2;
		HPDataType* tmp = (HPDataType*)realloc(hp->a, sizeof(HPDataType) * newcapacity);
		if (tmp == NULL)
		{
			perror("realloc fail");
			exit(-1);
		}

		hp->a = tmp;
		hp->capacity = newcapacity;
	}
	hp->a[hp->size] = x;
	hp->size++;
	// 向上调整
	AdjustUp(hp->a, hp->size - 1);
}

//向下调整
void AdjustDown(HPDataType* a, int n, int parent)
{
	//默认认为左孩子大
	int child = parent * 2 + 1;
	//超过数组大小
	while (child < n)
	{
		//确认child指向大的孩子
		if (child < n && a[child + 1] > a[child])    //第一处:a[child + 1] > a[child]
		{
			++child;
		}

		//孩子大于父亲,交换,继续调整
		if (a[child] > a[parent])    //第二处:a[child] > a[parent]
		{
			swap(&a[parent], &a[child]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}

    //将第一处和第二处的>换位<则是建小堆
}

// 堆的删除
void HeapPop(HP* hp)
{
	assert(hp);
	assert(hp->size > 0);

	//交换
	swap(&hp->a[0], &hp->a[hp->size - 1]);

	hp->size--;

	AdjustDown(hp->a, hp->size, 0);
}

// 取堆顶的数据
HPDataType HeapTop(HP* hp)
{
	assert(hp);
	assert(hp->size > 0);
	return hp->a[0];
}

// 堆的数据个数
int HeapSize(HP* hp)
{
	assert(hp);
	return hp->size;
}

// 堆的判空
bool HeapEmpty(HP* hp)
{
	assert(hp);
	return hp->size == 0;
}

// 堆的构建(给你一个数组,用给定的数组建堆)(不用push建堆)
void HeapCreate(Heap* hp, HPDataType* a, int n)
{
	assert(hp);
	hp->a = (HPDataType*)malloc(sizeof(HPDataType) * n);
	if (hp->a == NULL)
	{
		perror("malloc fail");
		exit(-1);
	}
	//把传过来的数组a,拷贝给hp->a,
	memcpy(hp->a, a, sizeof(HPDataType) * n);
	hp->size = hp->capacity = n;

	//建堆算法(向下调整)
	for (int i = (n - 1 - 1) / 2;i >= 0;--i)
	{
		//这里的AdjustDown是建立的大堆,若要减小堆,调整该函数里面的符号(函数里面有提示)
		AdjustDown(hp->a, n, i);
	}
}






//测试

void text1()
{
	HP hp;
	HeapInit(&hp);
	HPDataType array[] = { 27,15,19,18,28,34,65,49,25,37 };
	//用Push的方法建堆,是向上建堆,时间复杂度是O(n*logn);
	for (int i = 0;i < sizeof(array) / sizeof(HPDataType);i++)
	{
		HeapPush(&hp, array[i]);
	}
	HeapPrint(&hp);

	HeapPop(&hp);
	HeapPrint(&hp);

	//取堆里面最大的五个数
	int k = 5;
	while (k--)
	{
		printf("%d ", HeapTop(&hp));
		HeapPop(&hp);
	}

	HeapDestory(&hp);
}

void text2()
{
	HP hp;
	HPDataType array[] = { 27,15,19,18,28,34,65,49,25,37 };
	HeapCreate(&hp, array, sizeof(array) / sizeof(int));

	HeapPrint(&hp);

}

int main()
{
	text2();
	return 0;
}

5.堆排序

  • 排升序,建大堆
  • 排降序,建小堆

算法思想:堆排序和堆删除的算法思想一样,都用到了堆的向下调整算法

算法核心:用数组的最后一个元素(size位置)与数组首元素进行交换,并将数组的size--
                  使得下次交换时,最后一个位置的元素不受影响

具体步骤如下:以数组array={17,20,8,16,5,3}为例:(字有点丑,大家看懂就行)

 

代码如下: 

//交换
void swap(HPDataType* p1, HPDataType* p2)
{
	HPDataType temp = *p1;
	*p1 = *p2;
	*p2 = temp;
}
//像下调整(数组、数组大小、父节点下标)
void AdjustDown(HPDataType* a, int n, int parent)
{
	//默认认为左孩子大
	int child = parent * 2 + 1;
	//超过数组大小
	while (child < n)
	{
		//确认child指向大的孩子
		if (child < n && a[child + 1] > a[child])    //第一处:a[child + 1] > a[child]
		{
			++child;
		}

		//孩子大于父亲,交换,继续调整
		if (a[child] > a[parent])    //第二处:a[child] > a[parent]
		{
			swap(&a[parent], &a[child]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}

    //将第一处和第二处的>换位<则是建小堆
}
//堆排序
void HeapSort(int* a, int size)
{
	// 向下调整建堆 -- O(N)
	// 升序:建大堆
	for (int i = (size - 1 - 1) / 2; i >= 0; --i)
	{
		AdjustDown(a, size, i);
	}

	int end = size - 1;//(end表示数组需要与array[0]交换的下标位置)
	while (end > 0)
	{
		swap(&a[0], &a[end]);//交换array[0]和array[end]的位置
		AdjustDown(a, end, 0);//向下调整
		end--;
	}
}


int main()
{
	int array[] = { 27, 15, 19, 18, 28, 34, 65, 49, 25, 37 };
	HeapSort(array, sizeof(array) / sizeof(int));

    //打印
    for (int i = 0; i < sizeof(array) / sizeof(int); ++i)
	{
		printf("%d ", array[i]);
	}
	printf("\n");
	return 0;
}

6.TOP-K问题

TOP-K问题:即求数据结合中前K个最大的元素或者最小的元素,一般情况下数据量都比较大

 比如:专业前10名、世界500强、富豪榜、游戏中前100玩家等

对于Top-K问题,能想到的最简单直接的方式就是排序,但是:如果数据量非常大,排序就不太可取了(可能数据都不能一下子全部加载到内存中)。最佳的方式就是用堆来解决,基本思路如下:

  1. 用数据集合中前K个元素来建堆
    • 前k个最大的元素,则建小堆
    • 前k个最小的元素,则建大堆
  2. 用剩余的N-K个元素依次与堆顶元素来比较,不满足则替换堆顶元素

比如:N个数找最大的前K个:

  • 方法一(传统方法):建立一个N个数的大堆,Pop   K次,依次取堆顶
    • 时间复杂度:N+logN*K
    • 空间复杂度:O(1)
  • 方法二:建立K个数的小堆,依次遍历数据,比堆顶数据大,就替换堆顶,再向下调整,
    最后最大的前K个数,就在小堆里面。
    • 时间复杂度:N+(N-K)*logK  ->  O(N*logK)
    • 空间复杂度:O(K)

这里给定n个数,求n个数里面最大的k个数(方法二的代码实现)

#include<stdio.h>
#include<stdlib.h>
#include<time.h>

void swap(int* p1, int* p2)
{
	int temp = *p1;
	*p1 = *p2;
	*p2 = temp;
}
//像下调整(数组、数组大小、父节点下标)
void AdjustDown(int* a, int n, int parent)
{
	//默认认为左孩子大
	int child = parent * 2 + 1;
	//超过数组大小
	while (child < n)
	{
		//确认child指向大的孩子
		if (child + 1 < n && a[child + 1] < a[child])
		{
			++child;
		}
		//孩子大于父亲,交换,继续调整

		if (a[child] < a[parent])
		{
			swap(&a[parent], &a[child]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}

void TestHeap5()
{
	// 造数据
	int n, k;	//n为造数据的多少,k为选前几个最大的
	printf("请输入n和k:>");
	scanf("%d%d", &n, &k);

	//打开文件
	FILE* fin = fopen("data.txt", "w");
	if (fin == NULL)
	{
		perror("fopen fail");
		return;
	}

	//写文件
	srand(time(0));		//为了让rand()实现真随机,用时间戳给他一个种子
	//int randK = k;
	//写n个数据到文件data.txt中
	for (size_t i = 0; i < n; ++i)
	{
		int val = rand() % 100;
		fprintf(fin, "%d\n", val);
	}

	fclose(fin);

	//-------------------------------------------------------------------------//

	// 找topk

	//打开文件
	FILE* fout = fopen("data.txt", "r");
	if (fout == NULL)
	{
		perror("fopen fail");
		return;
	}

	//对数组扩容
	int* minHeap = (int*)malloc(sizeof(int) * k);
	if (minHeap == NULL)
	{
		perror("malloc fail");
		return;
	}

	//先读文件里面前k个数据并放入数组中
	for (int i = 0; i < k; ++i)
	{
		fscanf(fout, "%d", &minHeap[i]);
	}
	// 堆前k个数据建小堆
	for (int i = k - 1 - 1; i >= 0; --i)
	{
		AdjustDown(minHeap, k, i);
	}

	//文件的数据读取出来,保存到val值中,并与小堆堆顶的数据作比较
	int val = 0;
	while (fscanf(fout, "%d", &val) != EOF)
	{
		//大于堆顶数据,插入进去,并继续排小堆
		if (val > minHeap[0])
		{
			minHeap[0] = val;
			AdjustDown(minHeap, k, 0);
		}
	}

	//打印堆里面的数据
	for (int i = 0; i < k; ++i)
	{
		printf("%d ", minHeap[i]);
	}
	printf("\n");

	fclose(fout);
}

int main()
{
	TestHeap5();

	return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/425242.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【0基础敲代码】如何使用使用SeaFile搭建私有云盘并公网访问

目录 1. 前言 2. SeaFile云盘设置 2.1 Owncould的安装环境设置 2.2 SeaFile下载安装 2.3 SeaFile的配置 3. cpolar内网穿透 3.1 Cpolar下载安装 3.2 Cpolar的注册 3.3 Cpolar云端设置 3.4 Cpolar本地设置 4. 公网访问测试 5. 结语 1. 前言 现在我们身边的只能设备…

VsCode SSH远程连接服务器【内网穿透公网连接】

文章目录1.前言2.VS code的安装和设置2.1 VS code的下载安装2.2 OpenSSH的启用2.3 为VS code配置ssh2.4 局域网内测试VS code的ssh连接2.5 Cpolar下载安装3.Cpolar端口设置3.1 Cpolar云端设置3.2 Cpolar本地设置4.公网访问测试5.结语1.前言 记得笔者小时候看电视&#xff0c;看…

Thinkphp常见漏洞利用

一、基础知识 1.ThinkPHP简介&#xff1a; ThinkPHP是一个开源&#xff0c;快速、简单的轻量级国产PHP开发框架&#xff0c;诞生于2006年初&#xff0c;原名FCS&#xff0c;2007年元旦正式更名为ThinkPHP。使用面向对象的开发结构和MVC模式&#xff0c;融合了Struts的思想和T…

【创建者模式】建造者模式

文章目录1、简介2、结构3、实现3.1、需求场景3.2、产品类3.3、抽象建造者类3.4、具体建造者类3.5、指挥者类3.6、测试类3.7、演示结果4、应用场景5、实操举例6、优缺点分析7、抽象工厂模式区别1、简介 建造者模式(Builder Pattern)旨在将一个复杂对象的构建与表示分离&#xf…

SpringCloud:ElasticSearch之索引库操作

ElasticSearch索引库就类似数据库表&#xff0c;mapping映射就类似表的结构。 我们要向ElasticSearch中存储数据&#xff0c;必须先创建“库”和“表”。 1.mapping映射属性 mapping是对索引库中文档的约束&#xff0c;常见的mapping属性包括&#xff1a; type&#xff1a;…

Hyperledger Fabric 2.2版本环境搭建

前言 部署环境: CentOS7.9 提前安装好以下工具 git客户端golangdockerdocker-composecurl工具 以下是个人使用的版本 git: 2.39.2golang: 1.18.6docker: 23.0.3dockkekr-compose: v2.17.2curl: 7.29.0 官方文档参考链接&#xff1a;跳转链接&#xff0c;不同的版本对应的官…

008:Mapbox GL添加比例尺scale功能

第008个 点击查看专栏目录 本示例的目的是介绍演示如何在vue+mapbox中添加比例尺scale功能 。 直接复制下面的 vue+mapbox源代码,操作2分钟即可运行实现效果 文章目录 示例效果配置方式示例源代码(共66行)相关API参考:专栏目标示例效果 配置方式 1)查看基础设置:https…

Linux0.11 管道(十一)

系列文章目录 Linux 0.11启动过程分析&#xff08;一&#xff09; Linux 0.11 fork 函数&#xff08;二&#xff09; Linux0.11 缺页处理&#xff08;三&#xff09; Linux0.11 根文件系统挂载&#xff08;四&#xff09; Linux0.11 文件打开open函数&#xff08;五&#xff09…

fMRI研究 | 社交情境下的混合情绪

导读 背景&#xff1a;神经科学通常都是单独研究各种情绪&#xff0c;而混合的情绪状态&#xff08;例如愉悦和厌恶、悲伤和快乐的共存&#xff09;在日常生活中很常见。心理生理学和行为学证据表明&#xff0c;混合情绪可能具有不同于其组成情绪的反应特征。然而&#xff0c;…

什么是JWT?

起源 需要了解一门技术&#xff0c;首先从为什么产生开始说起是最好的。JWT 主要用于用户登录鉴权&#xff0c;所以我们从最传统的 session 认证开始说起。 session认证 众所周知&#xff0c;http 协议本身是无状态的协议&#xff0c;那就意味着当有用户向系统使用账户名称和…

RocketMQ源码分析之监控指标分析

这里是weihubeats,觉得文章不错可以关注公众号小奏技术&#xff0c;文章首发。拒绝营销号&#xff0c;拒绝标题党 Rocketmq版本 version: 5.1.0 背景 继续上次的高可用topic二开已经有了一段时间&#xff0c;现在我们需要对我们的限流数据进行监控&#xff0c;所以现在我们来…

Qt中英文切换(涉及多种场景)

qt中英文切换涉及到一个软件两个文件&#xff0c;分别是QtLinguist、.ts文件和.qm文件。 1、在Pro中添加 TRANSLATIONS en.ts \ch.ts添加这个文件后qmake&#xff0c;然后如下操作点击更新&#xff1a; 这个时候会生成2两个文件en.ts和ch.ts。 2、将这两个文件添加到项目中…

C++ : 构造函数 析构函数

&#x1f535;前提引入 &#xff1a; 1如果一个类中什么成员都没有&#xff0c;称为空类&#xff0c;但空类并非什么都没有&#xff0c;在我们没有写任何东西时&#xff0c;编译器会自动生成6个默认成员函数。 2.默认成员函数 &#xff1a; 用户没有显式实现&#xff0c;编译器…

Redis快速上手

Redis快速上手 OVERVIEWRedis快速上手1.redis数据类型2.redis常用命令StringListSetSortedSetHashKey相关3.redis配置文件4.redis数据持久化5.hiredis使用连接数据库执行redis命令函数释放资源程序实例1.redis数据类型 key: 必须是字符串 - “hello” value: 可选的 String类型…

核心业务5:充值业务实现

核心业务5:我要充值 1.充值业务流程图 2.充值业务流程逻辑 3.数据库表 4.前端逻辑代码 5.汇付宝代码逻辑 6.尚融宝代码逻辑 7.幂等性判断原理和解决方案 8.代码规范和原理了解 核心业务5:我要充值 1.充值业务流程图

基于springboot的在线考试系统源码数据库论文

目 录 目 录 第一章 概述 1.1研究背景 1.2 开发意义 1.3 研究现状 1.4 研究内容 1.5论文结构 第二章 开发技术介绍 2.1 系统开发平台 2.2 平台开发相关技术 2.2.1 Java技术 2.2.2 mysql数据库介绍 2.2.3 MySQL环境配置 2.2.4 B/S架构 2.2.5 Spr…

如何在Linux系统中使用 envsubst 命令替换环境变量?

在Linux系统中&#xff0c;环境变量是非常常见的一种机制&#xff0c;它们被用于存储重要的系统信息&#xff0c;比如用户的登录名、路径等等。当在脚本中需要使用这些变量时&#xff0c;可以使用envsubst命令&#xff0c;该命令可以将环境变量的值替换到文本文件中。 本文将介…

低静态电流-汽车电池反向保护系统的方法

低静态电流-汽车电池反向保护系统的方法 背景 车辆中电子电路数量不断增加&#xff0c;使得需要消耗的电池电量也随之大幅增长。为了支持遥控免钥进入和安全等功能&#xff0c;即使在汽车停车或熄火时&#xff0c;电池也要持续供电。 由于所有车辆都使用有限的电池供电&…

三轴XYZ平台生成gcode文件

1. 生成gcode坐标文件 gcode文件中保存的是需要绘制图形的路径信息&#xff0c;这里我们采用开源矢量图形编辑软件 Inkscape并通过Unicorn G-Code插件来生成 gcode坐标文件。 将软件资料包\Inkscape.rar 压缩文件解压到电脑上任意磁盘&#xff0c;软件内已安装 Unicorn G-Code插…

【花雕学AI】深度挖掘ChatGPT角色扮演的一个案例—CHARACTER play : 莎士比亚

CHARACTER play : 莎士比亚 : 52岁&#xff0c;男性&#xff0c;剧作家&#xff0c;诗人&#xff0c;喜欢文学&#xff0c;戏剧&#xff0c;爱情 : 1、问他为什么写《罗密欧与朱丽叶》 AI: 你好&#xff0c;我是莎士比亚&#xff0c;一位英国的剧作家和诗人。我很高兴你对我的…