前言
计算机科学家,
图灵奖得主,
前后断言法的创始人,
堆排序算法和Floyd-Warshall算法的创始人之一,
同时,他还是
有芝加哥大学文学士学位(有点抽象)
登场!!!
这位大佬自学成才(自学计算机)
基本思想
建立一个大堆(升序建大堆)
将堆顶元素与最后一个元素交换
交换后堆元素减一,重新调整堆
图1是我们准备的大堆,物理结构为
此时我们首尾交换
最大的数字就在数组的最后,他的位置就不用动了,
然后把最后一个数字剔除,将剩余的数字重新组成大堆,
以此类推,直至数组变为有序。
如何将一个数组变为大堆
随意列出一个堆
第一步
序号5向下调整,
使5,9序号所在分支变为大堆
这里本身就是大堆所以不动
第二步
序号4向下调整,
使4,7,8序号所在分支变为大堆
这里本身就是大堆所以不动
第三步
序号2向下调整,
使2,4,5,7,8,9序号所在分支变为大堆
第四步
序号3向下调整,
使3,6序号所在分支变为大堆
这里本身就是大堆所以不动
最后一步
从序号1向下调整,
将整个数组变为大堆
代码实现
1.将数组建立为大堆
//找最后一个节点的父节点,一个数的父节点是(n-1)/2,最后一个节点是n-1
for (int i = (n - 2) / 2; i >= 0 ; i--)
{
Adjustdown(f, n, i);
}
2.向下调整函数
void Adjustdown(vector<int>& f,int n,int parent)
{
//假设左孩子小
int child = parent*2 + 1;while (child<n)//确保孩子存在
{ //找出大孩子
if (child + 1<n && f[child] < f[child + 1])
{
child++;
}
if (f[child] > f[parent])
{
swap(f[child], f[parent]);
parent = child;
child = parent * 2 + 1;
}
else
break;
}
}
3.依次选数,调堆
while (end>0)
{
//选数
swap(f[0], f[end]);
//调堆
Adjustdown(f, end, 0);
end--;
}
完整代码
void Adjustdown(vector<int>& f,int n,int parent)
{
//假设左孩子小
int child = parent*2 + 1;while (child<n)//确保孩子存在
{ //找出大孩子
if (child + 1<n && f[child] < f[child + 1])
{
child++;
}
if (f[child] > f[parent])
{
swap(f[child], f[parent]);
parent = child;
child = parent * 2 + 1;
}
else
break;
}
}void HeapSort(vector<int>& f,int n)
{
//找最后一个节点的父节点,一个数的父节点是(n-1)/2,最后一个节点是n-1
for (int i = (n - 2) / 2; i >= 0 ; i--)
{
Adjustdown(f, n, i);
}int end = n - 1;
while (end>0)
{
//选数
swap(f[0], f[end]);
//调堆
Adjustdown(f, end, 0);
end--;
}
}
从升序到降序的思考
仅仅需要将讲个小于大于颠倒一下即可,
逻辑很简单,大家思考一下。
但是不推荐
拿我们上面举例的数组说明:
从序号开始,剩下的数看做一个堆但是在这之前建立好的堆关系全部乱了
需要重新建堆才能选出次小数!
总结
建立小堆排升序是可以的
但是效率很低没有体现优势