最大堆、最小堆的构造,利用堆实现数组排序的C++代码

本文介绍了堆的数据结构,包括最大堆和最小堆的概念,并详细阐述了如何使用C++构造堆和实现堆排序的过程。通过自底向上的策略调整二叉树,确保堆满足最大堆或最小堆的性质,最后讨论了堆排序的实现原理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、什么是堆?

堆通常是一个可以被看做一棵树的数组对象。堆总是满足下列性质:

  • 堆中某个节点的值总是不大于或不小于其父节点的值;

  • 堆总是一棵完全二叉树。

简单点说堆就是满足特定条件的完全二叉树,这里的“特定条件”分两种:即要么这棵树所有的节点都不大于(小于等于)其根节点(如果有)的值,如下图所示就称为最大堆
在这里插入图片描述

要么这棵树所有的节点都不小于(大于等于)其根节点的值,如下图所示的称为最小堆

在这里插入图片描述
一句话,对于一颗完全二叉树,根节点总最大时叫最大堆,根节点总最小时叫最小堆。

二、如何构造一个堆

以构建最大堆为例,为了方便起见,不妨用一个一维数组来表示一颗完全二叉树,例如:

int heap[] = {
    16,4,10,14,7,9,3,2,8,1 };

已知heap是一个完全二叉树,自然能够画出它的等效结构:

在这里插入图片描述
显然heap不是最大堆,为了使其满足最大堆结构,我们需要对二叉树的结构进行调整。

为了保证每次调整都向最大堆的目标更进一步,我们应当对整棵树调整。我们采取自下而上的策略调整二叉树(为什么不是自上而下呢?)。

具体思路是:1.对于除了叶子节点外的任一节点A,假设其两个孩子节点分别为B、C,如果A的值不是最大的,则将节点A和最大的那个节点(B或者C)交换。考虑到A节点被下放之后可能会影响已经调整好的子树(如果A不是叶子结点),我们应当做好后续收尾工作——对A的当前位置重复上一步的操作(即递归),直到A已经是最大数值时调整完毕。

  1. 第一个小目标:实现一颗最小子树的调整。需要注意我们已经选择了自下而上的策略,当对根节点A进行操作时,它的两个左右子树都已经满足最大堆的条件,需要做的就是将A放到合适的位置以及处理好一旦A下沉给底层带来的那些新问题。
void MaxHeapIFY(int *heap,int index,int heap_size)
{
   //寻找当前节点和其左右孩子中的最大值并交换,因为实际是自下而上操作,所以默认当前节点的左右子树都已是最大堆
	int left = 2 * index + 1;
	int right = left + 1;
	int largest = index;
	if (left < heap_size && heap[left]>heap[index]) // not out of index
		largest = left;
	if (right<heap_size && heap[right]>heap[largest])
		largest = right;
	if (largest != index) // 交换最大节点
	{
   
		int temp = heap[largest];
		heap[largest] = heap[index];
		heap[index] = temp;
		//如果当前堆发生改变,则递归地操作后续堆以适应最大堆结构
		MaxHeapIFY(heap, largest, heap_size);
	}
}

2.自底向下调用第一步的函数对整棵树进行调整,这里需要注意的细节是叶子结点在数组中的下标为heap_size/2-1

void BuildMaxHeap(int* heap,int heap_size)
{
   
	for (int i = (heap_size) / 2 - 1; i >= 0; i--)
	{
   
		MaxHeapIFY
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值