排序-堆排序(Heap Sort)

目录

前言:

堆排序思路

[为什么升序 → 建大根堆;降序 → 建小根堆。](#为什么升序 → 建大根堆;降序 → 建小根堆。)

演示图如下:

代码如下:

练习

排序的时间复杂度与空间复杂度

结语:


前言:

排序算法是计算机数据处理领域最基础、最核心的一类算法,广泛应用于数据检索、业务统计、任务调度等场景。常见的排序算法中,冒泡排序、选择排序、插入排序时间复杂度为(O(n^2)),面对大规模数据时效率低下;快速排序虽平均性能优异,但最坏情况下时间复杂度会退化至 (O(n^2)),且属于不稳定排序。

堆排序作为一种基于二叉堆数据结构实现的原地排序算法,凭借稳定的 (O(nlog n)) 时间复杂度、(O(1)) 的额外空间开销,弥补了上述算法的短板。它借助大根堆、小根堆的特性,不断选取当前区间内的极值,逐步完成元素就位。在学习堆排序的过程中,非常容易出现「升序建小根堆」的直觉误区。本文从堆的底层性质出发,完整讲解堆排序的原理、向上调整、向下调整、建堆逻辑与排序流程,结合实例剖析升序选择大根堆、降序选择小根堆的根本原因,给出可运行的 C++ 代码实现,帮助彻底吃透堆排序。

其他排序:

快速排序:排序-快速排序(Quick sort)(基础版)-CSDN博客

插入排序:排序---插入排序(Insertion Sort)-CSDN博客

冒泡排序与选择排序:排序-选择排序(Selection Sort)冒泡排序(Bubble Sort)-CSDN博客

希尔排序:排序-希尔排序(Shell Sort)-CSDN博客

归并排序:排序-归并排序(Merge Sort)-CSDN博客

堆排序思路

堆排序利用大根堆 的性质:堆顶元素永远是当前数组里最大值

先把整个数组构建成大根堆。

堆顶最大值和数组最后一个元素交换,最大值就位。

剩下前面未排序部分,重新向下调整恢复成大根堆。

重复交换 + 调整,直到全部有序,最终数组升序。

重点: 升序 → 建大根堆 ;降序 → 建小根堆。

虽然这个堆是完全二叉树,但是本质是还是数组,这是我们将其看成了堆。

为什么升序 → 建大根堆;降序 → 建小根堆。

堆排序是原地排序算法,不开额外数组,直接在原数组上完成排序。 核心循环逻辑固定不变:

将数组构建成堆。

交换堆顶元素 和 当前堆区间最后一个元素。

缩小堆的范围(末尾元素已经排好,不再参与堆调整)。

对新堆顶执行向下调整,恢复堆结构,重复直到有序。

最核心的一步:堆顶元素不是直接输出拿走,而是被交换到数组尾部。这就是口诀的根源!
升序如果错误使用小根堆,会发生什么?

如果你升序强行建小根堆。

小根堆堆顶是当前最小值。

第一次交换:最小值放到数组末尾。

演示图如下:

代码如下:

升序 堆排序

cpp 复制代码
#include<iostream>
using namespace std;


void AdjustUp(int* a, int child)
{
	int parent = (child - 1) / 2;
	while (child > 0)
	{
		if (a[parent] < a[child])
		{
			swap(a[parent], a[child]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}
}

void AdjustDown(int* a, int n, int parent)
{
	int child = parent * 2 + 1;
	while (child < n)
	{
		if (a[child] < a[child + 1] && child + 1 < n)++child;
		if (a[child] > a[parent])
		{
			swap(a[parent], a[child]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}



}

//升序的堆排序
void HeapSort(int* a, int n)
{
	for (int i = 1; i < n; i++)
		AdjustUp(a, i);

	int end = n - 1;
	while (end > 0)
	{
		swap(a[0], a[end]);
		AdjustDown(a, end, 0);
		--end;
	}

}




int main()
{
	int a[] = { 10,9,8,7,6,5,4,3,2,1 };


	HeapSort(a, 10);
	/*SelectionSort(a, 10);*/

	//InsertionSort(a, 3);
	//MergeSort(a, 10);
	/*ShellSort(a, 10);*/

	/*MergeSort(a, 10);*/
	/*QuickSortNonR(a, 0,9);*/
	for (auto e : a)
	{
		cout << e << " ";
	}
	return 0;
}

练习

自己完成降序的堆排序

排序的时间复杂度与空间复杂度

结语:

希望可以帮助到你,谢谢观看!!!

相关推荐
间歇性努力持续性发呆的野生快乐选手1 小时前
二分模板(整型,浮点型)
数据结构·c++·算法
evans在进步1 小时前
LeetCode 189 轮转数组:三次反转为什么能实现右旋?
数据结构·算法·leetcode
五_谷_丰_登3 小时前
平衡二叉搜索树讲解
数据结构·c++
星星.7223 小时前
【图论】最小生成树|Prim+Kruskal算法
数据结构·c++·算法·图论
_Narcissus_3 小时前
单调栈笔记及例题详解
数据结构·c++·笔记·算法·力扣·单调栈·洛谷
额,不知道写啥。4 小时前
从区间加一次函数到区间加多次函数最值-----区间数据结构与函数的一些东西(《浅谈函数最值的动态维护》的学习笔记)
数据结构·笔记·学习
LuminousCPP4 小时前
数据结构-二叉树(五):查找、销毁与前序序列建树
c语言·数据结构·笔记·二叉树
船厂电气自动化ai大模型15 小时前
AI大模型与数学·第57课 傅里叶全套工具链综合实战:串联级数/连续变换/DFT/FFT,图像、音频、扩散模型完整例题
数据结构·人工智能·深度学习·算法·机器学习