排序-堆排序(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;
}

练习

自己完成降序的堆排序

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

结语:

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

相关推荐
Ada's14 小时前
【计算机基础系列】003:Python数据结构
开发语言·数据结构·python
wanderist.18 小时前
线性筛法详解:从筛质数到欧拉函数、Möbius 函数与约数函数
java·数据结构·算法
All for pursuit.18 小时前
【设计-1】208.实现Trie (前缀树)
数据结构·c++·算法·leetcode
程序员清风19 小时前
哈希表:从键值映射到高效查找
数据结构·哈希算法·散列表
Logic1011 天前
C语言/数据结构字符串题解:最短循环节——找出能重复构成原串的最短基础子串
c语言·数据结构·字符串·枚举·时间复杂度·算法题·kmp优化
垆边人似月.1 天前
城市连通性(200分 / 并查集)
数据结构·算法·图论
Gust of wind1 天前
串与KMP模式匹配:存储结构、基本操作、next数组手算
c语言·数据结构·后端·算法
by209991 天前
list:迭代器与资源管理实现的综合叙述(下)
数据结构·c++·笔记·list
我不会起名字3221 天前
二叉树的遍历与二叉搜索树:把递归、迭代、还原树一次讲透
数据结构·leetcode·二叉树·二叉搜索树
yi0111 天前
DAY22: LeetCode 733:图像渲染——从二维网格开始理解 DFS 和 BFS
数据结构·笔记·python·算法·leetcode·深度优先·宽度优先