堆排序详解:大根堆的概念与实现
创作时间:
作者:
@小白创作中心
堆排序详解:大根堆的概念与实现
引用
CSDN
1.
https://m.blog.csdn.net/2401_87517331/article/details/143777846
堆排序是一种基于堆数据结构的排序算法,其中大根堆是最常用的一种实现方式。本文将详细介绍大根堆的概念、构建方法以及堆排序的实现过程。
大根堆的概念
首先,我们来引入大根堆的概念,以便更好地理解其工作原理。在堆中,每个节点都有一个父节点和最多两个子节点(左孩子和右孩子)。这种结构类似于高中生物中的遗传图谱,但不同的是,堆中每个父节点只能产生两个孩子。
那么,什么是大根堆呢?大根堆是一种特殊的堆结构,其特点是根节点的值最大,且对于堆中的任意父节点,其值都大于或等于其左右孩子的值。
1.1 堆排序中父节点和左右孩子的下标
堆实际上是从数组中构建而来的。通过观察堆的结构,我们可以发现:
- 第0行对应数组下标0
- 第1行对应数组下标1和2
- 第2行对应数组下标3、4、5和6
这种映射关系可以表示为:如果父节点在一维数组中的下标是i,那么其左孩子的下标为2i+1,右孩子的下标为2i+2。
1.2 局部大根堆的构建思路
在构建大根堆时,有两种主要思路:
- 从下标0开始构建大根堆
- 从最后一个父节点开始构建大根堆
从最后一个父节点开始构建大根堆更为高效,因为这样可以避免在构建过程中处理过多的父节点。
1.3 构建局部大根堆
要从最后一个父节点开始构建大根堆,首先需要确定最后一个父节点的位置。最后一个父节点的下标可以通过公式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; } // 局部大根堆构建完成
}
}
1.4 局部大根堆代码思路讲解
这段代码的主要思路是:
- 接受需要构造的父节点坐标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);
}
通过以上代码,可以实现对数组的堆排序。如果对文章内容有任何疑问,欢迎在评论区留言讨论。
热门推荐
如何选择适合的鼻烟壶材质?这种鼻烟壶材质的特点和价值如何体现?
重庆主城露营烧烤的好地方推荐(地点+路线)
中国硬科技企业出海:有效布局的关键与挑战
经典名方——桂枝甘草汤
隐患排查治理有哪些创新方法值得学习?
如何缓解饮食中的纤维产生的胀气
路由器怎样重新设置wifi密码
如何及时了解原油现价及其市场趋势?这种趋势对能源市场有何影响?
凌姓的起源和宗族发展史
越南的新年:旅行者的文化体验
5种有氧运动对膝盖损伤最小,看看你经常做的是不是对的
Deepseek 本地部署硬件要求详解
《明日方舟》300抽要攒多久 300抽要攒时间介绍
急性牙髓炎怎么快速止痛
电脑断网怎么办?多种实用解决方案帮你快速恢复网络连接
优化网络连接:网线插接技巧提升路由器效能
这个胃,究竟该不该切?
内存对游戏性能的深远影响,你了解多少?
如何通过创新方法优化企业的人才培养策略?
人类大脑独特性:进化的馈赠还是代价? 从细胞到发育:人类大脑独特性全揭秘
数字货币永续合约资金费率套利策略
SSM框架集详解:组成、结构、优势与应用案例
庄子的顶级人生观
品读八字箴言:至乐无乐,至誉无誉
期权涨停板幅度是如何设定的?这种设定会带来怎样的市场反应?
中央汇金之后,中国国新、中国诚通出手增持中国股票资产
如何优化室内空间的布局和设计?这样的优化有哪些具体要求?
周二 欧冠 阿森纳VS皇家马德里 比赛推荐
如何回复SCI审稿
SCI投稿后还可以修改吗