堆排序算法详解:从大根堆构建到完整排序实现
创作时间:
作者:
@小白创作中心
堆排序算法详解:从大根堆构建到完整排序实现
引用
CSDN
1.
https://m.blog.csdn.net/2401_87517331/article/details/143777846
堆排序是一种基于堆数据结构的排序算法,其中大根堆是最常用的一种。本文将详细介绍大根堆的概念、构建方法以及堆排序的具体实现。
大根堆的概念
首先,我们来引入大根堆的概念,以便更好地理解其工作原理。在堆中,每个父节点都有左右两个孩子节点,类似于高中生物的遗传图谱。不同的是,在堆中,每个父节点只能生两个孩子。
那么,什么是大根堆呢?大根堆的特点是:最上面的元素最大,即对于任何一个父节点来说,父节点都要比它的左右孩子大。只有满足这个条件的堆,才被称为大根堆。
1.1 堆排序中父节点和左右孩子的下标
堆是如何从数组中构建的呢?我们可以通过下图来理解:
从图中可以看出,堆的最上方是数组的下标0。我们可以将堆暂时看作一个二维数组。第0行是0,第1行下标是1和2,第2行下标是3、4、5和6。按照这种描述将堆排成一维数组后,就得到了下方的数组。
1.2 父节点和左右孩子的位置关系
父节点和左右孩子的位置关系如下:
- 假设父节点在一维数组中的下标是i,那么左孩子的下标就是2*i+1。
- 右孩子和左孩子相邻,且比左孩子大一位,因此右孩子的下标是2*i+2。
1.3 局部大根堆的构建思路
了解了大根堆的概念后,我们尝试构建一个大根堆。这里有两种思路:
- 从下标0开始构建大根堆
- 从最后一个父节点开始构造大根堆
第一种方法会让问题变得越来越复杂,因为随着层数的递进,需要处理的父节点数会呈指数级上升。因此,我们选择从最后一个父节点开始构建。
1.4 构建局部大根堆
首先,我们需要知道最后一个父节点的位置。最后一个父节点的下标index是arr.size()/2-1。
构建局部大根堆的代码如下:
void make_big(vector<int>& arr, int start, int end)
{
int now = start; // 传入一个父节点。
int l = 2 * now + 1; // 找到左孩子
for (; l <= end; now = l, l = 2 * now + 1)
{
if (l < end && arr[l] < arr[l + 1]) l++; // 如果这个节点有右孩子并且右孩子大的话
if (arr[now] < arr[l])
{
swap(arr[now], arr[l]);
}
// 如果父亲小于孩子就交换 // 注意孩子也可能是一个父亲,所以对孩子也要判断一下。
else { break; } // 局部大根堆构建完成
}
}
这段代码中:
start
参数用于指定需要构造哪个位置的局部大根堆。end
参数用于避免下标越界。- 代码通过循环找到最大孩子,并与父节点进行比较和交换。
1.5 局部大根堆代码思路讲解
代码的具体思路如下:
- 首先确定需要构造哪个位置的局部大根堆(父节点下标start)以及数组中一共有多少数(end)。
- 找到两个孩子中较大的那个。
- 如果父节点比孩子小,则进行交换,并继续检查孩子节点的大根堆是否成立。
2. 大根堆的实现和堆的排序思维
大根堆的完整实现代码如下:
int x = arr.size() / 2 - 1; // 找到最后一个父节点。
for (int i = x; i >= 0; i--)
{
make_big(arr, i, arr.size() - 1);
}
int end = arr.size() - 1;
// 构造大根堆。
// 之后进行交换。
for (int i = 0; i < arr.size() - 1; i++)
{
swap(arr[0], arr[end]);
end--;
make_big(arr, 0, end);
}
堆排序的核心思想是:
- 首先构建一个大根堆,使得堆顶元素是当前堆中的最大值。
- 将堆顶元素(最大值)与堆的最后一个元素交换,然后调整堆,使得剩余元素重新构成大根堆。
- 重复上述过程,直到整个数组有序。
完整代码如下:
#include<iostream>
#include<vector>
using namespace std;
void make_big(vector<int>& arr, int start, int end)
{
int now = start; // 传入一个父节点。
int l = 2 * now + 1; // 找到左孩子
for (; l <= end; now = l, l = 2 * now + 1)
{
if (l < end && arr[l] < arr[l + 1]) l++; // 如果这个节点有右孩子并且右孩子大的话
if (arr[now] < arr[l])
{
swap(arr[now], arr[l]);
}
// 如果父亲小于孩子就交换 // 注意孩子也可能是一个父亲,所以对孩子也要判断一下。
else { break; } // 局部大根堆构建完成
}
}
void foster(vector<int>& arr)
{
int x = arr.size() / 2 - 1; // 找到最后一个父节点。
for (int i = x; i >= 0; i--)
{
make_big(arr, i, arr.size() - 1);
}
int end = arr.size() - 1;
// 构造大根堆。
// 之后进行交换。
for (int i = 0; i < arr.size() - 1; i++)
{
swap(arr[0], arr[end]);
end--;
make_big(arr, 0, end);
}
for (int i = 0; i < arr.size(); i++)
{
cout << arr[i] << ' ';
}
}
int main()
{
vector<int> arr = { 1, 4, 3, 7, 9, 8, 5 }; // 一共有7个数字。
foster(arr);
}
通过这个案例代码,读者可以直观地看到堆排序算法的实现过程。如果对文章内容有任何疑问,欢迎在评论区留言讨论。
热门推荐
金果饮说明书主要包含哪些成分及功效是什么
和平经济学:探索经济稳定、冲突解决和全球繁荣之间的相互作用
文竹的花语是什么?文竹有哪些寓意?
南水北调全面通水十周年:累计调水超767亿立方米,惠及45座城市1.85亿人
对牙齿最重要的维生素和矿物质:10 种关键维生素和矿物质
揭秘便利店盈利模式:高毛利、全天候服务与多元化经营
电脑怎么开热点?台式电脑设置WiFi热点的方法详解
火灾逃生自救指南:不同场景下的应对措施
铺陈和铺排是WHAT概念
酒窖空调对酒窖恒温的重要性
培训儿童篮球的全攻略
【妇幼保健】关注婴幼儿成长中的大问题之“钙缺乏”篇
小儿布洛芬混悬液过量的危害
电脑投屏-电脑投屏的快捷键及其使用说明
“波音请回答,什么是安全?”
"老头乐"转正之争:民生需求与道路安全的两难选择
防水防腐保温工程:概念、工艺、材料及市场趋势
路由器软硬件大不同:软路由玩法多但上手难,硬路由才是家庭首选
维生素B2能长期吃么
如何快速掌握测试流程图怎么画?详细步骤与技巧解析
五条巷子的“状元”都来了!
经食道超声心动图:发现左心耳血栓的重要检查方法
美国通胀隐忧已转化为现实?
冬季游览黄果树瀑布:适宜性分析与旅游贴士
如何正确寻觅专业律师:你需要知道的那些事儿
房产赠与全攻略:法律流程与注意事项详解
微分方程的实际案例
补牙需要打麻药吗
四合院屋顶设计全解析,藏在古韵里的建筑智慧
重庆石柱:引智创收,种出乡村振兴新“橘”面