<数据结构>NO6.堆的实现|堆的应用

news2024/11/29 20:52:07

 🐇本文用到的所有代码都放在我的gitee仓库了🐇
syseptember的gitee仓库https://gitee.com/syseptember/data-structure/tree/4f0b1f9f56e3b0bee72fa0563c23a6917b3252e8/Heap/Heap

目录

堆的概念

堆的实现

堆的应用

堆排序

时间复杂度分析

 TopK问题

时间复杂度


堆的概念

堆(heap)是计算机科学中一类特殊的数据结构的统称。堆通常是一个可以被看做一棵树的数组对象

📚定义:

 📚性质:
①堆中某个结点的值总是不大于或不小于其父结点的值;
②堆总是可以看成一颗完全二叉树;

❗注意:堆的物理结构是数组,逻辑结构是完全二叉树,所以我们可以根据定义顺序表同样定义堆。

 💭练习题1:下面哪个序列可以看成堆?
100,60,70,50,32,65
60,70,65,50,32,100
65,100,70,32,50,60
70,65,100,32,50,60

🔑解析:答案为A

 

 

 


堆的实现

堆实现的功能主要有

  • 插入(HeapPush)
  • 删除(HeapPop)
  • 取堆顶元素(HeapTop)
  • 取堆数据个数(HeapSize)
  • 判断堆是否为空(HeapEmpty)

❗注意:堆的插入是在数组末尾插入元素,堆的删除是删除数组首元素(完全二叉树的根)

①堆的结构

typedef int HPDataType;
//由于堆属于完全二叉树,因此可以使用数组存放堆的节点
//堆的结构和顺序表一样
typedef struct Heap
{
	HPDataType* a;
	int size;
	int capacity;
}Heap;

 ②堆的初始化

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

③堆的销毁

//堆的销毁
void HeapDestroy(Heap* php)
{
	assert(php);
	free(php->a);
	php->a = NULL;
	php->capacity = php->size = 0;
}

④堆的插入
插入时需要判断堆是否需要扩容,其次再在数组末尾插入元素,插入元素后有可能不满足堆的性质所以需要经过调整算法将插入的数据移动到合适的位置保持堆的结构。如

 我们将插入的数据向上移动,最后使得该结构变为一个堆。最多会将插入的数据调整h-1次,也就是log(n+1)-1,所以时间复杂度为O(logN)
📚定义:称这种算法为向上调整算法(AdjustUp)。
现在我们来研究向上调整算法

向上调整算法
❓ 请思考:在堆结构中,父节点和子节点之间有什么关系?
🔑解析:堆的物理结构是数组,所以我们可以找出它们下标之间的关系
很容易发现如下关系:
🔍已知子节点下标child,父节点下标parent==(child-1) /2;
🔍已知父节点下标parent,左孩子下标childL==parent*2+1;右孩子下标childR==parent*2+2;
当堆中插入节点时,我们需要将该节点与父节点进行比较,假设我们想要的是小堆,那么如果该节点比父节点小,我们需要交换它们的位置,重复多次直到child下标等于0。
❗注意:向上调整算法前提除是最后一个节点的所有节点构成堆。

💬AdjustUp代码:

//向上调整
void AdjustUp(int* a, int child)
{
	int parent = (child - 1) / 2;
	while (child > 0)
	{
		//小堆:孩子比父亲大,如果孩子小于父亲,需交换
		if (a[child] < a[parent])
		{
			Swap(&a[child], &a[parent]);
			child = parent;
			parent = (parent - 1) / 2;
		}
		
		大堆:孩子比父亲小,如果孩子大于父亲,需交换
		//if (a[child] > a[parent])
		//{
		//	Swap(&a[child], &a[parent]);
		//	child = parent;
		//	parent = (parent - 1) / 2;
		//}
		else
		{
			break;
		}
	}
}

💬HeapPush代码 :

//堆的插入--时间复杂度为O(logN)
void HeapPush(Heap* php, HPDataType x)
{
	assert(php);
	//判断是否需要扩容
	if (php->capacity == php->size)
	{
		php->capacity = php->capacity == 0 ? 4 : 2 * php->capacity;
		HPDataType* tmp = realloc(php->a, sizeof(HPDataType) * php->capacity);
		if (NULL != tmp)
			php->a = tmp;
	}
	//插入元素
	php->a[php->size] = x;
	php->size++;
	//向上调整
	AdjustUp(php->a, php->size - 1);
}

 ⑤堆的删除
❗注意:堆为空不可以删除。
❓ 请思考:如何删除堆顶元素?可不可以直接用数组后面元素覆盖第一个元素?
❎可以但是不建议:如果直接用后面覆盖第一个元素首先回导致时间复杂度为O(n)。并且删除前的兄弟节点是不具备大小关系的,而直接覆盖第一个元素可能会导致原来的某一对兄弟节点变成父子节点,这是就需要具备大小关系了,所以直接覆盖第一个元素后需要重新建立堆的结构,这样实现起来效率太低了。比如

 ✅正确思路:先将堆顶元素和最后一个元素交换,在删除最后一个元素,在将堆顶元素移动到合适位置使整个结构变成堆。堆顶节点最多需要调整h-1次,也就是log(n+1)-1,时间复杂度为O(logN)。

📚定义:这个调整算法我们称为向下调整算法(AdjustDown)

向下调整算法 
根据父节点下标与子节点下标关系,我们可以类比向上调整算法实现向下调整算法。
假设我们实现的是小堆,如果父亲节点比孩子节点大,则选出较小的孩子节点,让较小的孩子节点和父亲节点交换,直到孩子节点下标等于数组有效元素个数。
❗注意:向下调整算法的前提是左右子树都为堆。

💬AdjustDown代码:

//向下调整
void AdjustDown(int* a, int size, int parent)
{
	int child = parent * 2 + 1;
	//保证左孩子下标在有效范围内
	while (child < size)
	{
		//小堆逻辑
		//让child是左右孩子中较小的那一个
		if (child + 1 < size && a[child] > a[child + 1])//左孩子存在右孩子不一定存在
		{
			child++;
		}
		//小堆孩子大于父亲,如果孩子比父亲小需要交换
		if (a[child] < a[parent])
		{
			Swap(&a[parent], &a[child]);
		}
		
		大堆逻辑
		child是左右孩子中较小的那一个
		//if (child + 1 < size && a[child] < a[child + 1])//左孩子存在右孩子不一定存在
		//{
		//	child++;
		//}
		大堆孩子小于父亲,如果孩子比父亲大需要交换
		//if (a[child] > a[parent])
		//{
		//	Swap(&a[parent], &a[child]);
		//}

		else
		{
			break;
		}
		parent = child;
		child = parent * 2 + 1;
	}
}

💬HeapPop代码:

//堆的删除(删除堆顶)--时间复杂度为O(logN)
void HeapPop(Heap* php)
{
	assert(php->size > 0);
	//交换顶和最后一个节点
	Swap(&php->a[0], &php->a[php->size - 1]);
	php->size--;
		//向下调整
	AdjustDown(php->a, php->size, 0);
}

⑥取出堆顶元素
💬HeapTop代码:

//取出堆顶元素
HPDataType HeapTop(Heap* php)
{
	assert(php); 
	assert(!HeapEmpty(php));
	return php->a[0];
}

⑦获取堆数据个数
💬HeapSize代码:

//堆的数据个数
int HeapSize(Heap* php)
{
	assert(php);
	assert(!HeapEmpty(php));
	return php->size;
}

⑧判断堆是否为空
💬HeapEmpty代码:

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

堆的应用

📚堆常见的应用有堆排序、TopK问题、优先级队列。接下来介绍堆排序和TopK问题。

堆排序

📚堆排序是一种基于堆数据结构的排序算法。它利用了堆的性质来进行排序操作。堆是一种完全二叉树,分为大堆和小堆两种类型。在大堆中,父节点的值大于或等于其子节点的值;而在小堆中,父节点的值小于或等于其子节点的值。
📚堆排序的基本思想:1.首先将待排序的元素构建成一个最大堆(或最小堆)2.然后将堆顶元素(即最大值或最小值)与最后一个元素交换位置 3.再对剩余的元素进行堆调整,使其满足堆的性质。4. 重复这个过程,直到所有的元素都排好序。
📚堆排序的优点:堆排序的优点是具有较好的平均和最坏情况时间复杂度(O(nlogn)),并且不需要额外的辅助空间。
📚堆排序的缺点:堆排序的实现较为复杂,包括构建堆和调整堆的过程,且不稳定,即相等元素的相对顺序可能会改变。

根据前面建堆的代码,我们很容易想出一种堆排序方法
💬使用堆排序排降序代码

//该方法可以,但是由缺点
//弊端1.现有一个堆 2.空间消耗
void HeapSort(int* arr, int size)
{
	Heap hp;
	HeapInit(&hp);
	//建堆O(NlogN)
	for (int i = 0; i < size; i++)
	{
		HeapPush(&hp, arr[i]);
	}
	//O(N*logN)
	for (int i = 0; i < size; i++)
	{
		arr[i] = HeapTop(&hp);
		HeapPop(&hp);
	}
//向上调整
void AdjustUp(int* a, int child)
{
	int parent = (child - 1) / 2;
	while (child > 0)
	{
		//小堆:孩子比父亲大,如果孩子小于父亲,需交换
		if (a[child] < a[parent])
		{
			Swap(&a[child], &a[parent]);
			child = parent;
			parent = (parent - 1) / 2;
		}
		else
		{
			break;
		}
	}
}

🚩解释代码:首先建立一个小堆,将数组元素全部放入小堆,这让堆顶就是最小元素,每次取出最小元素放在数组未排序部分的最前面,再删除堆顶元素,取出次小元素放入数组未排序部分前面,最后成功将数组变为升序。

❎上述方法虽然可行,但是存在几点弊端。
1.必须先有堆才能实现堆排序。
2.使用了额外的空间消耗。

我们可以对上述思路进行改进,能不能想办法将传入的数组看为堆?这样就不需要额外建立堆了,也就不会使用额外的空间了。请看下面的思路👇 

❓ 请思考:堆排序的第一步就是构建堆,如果我想让数组升序,是应该建立大堆还是小堆?
🔑解析:应该建大堆 。🔍建立大堆:建立大堆时每次的堆顶就是最大元素,只需要将堆顶和堆中的最后一个元素交换位置,这样每次最大的元素就去了堆尾部也就是数组尾部,再对剩下未排序的元素进行向下调整(AdjustDown)维持大堆的结构,重复将堆顶元素放在当前堆末尾......,待排序有n个元素,只需重复n-1次上述操作即可完成排序。

 🔍建立小堆:如果建立小堆排升序的话每次堆顶元素是最小的,选出最小的元素之后需要选出次小的,而堆顶已经是有序的了,所以接下来的堆有可能需要重新排序才满足堆的性质。所以如果排升序建立小堆的话,每次选出最小的之后都需要重新建堆,比较麻烦,所以不推荐建小堆。

🔺总结:排升序 -- 建立大堆;排降序 -- 建立小堆

🔨step1.建堆
假设无序数组排降序,对数组进行的第一个操作就是建堆(小堆),❗注意:这里是将数组本身就看作堆。建立小堆有两种方式,一种是取出数组元素向上调整建堆,另一种是向下调整建堆。
🔍向上调整建堆:当堆中只有一个元素时,可以将此堆看成大堆或者小堆,我们这里需要排降序。所以将一个节点看作小堆。我们这里需要将数组本身看作堆,所以数组首元素可以看成一个小堆,依次取数组第二个元素、第三个元素.....第n个元素进行向上调整保持小堆的性质。最终结果就是数组中的n个元素就是以小堆方式排列的。
🔍向下调整建堆:因为向下调整的前提是左右子树都为堆,所以对于数组最开始的状态我们不能保证数组的没个左右子树都为堆,所以我不能从数组第二个元素开始一次向下建堆,而是要到倒着开始向下建堆,将数组看成完全二叉树,我们需要从二叉树的最后一个叶子节点的父亲节点开始依次向下建堆,直到遍历到从整个二叉树的根,对该根进行向下调整后整个数组就是小堆的结构。
❗我们可以根据堆中父亲与孩子小标间的关系找到最后一个叶子节点的父亲。
如图:

 🔨step2.交换堆顶和最后一个叶子节点
将数组变成大堆后,我们只需交换堆顶和最后一个叶子节点就可以让堆中最后一个元素变成最大值。

 💬HeapSort代码:

///复杂度O(NlogN)
void HeapSort(int* arr, int size)
{
	//升序 -- 建大堆
	//降序 -- 建小堆
	
	//向上调整建堆---o(nlogn)
	for (int i = 1; i < size; i++)
	{
		AdjustUp(arr, i);
	}
	
	向下调整建堆(从最后一个叶子节点的父亲开始调整)---O(n)
	//for (int i = (size-1-1)/2; i >= 0; i--)
	//{
	//	AdjustDown(arr, size, i);
	//}

	int end = size - 1;
	//O(NlogN)
	while (end > 0)
	{
		//最小的元素和最后一个元素交换
		Swap(&arr[end], &arr[0]);
		//向下调整 -- 找出次小的元素O(logn)
		AdjustDown(arr, end, 0);
		//调整时减去已经有序的元素
		end--;
	}

}

时间复杂度分析

🔍向上调整建堆的时间复杂度O(NlogN);
🔍向下调整建堆的时间复杂度O(N);
无论哪种建堆方法,HeapSort的时间复杂度都为O(NlogN)。
感兴趣的同学可以看下推导:

 TopK问题

📚Top-k问题是一种优质筛选问题:从一个数据集合中找出前 k 个最大(或最小)的元素。这个问题在数据处理和算法设计中非常常见,有许多不同的解决方法。

❎我们可以直接对数据进行排序,在选出数组前k个数组,但是这样有缺陷的,比如数据个数很大时内存中无法开辟容纳它们的空间。

//该方法可以,但是由缺点
//弊端1.现有一个堆 2.空间消耗
void HeapSort(int* arr, int size)
{
	Heap hp;
	HeapInit(&hp);
	//建堆O(NlogN)
	for (int i = 0; i < size; i++)
	{
		HeapPush(&hp, arr[i]);
	}
	//N*logN
	for (int i = 0; i < size; i++)
	{
		arr[i] = HeapTop(&hp);
		HeapPop(&hp);
	}

⚡优化:假设我们需要选出最大的k个数据

  1. 创建一个大小为 k 的最小堆。
  2. 将数据集合中前k个元素入堆。
  3. 遍历数据集合中剩下的n-k个元素,如果遍历的数据大于堆顶元素则用该数据替换堆顶元素。并且像下调整保持小堆。
  4. 遍历完所有元素后,堆中保留的就是前 k 个最大的元素。

建立小堆是因为可以让堆顶元素是当前堆中k个元素最小的,如果其余元素比堆顶(当前堆中k个元素最小的元素)要大,那么可以让该元素替换堆顶的元素。此时堆中的所有元素是遍历到目前最大的k个元素,直到遍历结束堆中的k个元素就是所有元素中最大的。
❓请思考:如果找最大的k个建立的是大堆会怎么样?
🔑解析:如果建立的是大堆,假设推中的k个元素有所有元素中最大的元素,由于是大堆,所以最大元素一定在堆顶,剩下的n-k个元素即使有次大的,和堆顶比较依然小于堆顶,也就是说堆顶被  ”挡住了“,所以无法找到前k个最大的元素。

🔺总结:前k个最大的 -- 建立大小为k的小堆;前k个最小的 -- 建立大小为k的大堆。

💬TopK代码

//优质筛选问题
void TestTopK(int* arr, int n, int k)
{
	int* kminHeap = (int*)malloc(sizeof(int) * k);
	if (kminHeap == NULL)
	{
		perror("kminHeap申请失败:");
		exit(EXIT_FAILURE);
	}

	思路1排序----当数据很大时无法实现,内存中没有那么多空间可以开辟
	//  时间复杂度O(nlogn)
	//HeapSort(arr, size);
	//for (int i = 0; i < k; i++)
	//{
	//	kminHeap[i] = arr[i];
	//}
	//for (int i = 0; i < k; i++)
	//{
	//	printf("%d ", kminHeap[i]);
	//}

	//思路2(重点)
	//找前k个最小的 -- 建大堆
	//找前k个最大的 -- 建小堆

	//1.先将k个数据放入堆中,在将n-k个数据依次和堆顶比较:

	//2.如果寻找前个k最小的,将比大堆堆顶小的元素放入堆中,进行向下调整
	//  如果寻找前个k最大的,将比小堆堆顶大的元素放入堆中,进行向下调整

	//前k个元素建立堆
	for (int i = 0; i < k; i++)
	{
		kminHeap[i] = arr[i];
	}

	//F(n)=(n - k) * (H - 1) = (n - k) * (log(k+1) - 1) -- O(nlogk)
	for (int i = k; i < n; i++)
	{
		if (arr[i] > kminHeap[0])
		{
			kminHeap[0] = arr[i];
			AdjustDown(kminHeap, k, 0);//AdjustDown建小堆
		}
	}
	for (int i = 0; i < k; i++)
	{
		printf("%d ", kminHeap[i]);
	}
	free(kminHeap);
}

时间复杂度

🔍思路1用到了堆排序,时间复杂度为O(NlogN);
🔍思路2建堆的时间可以忽略,剩下的n-k个元素每个都有可能移动H-1次,所以时间复杂度为O(NlogK)

🐇本文用到的所有代码都放在我的gitee仓库了🐇
syseptember的gitee仓库https://gitee.com/syseptember/data-structure/tree/4f0b1f9f56e3b0bee72fa0563c23a6917b3252e8/Heap/Heap

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

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

相关文章

做功能测试好几年,一直都像是给人“打杂”的!直到这天我开始……

“做功能测试这几年&#xff0c;一直都像是给人打杂的&#xff01;”这句话&#xff0c;想必很多测试人员都有过同感。曾经&#xff0c;我们每天要重复执行繁琐的测试流程&#xff0c;手动输入大量数据、进行各种操作、检查每一个细节&#xff0c;整个过程反复无常&#xff0c;…

用 AI 轻松管理数据收集和分布! #TallyForms

工作中需要面对各种各样让人眼花缭乱的表格&#x1f92f; 此时一款让数据收集和整理变得轻松的平台简直就是打工人们的福音&#xff0c;TallyForms 就拥有自定义表单、实时分析和高效管理的全方位解决方案&#xff01; TallyForms TallyForms 是一个免费的在线表单生成器&am…

[论文阅读] Explicit Visual Prompting for Low-Level Structure Segmentations

[论文地址] [代码] [CVPR 23] Abstract 我们考虑了检测图像中低层次结构的通用问题&#xff0c;其中包括分割被操纵的部分&#xff0c;识别失焦像素&#xff0c;分离阴影区域&#xff0c;以及检测隐藏的物体。每个问题通常都有一个特定领域的解决方案&#xff0c;我们表明&am…

高压放大器在大学教研领域的实际应用

在大学教研领域中&#xff0c;高压放大器可以用于多种实际应用。下面将介绍其中几个典型的应用场景。 1、激光切割 适用高校学院&#xff1a;机械学院 应用场景&#xff1a;机械制造、各类材料的切割 2、超声雾化 适用高校学院&#xff1a;医学院、机械学院、物理学院 应用场景…

C4D R26 渲染学习笔记(1):C4D版本选择和初始UI框介绍

C4D版本知识 C4D通过R来进行版本区分&#xff0c;现在2023年5月22日最新版的是R26。说一下特殊版本。 C4D版本介绍特点R19OC快乐版3.07最高版本&#xff0c;OC是C4D最具性价比的渲染器&#xff0c;OC学习成本低&#xff0c;渲染速度快&#xff0c;但是注意OC 3.07只支持10系N…

MySQL8.0数据库超详细安装教程全过程

1、官网下载MySQL8.0地址&#xff1a;MySQL :: Download MySQL Installer (Archived Versions) 2、 双击安装包进行安装 3、自定义安装 4、选择MySQL Server8.0 5、创建MYSQL数据存储目录及安装目录 6、配置安装路径及数据存储目录 7、确认继续 8、选择MySQL&#xff0c;下一步…

我4年测试,已失业3个月.....

我做测试4年&#xff0c;一线城市薪水拿到15K&#xff0c;中间还修了一个专升本&#xff0c;这个年限不说资深肯定也是配得上经验丰富的。今年行情不好人尽皆知&#xff0c;但我还是对我的薪水不是很满意&#xff0c;于是打算出去面试&#xff0c;希望可以搏一个高薪。 但真到面…

分布式项目 11 在项目中使用jsonp发送请求并且处理

在项目中使用jsonp技术 01.相关子系统的搭建 第一步&#xff1a;创建一个新的子系统&#xff0c;叫做jt-sso 选中jt父级项目&#xff0c;然后鼠标右键进行new&#xff0c;然后选中maven Model&#xff0c;进行项目的创建&#xff0c;具体操 作如下图所示&#xff1a; 第二步…

STM32单片机(三)第一节:GPIO输出

❤️ 专栏简介&#xff1a;本专栏记录了从零学习单片机的过程&#xff0c;其中包括51单片机和STM32单片机两部分&#xff1b;建议先学习51单片机&#xff0c;其是STM32等高级单片机的基础&#xff1b;这样再学习STM32时才能融会贯通。 ☀️ 专栏适用人群 &#xff1a;适用于想要…

5个超好用的在线抠图网站,告别繁琐的PS,快马住!

本期给大家推荐5个超好用的在线抠图网站&#xff0c;几秒钟就能搞定你的需求&#xff0c;告别繁琐的PS&#xff0c;重点是免费哦&#xff0c;赶紧收藏起来吧&#xff01; 1、菜鸟图库 https://www.sucai999.com/default/crx/removebg?vNTYwNDUx 菜鸟图库是一个专门为新手设计…

Go网络通信

Go中HTTP协议客户端实现 Go语言标准库内置了net/http包&#xff0c;涵盖了HTTP客户端和服务端具体的实现方式。内置的net/http包提供了最简洁的HTTP客户端实现方式&#xff0c;无须借助第三方网络通信库&#xff0c;就可以直接使用HTTP中用得最多的GET和POST方式请求数据。 fun…

一道很简单的面试题,怎么那么多人不会?

大厂面试题分享 面试题库 前后端面试题库 &#xff08;面试必备&#xff09; 推荐&#xff1a;★★★★★ 地址&#xff1a;前端面试题库 web前端面试题库 VS java后端面试题库大全 最近帮公司招聘&#xff0c;主要负责一面&#xff0c;所以基本上问的基础多一点。但是我在…

【云计算与虚拟化】第五章—— vCenter Server的高级功能

第五章—— vCenter Server的高级功能 1、在ESXi01中&#xff0c;新建一个资源池&#xff0c;命名为polo&#xff0c;将ESXi01上的虚拟机加入其中,CPU资源设置如下&#xff1a;份额正常&#xff0c;预留自定义&#xff0c;限制自定义&#xff0c;不可扩展预留&#xff0c;内存…

新版电脑微信怎么锁屏?不能升级到新版本又怎么锁?

#微信新增锁定功能# 许多小伙伴在工作或家里用电脑时都不想让别人私自看到自己微信的聊天记录&#xff0c;手机端还好&#xff0c;毕竟会自动锁屏&#xff0c;而且开屏也简单&#xff0c;只需指纹或人脸、手势就行&#xff0c;但电脑端就比较麻烦了&#xff0c;只要人一离开&a…

Intewell智能工业操作系统,助力制造业智能化发展

软件定义控制 ——工业操作系统是工业互联网的核心&#xff0c;也是工业产业生态的核心&#xff0c;更是工业时代安全的基石。基于软件定义控制的智能工业操作系统&#xff0c;是我国工业发展、工业安全、工业未来的核心。 日前&#xff0c;以“筑牢粤港澳软件产业新生态、打…

组合总和-回溯

1题目 找出所有相加之和为 n 的 k 个数的组合&#xff0c;且满足下列条件&#xff1a; 只使用数字1到9每个数字 最多使用一次 返回 所有可能的有效组合的列表 。该列表不能包含相同的组合两次&#xff0c;组合可以以任何顺序返回。 示例 1: 输入: k 3, n 7 输出: [[1,2,…

全场景,新技术,维视智造携重磅产品实力收官SNEC2023

5月26日&#xff0c; SNEC第十六届(2023)国际太阳能光伏与智慧能源(上海)大会暨展览会 圆满落幕 。 展会期间维视 智造旗下视觉检测系统、光伏组件生产核心场景检测方案等高口碑产品悉数 亮相&#xff0c;吸引了众多观众驻足观摩。 喜看稻菽千重浪&#xff01;接下来让我们一起…

《微服务实战》 第二十二章 Redis发布订阅、事务

前言 本章节讲解Redis发布订阅、事务。 1、发布订阅 Redis PubSub 模块又称发布订阅者模式&#xff0c;是一种消息传递系统&#xff0c;实现了消息多播功能。发布者&#xff08;即发送方&#xff09;发送消息&#xff0c;订阅者&#xff08;即接收方&#xff09;接收消息&am…

同源部署自定义sessionId解析器实现一个浏览器同时登录正常使用

前言 废话不多说&#xff0c;昨天是实现同源部署返回不同cookie&#xff0c;现在是核心&#xff0c;就是一个浏览器同时登录&#xff0c;客户端、运营端同时正常使用。 一、核心实现 核心实现实际上就是自定义HttpSessionIdResolver处理sessionid解析&#xff0c;上一篇博文已实…

VMWare ESXI6.7创建虚拟机

VMware ESXi&#xff1a;专门构建的裸机 管理程序 首先开启ESXI主机 登录ESXI 打开浏览器输入物理机ip&#xff0c;输入账号密码进行登录 创建虚拟机 选择创建类型 创建RedHat7.6 选择存储类型和数据存储 仅一个存储&#xff0c;直接点下一页即可 配置虚拟机硬件和虚拟机附…