堆结构与堆排序

堆结构

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

  • 大根堆
  • 小根堆

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

对于下标为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); //维护大根堆的性质
	}
}
相关推荐
过期动态11 小时前
【LeetCode 热题 100】找到字符串中所有字母异位词
java·数据结构·算法·leetcode·职场和发展·rabbitmq
来一碗刘肉面11 小时前
栈在递归中的应用
数据结构·算法
8K超高清11 小时前
博冠获中国电影电视技术学会科技进步奖
人工智能·科技·算法·安全·接口隔离原则·智能硬件
皓月斯语11 小时前
程序设计语言的特点
开发语言·数据结构·c++
微石科技11 小时前
长期慢病如何做好居家管理?宁波微石科技星梦云康改善周期数据零散短板
大数据·科技·物联网·算法
邪修king12 小时前
C++ 进阶终章:异常机制与智能指针全解 —— 从错误处理到 RAII 资源管理,打通现代 C++ 的核心命脉
android·数据结构·c++
微露清风12 小时前
快速排序算法学习记录
学习·算法·排序算法
梓䈑17 小时前
【算法题攻略】BFS 解决FloodFill 算法、最短路问题、多源BFS 和 拓扑排序
c++·算法·宽度优先
Adios79419 小时前
设置交集大小至少为2
数据结构·算法·leetcode