IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> 【数据结构】------ 堆 -> 正文阅读

[数据结构与算法]【数据结构】------ 堆

目录

堆的概念及结构

堆的实现

堆向上调整算法

堆向下调整算法

堆的创建

堆的初始化和销毁?

堆的插入

堆的删除

获取堆顶的数据

?获取堆的数据个数

堆的判空?

TopK问题(在N个数找出最大(小)的前K个)

堆排序


?

堆的概念及结构

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

(所有数组都可以表示为完全二叉树,但不一定可以表示为堆)

假设parent是父亲节点在数组中的下标,leftchild=parent*2+1,rightchild=parent*2+2。

假设孩子在数组中的下标是child,不管是左孩子还是右孩子,parent=(child-1)/2。)

堆的性质:

  • ?堆中某个节点的值总是不大于或不小于其父节点的值;
  • ?堆总是一棵完全二叉树。

大堆:树中一个树及子树中,父亲都大于等于孩子。

小堆:树中一个树及子树中,父亲都小于等于孩子。

堆的实现

堆向上调整算法

当我们在一个堆的末尾插入一个数据后,需要对堆进行调整,使其仍然是一个堆,这时需要用到堆的向上调整算法。

向上调整算法的基本思想(以建小堆为例):
?1.将目标结点与其父结点比较。
?2.若目标结点的值比其父结点的值小,则交换目标结点与其父结点的位置,并将原目标结点的父? ? ? ?结点当作新的目标结点继续进行向上调整。若目标结点的值比其父结点的值大,或者已经调整? ? ? ?到了根节点,则停止调整,此时该树已经是小堆了。

?

void Swap(HPDataType* px, HPDataType* py)
{
	HPDataType tmp = *px;
	*px = *py;
	*py = tmp;
}
void AdjustUp(int* a,int child)
{
	int parent = (child - 1) / 2;
	//while (parent > 0)
    //parent =0 ---恒成立,循环是通过beak跳出去的,不符合要求,但是能达到想要的效果
    while (child > 0)
	{
		if (a[child] < a[parent])
		{
			Swap(&a[child], &a[parent]);
			child = parent;
			parent = (child - 1) / 2;//负数除2 parent=0

		}
		else
		{
			break;
		}
		
	}
}

堆向下调整算法

现在我们给出一个数组,逻辑上看做一颗完全二叉树。我们通过从根节点开始的向下调整算法可以把它调整成一个小堆。向下调整算法有一个前提:左右子树必须是一个堆,才能调整。

向下调整为小堆,那么根结点的左右子树必须都为小堆。
向下调整为大堆,那么根结点的左右子树必须都为大堆。

?向下调整为小堆示意图:

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

void Swap(HPDataType* px, HPDataType* py)
{
	HPDataType tmp = *px;
	*px = *py;
	*py = tmp;
}
void AdjustDown(HPDataType* a, int n, int parent)
{
	int child = 2 * parent + 1;
	
	//调整结束条件:
	//父亲 <= 小的孩子,停止
	//调整到叶子节点(叶子节点没有孩子,左孩子下标超出数组范围,则是调整到叶子节点)
	while (child < n)
	{
		//选出左右孩子小的那一个(小堆)
		if (child + 1 < n && a[child + 1] < a[child])//右孩子可能不存在
		{
			++child;
		}
		//如果小的孩子小于父亲,则交换,并继续向下调整(小堆)
		if (a[child] < a[parent])
		{
			Swap(&a[child], &a[parent]);
			parent = child;
			child = 2 * parent + 1;
		}
		else
		{
			break;
		}
	}
}

使用堆的向下调整算法,最坏的情况下(即一直需要交换结点),需要循环的次数为:h - 1次(h为树的高度)。而h = log2(N+1)(N为树的总结点数)。所以堆的向下调整算法的时间复杂度为:O(logN)?。?

堆的创建

下面我们给出一个数组,这个数组逻辑上可以看做一颗完全二叉树,但是还不是一个堆,现在我们通过算法,把它构建成一个堆。根节点左右子树不是堆,我们怎么调整呢?

int a[] = {1,5,3,8,7,6};

这里我们从倒数的第一个非叶子节点的子树开始调整,一直调整到根节点的树,就可以调整成堆。

?

倒数的第一个是最后一个节点的父亲

child=parent*2+1

parent=(child-1)/2

for (int i = (n - 1 - 1) / 2; i >= 0; i--)
	{
		AdjustDown(a, n, i);
	}

建堆的时间复杂度:O(N)?

堆的初始化和销毁?

堆的结构:

typedef int HPDataType;//堆的数据类型
typedef struct Heap
{
	HPDataType* a;//数组--存储堆
	int size;    //堆中数据的个数
	int capacity;//数组的容量
}HP;

构建一个堆首先对结构进行初始化,使用完堆后要进行销毁(为了避免内存泄漏,使用完动态开辟的内存空间后都要及时释放该空间)

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

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

堆的插入

在堆的末尾插入数据,向上调整成为小堆(大堆)

void HeapPush(HP* hp, HPDataType x)
{
	assert(hp);
	if (hp->size == hp->capacity)
	{
		size_t newcapacity = hp->capacity == 0 ? 4 : hp->capacity * 2;
		HPDataType* tmp = (HPDataType*)realloc(hp->a, sizeof(HPDataType)*newcapacity);
		if (tmp == NULL)
		{
			printf("realloc fail\n");
			exit(-1);
		}
		hp->a = tmp;
		hp->capacity = newcapacity;
	}
	
	hp->a[hp->size] = x;
	hp->size++;
	//向上调整
	AdjustUp(hp->a, hp->size - 1);
}

堆的删除

删除堆是删除堆顶的数据,将堆顶的数据和最后一个数据一换,然后删除数组最后一个数据,再进行向下调整算法。

//删除堆顶的数据
void HeapPop(HP* hp)
{
	assert(hp);
	assert(!HeapEmpty(hp));

	Swap(&hp->a[0], &hp->a[hp->size - 1]);
	hp->size--;

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

获取堆顶的数据

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

?获取堆的数据个数

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

堆的判空?

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

TopK问题(在N个数找出最大(小)的前K个)

例:
1000个数找出最大的前10个
方式一:先排降序,前十个就是最大的。时间复杂度:O(N+logN*K),排序方法可用快排,归并等。
方式二:N个数依次插入大堆(时间复杂度:N),Pop K次,每次取堆顶的数据,就是前K个。时间复杂度:O(N+logN*K)
方式三:假设N非常大,N是十亿,内存中存不下这些数,他们存在文件中,K是100。这时方式一和方式二都不能用了。
1.用前K个数建立一个K个数的小堆。
2.剩下的N-K个数,依次跟堆顶的数据进行比较,如果比堆顶数据大,就替换堆顶的数据,再向下调整。
3.最后堆里面K个数就是最大的K个数。时间复杂度:K+(N-K)logK ?----> O(N*logK)

方式三代码:

//Topk问题
void PrintTopK(int* a, int n, int k)
{
	HP hp;
	HeapInit(&hp);
	//前K个数建立小堆
	for (int i = 0; i < k; i++)
	{
		HeapPush(&hp, a[i]);
	}
	//N-K 个数依次和堆顶的数据比较
	for (int i = k; i < n; i++)
	{
		if (a[i] > HeapTop(&hp))
		{
			HeapPop(&hp);
			HeapPush(&hp, a[i]);

			//hp.a[0] = a[i];
			//AdjustDown(hp.a,hp.size,0);

		}
	}
	HeapPrint(&hp);
	HeapDestory(&hp);
	
}

堆排序

排升序构建大堆。

排降序构建小堆

堆排序(升序)
1.构建大堆,选出最大的数
2.将第一个数与最后一个数交换
3.将调整后的最后一个数不看成堆里面的数据,向上调整,找出次小的数,将次小的数和最后一个数交换(此时最后一个数实际上是第n-1个数)
4.以此类推,最后堆里面的数据就是升序的了。

void HeapSort(int* a,int n)
{
	//构建大堆(向上调整,从第二个节点开始)
	/*for (int i = 1; i < n; i++)
	{
		AdjustUp(a, i);
	 }*/
	//构建大堆(向下调整,从最后一个非叶子节点,最后一个非叶子节点是最后一个节点的父亲)
	for (int i = (n - 1 -1)/2; i >= 0; i--)
	{
		AdjustDown(a, n, i);
	}
	//依次选数,调堆
	for (int end = n - 1; end > 0; end--)
	{
		Swap(&a[end], &a[0]);
		//再调堆,选出次小的数
		AdjustDown(a, end, 0);
	}
}

  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2022-10-17 12:59:49  更:2022-10-17 13:01:09 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年9日历 -2024/9/30 3:35:42-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码