堆结构与堆排序

堆结构

堆是一棵特殊的完全二叉树,树中的节点满足一定的性质,根据性质的不同,堆有两种:

  • 大根堆
  • 小根堆

虽然堆是用完全二叉树表示的,但在使用时通常用一个数组来存储。以数组的下标来标识堆中的节点,并且可以根据下标来获得某一节点的父节点和左右子节点。

对于下标为i的节点,其父节点为(i - 1) / 2,其左子节点为2 * i + 1,右子节点为2 * i + 2。

我们定义一个节点的高度为从该节点到叶子节点的最长简单向下路径的边数。树的高度就是根节点的高度。对于具有n个元素的数组,将其看作完全二叉树的话,树的高度为lgn。

txt 复制代码
arr = { 1, 2, 3, 4, 5, 6, 7 }

            1
        2       3
     4    5   6   7

同时可以看到,对于大小为n的堆,n/2为该堆的第一个叶子节点。

大根堆

对于任意一个节点来说,它的值都比其子节点的值大。

txt 复制代码
            7
        6       5
     4     3  2   1

堆的调整

在讲如何构建堆之前,先讲讲堆的调整操作。以大根堆为例。

对于堆中的一个节点来说,调整的方式有两种:向上调整和向下调整。

什么是向上调整?对于节点i来说,以i为根的子树已经满足了大根堆的性质,但是i之上的节点不一定满足大根堆的性质。向上调整将i与其父节点作比较,如i节点的值比父节点大,则交换i和父节点。如此重复,直到i比父节点小或者i为根节点。

cpp 复制代码
#define PARENT(i)	((i - 1) / 2)
#define LEFT(i)		(2 * i + 1)
#define RIGHT(i)	(2 * i + 2)

static void swap(int* arr, int i, int j)
{
	arr[i] += arr[j];
	arr[j] = arr[i] - arr[j];
	arr[i] = arr[i] - arr[j];
}

//堆的插入操作,向上调整,将i位置的节点调整至符合大根堆性质的位置
static void MaxHeapInsert(int* arr, int i)
{
	int p = PARENT(i);
	if (p >= 0 && arr[i] > arr[p])
	{
		swap(arr, i, p);
		MaxHeapInsert(arr, p);
	}
}

了解了向上调整,向下调整就很好理解了:

cpp 复制代码
//向下调整。
static void MaxHeapfiy(int* arr, int size, int i)
{
	int l = LEFT(i);
	int r = RIGHT(i);

	int largest;
	if (l < size && arr[l] > arr[i])
		largest = l;
	else largest = i;

	if (r < size && arr[r] > arr[largest])
		largest = r;

	if (largest != i)
	{
		swap(arr, i, largest);
		MaxHeapfiy(arr, size, largest);
	}
}

构建大根堆

假如现在有一个数组,我们希望把它变为大根堆的形式,该如何做到?

很简单,我们只需要遍历数组,对每个节点都进行向上调整即可:

cpp 复制代码
static void BuildMaxHeap(int* arr, int size)
{
	for (int i = 0; i < size; i++)
		MaxHeapInsert(arr, i);
}

小根堆

了解了大根堆及其调整方法之后,小根堆的调整和构建自然不是问题。

堆排序

我们现在有一个无序数组,我们可以利用堆的性质来对其进行排序。以从小到大排序为例,我们使用了大根堆。

堆排序步骤如下:

  1. 将数组构建为大根堆
  2. 将堆顶元素和堆最后一个叶子节点交换
  3. 堆的大小减一
  4. 维护大根堆
  5. 重复1到4的步骤直到堆的大小为1
cpp 复制代码
static void HeapSort(int* arr, int size)
{
	BuildMaxHeap(arr, size);
	int heap_size = size;
	while (heap_size != 1) //注意终止条件
	{
		swap(arr, 0, heap_size - 1); //将最大值与末尾元素交换
		printArr(arr, size);
		heap_size--; //调整堆的大小
		MaxHeapfiy(arr, heap_size, 0); //维护大根堆的性质
	}
}
相关推荐
会员源码网8 小时前
使用`mysql_*`废弃函数(PHP7+完全移除,导致代码无法运行)
后端·算法
木心月转码ing9 小时前
Hot100-Day10-T438T438找到字符串中所有字母异位词
算法
HelloReader10 小时前
Wi-Fi CSI 感知技术用无线信号“看见“室内的人
算法
颜酱12 小时前
二叉树分解问题思路解题模式
javascript·后端·算法
qianpeng89714 小时前
水声匹配场定位原理及实验
算法
董董灿是个攻城狮1 天前
AI视觉连载8:传统 CV 之边缘检测
算法
AI软著研究员1 天前
程序员必看:软著不是“面子工程”,是代码的“法律保险”
算法
FunnySaltyFish1 天前
什么?Compose 把 GapBuffer 换成了 LinkBuffer?
算法·kotlin·android jetpack
颜酱1 天前
理解二叉树最近公共祖先(LCA):从基础到变种解析
javascript·后端·算法
地平线开发者2 天前
SparseDrive 模型导出与性能优化实战
算法·自动驾驶