【数据结构】堆

【数据结构-堆】堆 堆(Heap)是一种重要的数据结构,通常用于实现优先队列和一些其他算法。堆通常是一个完全二叉树,这意味着树中的每个节点都有最多两个子节点,除了最后一层,其他层都是满的。这种特性使得堆可以有效地使用数组来表示,因为数组的索引操作非常高效。堆分为两种主要类型,最小堆和最大堆,它们都具有堆序性质。在最小堆中,每个节点的值都小于或等于其子节点的值,根节点的值最小。在最大堆中,每个节点的值都大于或等于其子节点的值,根节点的值最大。 阅读详情

一、堆的概念及结构

有一个集合 K = {k0 , k1 , k2 , … , kn-1} ,把它的所有元素按完全二叉树的顺序存储方式存储在一 个一维数组中 ,并满足: Ki <= K2i+1 且 Ki<= K2i+2 (Ki >= K2i+1 且 Ki >=K2i+2) i = 0 , 1 , 2… ,(即双亲比孩子的数值小(大))则称为小堆 ( 或大堆) 。将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。

堆的性质:堆中某个节点的值总是不大于或不小于其父节点的值;堆的逻辑结构是一棵完全二叉树。

在这里插入图片描述

二、堆的实现

1. 结构的定义

堆的元素按完全二叉树的顺序存储方式存储在一维数组中,所以堆的结构和顺序表的结构一样。

typedef int HeapDataType;

typedef struct heap
{
	HeapDataType* data;
	int size;
	int capacity;
}heap;

2. 堆的初始化

和顺序表一样,先在Init函数外创建一个堆变量,再把堆的地址传进初始化函数,创建一个空堆。

void InitHeap(heap* ph)
{
	assert(ph);
	ph->data = NULL;
	ph->capacity = 0;
	ph->size = 0;
}

3. 堆的插入

堆只能在尾部进行插入,由于堆要求在插入元素后仍保持堆的性质,所以我们需要对堆进行向上调整,把新插入的元素调整到正确的位置,向上调整的过程其实也是建堆的过程。

//小根堆
void HeapPush(heap* ph, HeapDataType x)
{
	assert(ph);
	if (ph->capacity == ph->size) //扩容
	{
		int newcapacity = ph->data == NULL ? 4 : ph->capacity * 2;
		HeapDataType* temp = (HeapDataType*)realloc(ph->data, sizeof(HeapDataType) * newcapacity);
		if (temp == NULL)
		{
			perror("realloc");
			exit(-1);
		}
		ph->data = temp;
		ph->capacity = newcapacity;
	}
	ph->data[ph->size] = x;
    //向上调整,保持堆结构
	AdjustUp(ph->data, ph->size);
	ph->size++;
}

4. 向上调整

这里以小根堆为例,如图:假设现在我们已经有了一个小根堆,现在我们往堆尾插入一个元素,那么可能会出现两种情况:

在这里插入图片描述

1、插入的元素大于父节点,此时我们的堆仍保持小根堆结构,所以不需要改动;比如我们往堆中插入30;

在这里插入图片描述

2、插入的元素小于父节点;这种情况下我们就需要把该节点不断往上调整,直到把堆调整为小根堆,最坏的情况是该节点被调整为根节点,比如我们插入10;

在这里插入图片描述

void Swap(HeapDataType* a, HeapDataType* b)
{
	assert(a && b);
	HeapDataType temp = *a;
	*a = *b;
	*b = temp;
}

//向上调整 - 小堆
void AdjustUp(HeapDataType* nums, int child)
{
	assert(nums);
	while (child > 0)//最坏调整到根节点停止
	{
		int parents = (child - 1) / 2;//父节点下标
		if (nums[child] < nums[parents]) //交换条件
		{
			Swap(&nums[child], &nums[parents]);
			child = parents;          //子节点取代父节点下标
		}
		else //否则直接退出,此时已经满足堆结构
		{
			break; 
		}
	}
}

如果我们需要调整大根堆,只需要把交换的条件修改一下即可。

if(nums[child] > nums[parents])

5. 堆的删除

对于堆的删除有明确的规定:我们只能删除堆顶的元素;但是头删之后存在两个问题:

1、顺序表头删需要挪动数据,效率低下;

2、挪动数据之后堆中各节点的父子关系完全破坏,很明显不满足堆的结构了;

对于上面的这些问题,我们有如下解决办法:

1、我们在删除之前先将堆顶和堆尾的元素交换,然后让size减一,这样相当于删除了堆顶的元素,且效率达到了O(1);

2、由于我们把堆尾元素交换到了堆顶,堆的结构遭到了局部破坏,所以需要设计一个向下调整算法来保持堆的结构;

//小根堆
void HeapPop(heap* ph)
{
	assert(ph && ph->size > 0);
	Swap(ph->data, ph->data + ph->size - 1);
	ph->size--;
	AdjustDown(ph->data, ph->size, 0);
}

6. 向下调整

堆向下调整的思路和向上调整刚好相反 (还是以小根堆为例):

1、找出子节点中较小的节点;2、比较父节点与较小子节点,如果父节点大于较小子节点则交换两个节点;3、交换之后,原来的子节点成为新的父节点,然后继续 1 2 步骤,直到调整为堆的结构。

注意:

  1. 向下调整算法比向上调整多一个参数size,因为向下调整需要判断子节点不能超过堆的范围

  2. 向下调整算法有一个前提:左右子树必须都是堆,才能调整。

在这里插入图片描述

void Swap(HeapDataType* a, HeapDataType* b)
{
	assert(a && b);
	HeapDataType temp = *a;
	*a = *b;
	*b = temp;
}

//向下调整 - 小堆
void AdjustDown(HeapDataType* nums, int size, int sub)
{
	assert(nums);
	int parents = sub;
	int minchild = parents * 2 + 1;//先默认最小孩子为左孩子
	//当子结点超过堆的范围就结束
	while (minchild < size)
	{
        //选出真正的较小子节点
		if (minchild + 1 < size && nums[minchild + 1] < nums[minchild])
		{
			minchild++;
		}
		//如果父节点大于最小的子结点,就交换双方位置
		if (nums[parents] > nums[minchild])
		{
			Swap(nums + parents, nums + minchild);
			//迭代
			parents = minchild;
			minchild = parents * 2 + 1;
		}
		//否则不用向下调整,直接跳出循环
		else
		{
			break;
		}
	}
}

和向上调整类似,如果我们想要向下调整为大堆,也只需要改变交换条件:

//选出真正的较大子节点
if (minchild + 1 < size && nums[minchild + 1] > nums[minchild])
//如果父节点小于最大的子结点,就交换双方位置
if (nums[parents] < nums[minchild])

7、取出堆顶的元素

HeapDataType HeapTop(heap* ph)
{
	assert(ph && ph->size > 0);
	return ph->data[0];
}

8、返回堆的元素个数

int HeapSize(heap* ph)
{
	assert(ph);
	return ph->size;
}

9、判断堆是否为空

bool IsHeapEmpty(heap* ph)
{
	assert(ph);
	return ph->size == 0;
}

10、打印堆中的数据

void PrintHeap(heap* ph)
{
	assert(ph);
	for (int i = 0; i < ph->size; i++)
	{
		printf("%d ", ph->data[i]);
	}
	printf("\n");
}

11、堆的销毁

void DestroyHeap(heap* ph)
{
	assert(ph);
	free(ph->data);
	ph->capacity = 0;
	ph->size = 0;
}

三、堆的应用

1. 堆排序

堆排序是选择排序的一种,它的时间复杂度为 O(N*logN),空间复杂度为 O(1),是一种十分优秀的排序算法,主要步骤就是建堆和选择数。

建堆

建堆有两种方法:向上调整建堆和向下调整建堆。

向上调整建堆: 把数组的第一个元素作为堆的根节点,然后把其余元素看作插入结点在堆末尾插入,每插入一个元素就向上调整一次,从而始终保证堆的结构;

在这里插入图片描述

向上调整建堆的时间复杂度: 由于堆是完全二叉树,而满二叉树是完全二叉树的一种,所以此处为了简化计算,使用满二叉树来求时间复杂度

在这里插入图片描述

如上图,我们把每一层的节点个数乘以每一个节点需要调整的次数,最后再求和,就可以得到一共需要调整的次数;然后再根据满二叉树节点总数与树的高度的关系将表达式中的h替换掉,最终可以得到向上调整建堆的时间复杂度为:O(N*logN);

向下调整建堆: 从倒数第一个非叶子节点 (即最后一个叶子节点的父节点) 开始向下调整,直到调整到根。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dois3qci-1692531353764)(C:\Users\86185\AppData\Roaming\Typora\typora-user-images\image-20230818180009394.png)]

向下调整建堆的时间复杂度: 使用满二叉树来求时间复杂度

在这里插入图片描述

如上图,向下调整建堆的时间复杂度为:O(N),比向上调整的效率更高,这似乎有点反直觉,因为两者看起来并没有什么本质的不同,无非就是方向相反,但是简单分析就能发现原因,因为向下调整随着调整次数的增大,需要调整的结点越来越少,两者相乘就不会特别大,另外最后一层(接近一半的结点)是不需要调整的。

综合上面两种建堆方法,最好选择向下调整建堆,建堆的时间复杂度为:O(N);

选数

建堆完成后,接下来就是选数,假设现在我们要排降序,那么方法一共有两种:

1、建大堆:开辟一个和原数组同等大小的新数组,然后对原数组建大堆,每次取出堆顶的元素 (最大的元素) 按顺序放在新数组中,最后再将新数组中的数据覆盖至原数组;

缺点:需要开辟额外的空间,删除堆顶元素后向下调整的时间复杂度是O(logN),所以嵌套后时间复杂度:O(N*logN),空间复杂度:O(N);

2、建小堆:先对原数组建小堆,然后将堆顶和堆尾的数据进行交换,使得数组中最小的元素处于数组末尾,然后向下调整前n - 1个元素,使得次小的数据位于堆顶,最后重复前面的步骤,把次小的数据存放到最小的数据之前,直到数组有序;

优点:没有额外的空间消耗,且时间复杂度达到了 O(N*logN);

综合上面两种选数的方法,选数的时间复杂度为:O(N*logN),空间复杂度为:O(1);

void Swap(HeapDataType* a, HeapDataType* b)
{
	assert(a && b);
	HeapDataType temp = *a;
	*a = *b;
	*b = temp;
}

//向下调整 - 小堆
void AdjustDown(HeapDataType* nums, int size, int sub)
{
	assert(nums);
	int parents = sub;
	int minchild = parents * 2 + 1;//先默认最小孩子为左孩子
	//当子结点超过堆的范围就结束
	while (minchild < size)
	{
        //选出真正的较小子节点
		if (minchild + 1 < size && nums[minchild + 1] < nums[minchild])
		{
			minchild++;
		}
		//如果父节点大于最小的子结点,就交换双方位置
		if (nums[parents] > nums[minchild])
		{
			Swap(nums + parents, nums + minchild);
			//迭代
			parents = minchild;
			minchild = parents * 2 + 1;
		}
		//否则不用向下调整,直接跳出循环
		else
		{
			break;
		}
	}
}

//升序 -> 建大根堆
//降序 -> 建小根堆
void HeapSort(int* nums, int numsSize)
{
	//建堆method1:插入法 向上调整 O(N * logN)
	/*for (int i = 1; i < numsSize; i++)
	{
		AdjustUp(nums, i);
	}*/
	
	//建堆method2:向下调整 O(N)
	for (int i = (numsSize - 1 - 1) / 2; i >= 0; i--)
	{
		AdjustDown(nums, numsSize, i);
	}
    
    //选数
	//降序 -> 小根堆 -> 向下调整 O(N * logN) 
	while (numsSize > 1)
	{
		Swap(nums, nums + numsSize - 1);
		AdjustDown(nums, --numsSize, 0);
	}
}

int main()
{
	int nums[] = { 10, 26, 85, 4, 23, 14, 12, 36, 16 };
	HeapSort(nums, sizeof(nums) / sizeof(int));
	for (int i = 0; i < sizeof(nums) / sizeof(int); i++)
	{
		printf("%d ", nums[i]);
	}
	printf("\n");
    return 0;
}

在这里插入图片描述

2. Topk问题

TOP-K问题:即求数据集合中前K个最大的元素或者最小的元素,一般情况下数据量都比较大,无法排序;比如:世界500强、富豪榜、王者荣耀巅峰前十等。

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

  1. 用数据集合的前K个元素来建堆, 求前k个最大的元素,则建小堆,反之则建大堆;

  2. 用剩余的N-K个元素依次与堆顶元素来比较,满足条件则替换堆顶元素,并向下调整保持堆的结构;

void Swap(HeapDataType* a, HeapDataType* b)
{
	assert(a && b);
	HeapDataType temp = *a;
	*a = *b;
	*b = temp;
}

//向下调整 - 小堆
void AdjustDown(HeapDataType* nums, int size, int sub)
{
	assert(nums);
	int parents = sub;
	int minchild = parents * 2 + 1;//先默认最小孩子为左孩子
	//当子结点超过堆的范围就结束
	while (minchild < size)
	{
        //选出真正的较小子节点
		if (minchild + 1 < size && nums[minchild + 1] < nums[minchild])
		{
			minchild++;
		}
		//如果父节点大于最小的子结点,就交换双方位置
		if (nums[parents] > nums[minchild])
		{
			Swap(nums + parents, nums + minchild);
			//迭代
			parents = minchild;
			minchild = parents * 2 + 1;
		}
		//否则不用向下调整,直接跳出循环
		else
		{
			break;
		}
	}
}

int* Topk(int* nums, int numsSize, int k)
{
	int* ans = (int*)malloc(sizeof(int) * k);
	if (ans == NULL)
	{
		perror("Topk");
		exit(-1);
	}
	for (int i = 0; i < k; i++)
	{
		ans[i] = nums[i];
	}
    //向下建堆
	for (int i = (k - 1 - 1) / 2; i >= 0; i--)
	{
		AdjustDown(ans, k, i);
	}
	for (int i = k; i < numsSize; i++)
	{
		if (nums[i] > ans[0]) //如果数组中其他元素大于堆顶元素
		{
			ans[0] = nums[i]; //替换堆顶元素
			AdjustDown(ans, k, 0); //向下调整保持堆的结构
		}
	}
	return ans;
}

int main()
{
	srand((unsigned int)time(NULL)); //用时间戳产生随机数种子
	int* nums = (int*)malloc(sizeof(int) * 10000);
	if (nums == NULL)
	{
		perror("TestTopk");
		exit(-1);
	}
    //生成10000个小于10000的整数
	for (int i = 0; i < 10000; i++)
	{
		nums[i] = rand() % 10000;
	}
    //生成10个大于10000的整数
	for (int i = 0; i < 10; i++)
	{
		nums[rand() % 10000] = 10000 + i;
	}
	int* ans = Topk(nums, 10000, 10);
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", ans[i]);
	}
	printf("\n");
	free(ans);
	ans = NULL;
	free(nums);
	nums = NULL;
    return 0;
}

在这里插入图片描述

四、完整代码

Heap.h

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<stdbool.h>

typedef int HeapDataType;

typedef struct heap
{
	HeapDataType* data;
	int size;
	int capacity;
}heap;

//初始堆
void InitHeap(heap* ph);

//打印堆
void PrintHeap(heap* ph);

//往堆里插入数据
void HeapPush(heap* ph, HeapDataType x);

//删除堆的首元素
void HeapPop(heap* ph);

//获取堆的首元素
HeapDataType HeapTop(heap* ph);

//判断堆是否为空
bool IsHeapEmpty(heap* ph);

//求堆的大小
int HeapSize(heap* ph);

//销毁堆
void DestroyHeap(heap* ph);

//堆排序
void HeapSort(int* nums, int numsSize);

//Topk
int* Topk(int* nums, int numsSize, int k);

Heap.c

#include"heap.h"

void InitHeap(heap* ph)
{
	assert(ph);
	ph->data = NULL;
	ph->capacity = 0;
	ph->size = 0;
}

void PrintHeap(heap* ph)
{
	assert(ph);
	for (int i = 0; i < ph->size; i++)
	{
		printf("%d ", ph->data[i]);
	}
	printf("\n");
}

void Swap(HeapDataType* a, HeapDataType* b)
{
	assert(a && b);
	HeapDataType temp = *a;
	*a = *b;
	*b = temp;
}

//向上调整 - 小堆
void AdjustUp(HeapDataType* nums, int child)
{
	assert(nums);
	while (child > 0)
	{
		int parents = (child - 1) / 2;
		if (nums[child] < nums[parents])
		{
			Swap(&nums[child], &nums[parents]);
			child = parents;
		}
		else
		{
			break;
		}
	}
}

//向下调整 - 小堆
void AdjustDown(HeapDataType* nums, int size, int sub)
{
	assert(nums);
	int parents = sub;
	int minchild = parents * 2 + 1;//先默认最小孩子为左孩子
	//当子结点超过堆的范围就结束
	while (minchild < size)
	{
		if (minchild + 1 < size && nums[minchild + 1] < nums[minchild])
		{
			minchild++;
		}
		//如果父节点大于最小的子结点,就交换双方位置
		if (nums[parents] > nums[minchild])
		{
			Swap(nums + parents, nums + minchild);
			//迭代
			parents = minchild;
			minchild = parents * 2 + 1;
		}
		//否则不用向下调整,直接跳出循环
		else
		{
			break;
		}
	}
}

//小根堆
void HeapPush(heap* ph, HeapDataType x)
{
	assert(ph);
	if (ph->capacity == ph->size) //扩容
	{
		int newcapacity = ph->data == NULL ? 4 : ph->capacity * 2;
		HeapDataType* temp = (HeapDataType*)realloc(ph->data, sizeof(HeapDataType) * newcapacity);
		if (temp == NULL)
		{
			perror("realloc");
			exit(-1);
		}
		ph->data = temp;
		ph->capacity = newcapacity;
	}
	ph->data[ph->size] = x;
	AdjustUp(ph->data, ph->size);
	ph->size++;
}

//小根堆
void HeapPop(heap* ph)
{
	assert(ph && ph->size > 0);
	Swap(ph->data, ph->data + ph->size - 1);
	ph->size--;
	AdjustDown(ph->data, ph->size, 0);
}

HeapDataType HeapTop(heap* ph)
{
	assert(ph && ph->size > 0);
	return ph->data[0];
}

bool IsHeapEmpty(heap* ph)
{
	assert(ph);
	return ph->size == 0;
}

int HeapSize(heap* ph)
{
	assert(ph);
	return ph->size;
}

void DestroyHeap(heap* ph)
{
	assert(ph);
	free(ph->data);
	ph->capacity = 0;
	ph->size = 0;
}


//升序 -> 大根堆
//降序 -> 小根堆
void HeapSort(int* nums, int numsSize)
{
	//建堆method1:插入法 向上调整 O(N * logN)
	/*for (int i = 1; i < numsSize; i++)
	{
		AdjustUp(nums, i);
	}*/
	
	//建堆method2:向下调整 O(N)
	for (int i = (numsSize - 1 - 1) / 2; i >= 0; i--)
	{
		AdjustDown(nums, numsSize, i);
	}
	//降序 - 小根堆 - 向下调整 O(N * logN) 
	while (numsSize > 1)
	{
		Swap(nums, nums + numsSize - 1);
		AdjustDown(nums, --numsSize, 0);
	}
}

int* Topk(int* nums, int numsSize, int k)
{
	int* ans = (int*)malloc(sizeof(int) * k);
	if (ans == NULL)
	{
		perror("Topk");
		exit(-1);
	}
	for (int i = 0; i < k; i++)
	{
		ans[i] = nums[i];
	}
	for (int i = (k - 1 - 1) / 2; i >= 0; i--)
	{
		AdjustDown(ans, k, i);
	}
	for (int i = k; i < numsSize; i++)
	{
		if (nums[i] > ans[0])
		{
			ans[0] = nums[i];
			AdjustDown(ans, k, 0);
		}
	}
	return ans;
}

test.c

#include"heap.h"
#include<time.h>

void TestHeap()
{
	heap H;//创建堆变量
	InitHeap(&H);
	int nums[] = { 10, 26, 85, 4, 23, 14, 12, 36, 16 };
	for (int i = 0; i < sizeof(nums) / sizeof(int); i++)
	{
		HeapPush(&H, nums[i]);
	}
	PrintHeap(&H);
	int n = sizeof(nums) / sizeof(int);
	while (n--)
	{
		printf("%d ", HeapTop(&H));
		HeapPop(&H);
	}
}

void TestHeapSort()
{
	int nums[] = { 10, 26, 85, 4, 23, 14, 12, 36, 16 };
	HeapSort(nums, sizeof(nums) / sizeof(int));
	for (int i = 0; i < sizeof(nums) / sizeof(int); i++)
	{
		printf("%d ", nums[i]);
	}
	printf("\n");
}

void TestTopk()
{
	srand((unsigned int)time(NULL));
	int* nums = (int*)malloc(sizeof(int) * 10000);
	if (nums == NULL)
	{
		perror("TestTopk");
		exit(-1);
	}
	for (int i = 0; i < 10000; i++)
	{
		nums[i] = rand() % 10000;
	}
	for (int i = 0; i < 10; i++)
	{
		nums[rand() % 10000] = 10000 + i;
	}
	int* ans = Topk(nums, 10000, 10);
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", ans[i]);
	}
	printf("\n");
	free(ans);
	ans = NULL;
	free(nums);
	nums = NULL;
}

int main()
{
	//TestHeap();
	//TestHeapSort();
	TestTopk();
	return 0;
}
}

void TestHeapSort()
{
	int nums[] = { 10, 26, 85, 4, 23, 14, 12, 36, 16 };
	HeapSort(nums, sizeof(nums) / sizeof(int));
	for (int i = 0; i < sizeof(nums) / sizeof(int); i++)
	{
		printf("%d ", nums[i]);
	}
	printf("\n");
}

void TestTopk()
{
	srand((unsigned int)time(NULL));
	int* nums = (int*)malloc(sizeof(int) * 10000);
	if (nums == NULL)
	{
		perror("TestTopk");
		exit(-1);
	}
	for (int i = 0; i < 10000; i++)
	{
		nums[i] = rand() % 10000;
	}
	for (int i = 0; i < 10; i++)
	{
		nums[rand() % 10000] = 10000 + i;
	}
	int* ans = Topk(nums, 10000, 10);
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", ans[i]);
	}
	printf("\n");
	free(ans);
	ans = NULL;
	free(nums);
	nums = NULL;
}

int main()
{
	//TestHeap();
	//TestHeapSort();
	TestTopk();
	return 0;
}
Ubuntu系统下USB无线网卡无法识别的解决方案 Realtek的USB设备可能是无线网卡、有线网卡、读卡器等,系统仅知道“这是Realtek的USB设备”,但不知道“如何将其作为无线网卡驱动”。通过以上步骤,RTL8812AU 芯片的 Edimax 无线网卡应能被 Ubuntu 识别并正常工作。Realtek等品牌的无线网卡可能被系统简单识别为"USB设备",但未匹配到对应的无线驱动,导致设备无法发挥网络功能。是系统识别了USB设备,却未正确加载无线网卡驱动。(前4位是厂商ID,后4位是产品ID),显示设备已识别,但网络管理工具无反应, 阅读详情

相关推荐

2.1 SRv6 TE Policy

SRv6 TE Policy是在SRv6技术基础上发展的一种新的隧道引流技术。SRv6 TE Policy路径表示为指定路径的段列表(Segment List),称为SID列表(Segment ID List)。每个SID列表是从源到目的地的端到端路径,并指示网络中的设备遵循指定的路径,而不是遵循IGP计算的最短路径。如果数据包被导入SRv6 TE Policy中,SID列表由头端添加到数据包上,网络的其余设备执行SID列表中嵌入的指令。

m0_70976024的博客 1237

数据结构之堆

目录一、堆1. 概念2. 性质3. 结构二、堆的实现1. 算法实现:(1)向下调整算法(2)向上调整算法(堆的创建)(3)堆的插入(4)堆的删除(5)堆的排序2. 代码实现(小堆):(1)堆的定义(2)交换(3)检查容量(4)向下调整(5)向上调整(6)堆的初始化(7)堆的创建(8)销毁堆(9)堆的插入(10)堆的删除(11)获取堆顶元素(12)判空(13)堆排序(14)打印堆 一、堆 1. 概念 如果有一个关键码的集合K = {k0,k1, k2,…,kn-1},把它的所有元素按完全二叉树的顺序存储方式

qq_34270874的博客 3万+

jetson nano网络配置(有线、无线、USB)

注意:本节内容使用Jetson nano B01 版本或Jetson nano ALT 版本安装方式相同,在安装无线网卡等设备时,由于线材和设备比较脆弱,需要轻拿轻放,接口连接不上时,需要重新对准接口在进行连接,不要强行连接。Jetson nano 需要连接网络来进行更多的操作,本节就来讲解Jetson nano 的三种网络连接方法。有线:我们可以使用网线连接Jetson nano 的RJ45 接口,实现联网。

m0_73841621的博客 1万+

数据结构 —— 堆

堆是一种特殊的树形数据结构,称为“二叉堆”(binary heap)看它的名字也可以看出堆与二叉树有关系:其实堆就是一种特殊的二叉树堆的性质:堆中某个结点的值总是不大于或不小于其父结点的值;堆总是一棵完全二叉树。

2301_78957570的博客 3003

MPU6050模块DIY翻车实录:ID能读但数据全为零?原来是这个10uF电容惹的祸

本文详细记录了MPU6050模块调试过程中遇到的ID能读但数据全为零的问题排查过程。通过对比不同参考设计,发现CPOUT引脚10uF电容选择不当是导致数据异常的根源,更换为官方推荐的2.2nF电容后问题解决。文章为硬件开发者提供了宝贵的电路设计经验。

weixin_29158047的博客 319

Win10环境借助DockerDesktop部署Open web UI集成DeepSeek

Win10环境借助DockerDesktop部署Open web UI集成DeepSeek,并借助DuckDuckGo实现联网查询

虎鲸不是鱼 1698

C++数据结构·堆

堆是一种重要的完全二叉树数据结构,分为最大堆和最小堆两种类型。最大堆的每个节点值大于等于子节点,最小堆则相反。堆通常用数组表示,支持O(log n)的插入和删除操作,能O(1)获取最大/最小值。本文详细介绍了堆的特性、表示方法和时间复杂度,并提供了C++模板代码,包括STL优先队列和手写堆的实现。通过合并果子问题(P1090)展示了堆的实际应用,给出了两种AC代码方案。最后推荐了相关刷题题单,帮助读者巩固堆的知识和应用能力。

Gohldg的博客 2981

柑橘病虫害识别方案总结

1. 方案一 1.1 赛题背景 1.2 数据分析 1.3 模型设计 1.3.1 模型具体方案 1.3.2 模型设计 1.4 实验结果 1.5 总结与展望 2. 方案二 2.1 赛题介绍 2.2 数据分析 2.3 算法方案解析 2.3.1 检测模型 2.3.2 提升策略 2.4 优化思路 3. 方案三 3.1 数据分析 3.1.1 数据说明 3.2 技术展示 3.2.1 数据输入端 3.2.2 模型结构端 3.2.3 上下文信息 3.2.4 训练优化端 3.2.5 创新性分析 3.3 算法及系统思考.....

herosunly的博客 2万+

数据结构——堆

数据结构——堆 求点赞收藏评论关注!!! 感谢各位大佬!!!

2301_80401288的博客 1215

超声波传感器(CHx01) 学习笔记 Ⅲ-API介绍

超声波传感器(CHx01) API介绍

nicole088的专栏 1386

Linux系统安全学习手册

Linux反弹shell Linux系统应急响应 Linux服务器安全加固 Linux下的用户、组和权限 Linux中的gcc Linux中su和sudo的用法和区别 Linux系统python2与python3共存 Linux中grep工具的使用 Linux中环境变量的设置 Linux中常见的150个命令(干货) Linux中编写Shell脚本 LNMP环境搭建Wordpress博客 LAMP环境搭建一个Discuz论坛 Linux运维比较常用的一些脚本 Linux系统中安装软

谢公子的博客 7532

数据结构—堆

堆是一种特殊的数据结构,它是一棵完全二叉树。把所有的元素按照完全二叉树的结构,按照层序遍历的顺序储存在一维数组中,如果该二叉树满足父节点小于等于子节点,叫做最小堆(小根堆);如果该二叉树满足父节点大于等于子节点,叫做最大堆(大根堆)。​。

Flashing-C的博客 5039

PC 端微信页面拆分及 JavaFx 使用

一、前言 对于一个知识点的学习过程来说,往往使用自己熟悉的工具或方式才更易于上手。因为所有同类型的知识点在抛出复杂的流程拨云见日后,所能得到的几乎都是同样的设计思想和实现理论。 在 Java 语言桌面版开发中,直至目前共提供了三套 UI 开发方式;Awt、Swing、JavaFx,以及一些扩展组件 SWT 等。在这三套 UI 组件中,JavaFx 是最新也是最为好用的,因为他提供了丰富的功能,以及...

bugstack虫洞栈 3606

堆(数据结构)

1.堆的概念 如果有一个关键码的集合K = {k0,k1, k2,…,kn-1},把它的所有元素按完全二叉树的顺序存储方式存储在一个一维数组中,并满足:Ki <= K2i+1 且 Ki<=K2i+2 ,则称为小堆(或大堆)。将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。除了最后一层以外上面的节点但是非空的,最后一层节点是从左到右依次排布的。如图: ...

zgdlxs的博客 2万+

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

< 数据结构 > 堆的实现

目录 1、前言 堆的概念 堆的结构 2、堆的实现 2.1、准备工作 创建堆结构 初始化堆 堆的打印 堆的销毁 建堆时间复杂度 2.2、堆调整 堆的交换 堆向上调整算法 堆向下调整算法 2.3、核心功能 堆的插入 堆的删除 ...

bit_zyx的博客 2812

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

数据结构——【堆】详解

一. 堆🌲 1. 堆的概念 堆(heap):一种有特殊用途的数据结构——用来在一组变化频繁(发生增删查改的频率较高)的数据集中查找最值。 堆在物理层面上,表现为一组连续的数组区间:long[] array ;将整个数组看作是堆。 堆在逻辑结构上,一般被视为是一颗完全二叉树。 满足任意结点的值都大于其子树中结点的值,叫做大堆,或者大根堆,或者最大堆;反之,则是小堆,或者小根堆,或者最小堆。当一个堆为大堆时,它的每一棵子树都是大堆。 2. 堆的存储方式 从堆的概念可知,堆是一棵完全二叉树,

Lucky_mzc的博客 7984

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

C++数据结构详解——堆

1.堆的原理精讲 最大堆特点: 当然,也有最小堆,最小堆就是将最大堆反过来,根结点为最小值~ 堆是树中最有个性的树,他是用数组表示的树 。 2.在数组中快速创建堆算法 1.首先我们需要找到最后一个结点的父结点如图(a),我们找到的结点是 87,然后找出该结点的最大子节点与自己比较,若该子节点比自身大,则将两个结点交换。 图(a)中,87 比左子节点 95 小,则交换之。如图(b)所示 2.我们移动到第一步前一个父节点 93,如图(c)所示.同理做第一步的比较...

qq_54169998的博客 1万+

什么是堆?深入理解堆数据结构及其应用

什么是堆?它和内存分配中的堆是一回事吗?

大葱的田地🌱 1148

堆——数据结构

这里写自定义目录标题堆的基本概念堆的常用方法构建 首先,要说明一下本篇文章说的堆和JVM内存结构里边的堆是两个东西。它是优先级队列的底层数据结构。 new出来的空间(new一个对象)-------->这里的堆指的是一块具有特殊作用的内存空间(JVM内存区域中) 优先级队列的底层数据结构 ------->这里的堆是一种数据结构 那么什么是堆这种数据结构呢? 堆的基本概念 这个堆是建立在完全二叉树的存储方式上,我们知道完全二叉树适合用顺序的存储方式来存储,因为它是没有满的满二叉树,所以通过数组来

yuiop123455的博客 1377

数据结构之——堆

堆可以被看作是一棵树的数组对象。在存储结构上,堆与普通树有一些区别。普通树的存储方式可以有多种,如链式存储、数组存储等。而堆通常采用数组来存储完全二叉树,这样可以利用数组的索引关系快速定位节点的父子关系。孩子兄弟表示法是一种表示树结构的方式。在这种表示法中,每个节点有两个指针,分别指向其第一个孩子节点和下一个兄弟节点。相比之下,堆采用数组存储时,通过特定的索引计算可以快速找到节点的父子关系。

m0_71974552的博客 3228

【数据结构】什么是堆

#

weibo_dm的博客 2万+

堆【C++数据结构】

本文深入剖析堆结构原理、数组实现、核心操作及STL应用,详解Top-K等实战场景

不会C++、害怕嵌入式且不爱看动漫的小趴菜 1万+
上一篇: 【数据结构】栈和队列
下一篇: 【数据结构】二叉树
岸边露伴'
博客等级 码龄3年 2粉丝 20原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值