Description
用函数实现堆排序,并输出每趟排序的结果输入格式
第一行:键盘输入待排序关键的个数n 第二行:输入n个待排序关键字,用空格分隔数据输出格式
第一行:初始建堆后的结果 其后各行输出交换堆顶元素并调整堆的结果,数据之间用一个空格分隔输入样例
10 5 4 8 0 9 3 2 6 7 1输出样例
9 7 8 6 4 3 2 5 0 1 8 7 3 6 4 1 2 5 0 9 7 6 3 5 4 1 2 0 8 9 6 5 3 0 4 1 2 7 8 9 5 4 3 0 2 1 6 7 8 9 4 2 3 0 1 5 6 7 8 9 3 2 1 0 4 5 6 7 8 9 2 0 1 3 4 5 6 7 8 9 1 0 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9
堆排序,相对于前面的算法写起来有点难,但是认真思考后其实逻辑也是很清晰的。遇到比较难的算法我是不太喜欢看书的,因为书上全是文字,CSDN或者知乎上一些大佬写的真的很详细且易懂。
一、堆的性质
1、是一棵完全二叉树
2、每个结点的值都大于或等于其子结点的值,我们称其为大根堆;反之,成为小根堆。
二、堆的存储
一般用数组来表示堆(PS.我这里的数组从下标1开始)
不难发现,下标为i的结点的左孩子结点对应下标为2i,右孩子结点对应下标为2i+1
下标为i的结点的父结点下标为i/2
三、堆的操作
1、大根堆调整——使子结点永远小于其父结点
2、创建最大堆——重新排序
3、堆排序——将堆顶(即最大值)和无序序列的最后一个元素调换位置(下面详细解释)
我们知道,大根堆的堆顶记录是数组中的最大值,这样一来,我们每次选择无序序列中的最大值就会变简单,大概步骤如下:
(1)构建大根堆
(2)将根结点和无序序列的最后一个元素a[n]交换位置
(3)再从根结点到a[n-1]再次构建大根堆
(4)以此类推,知道无序序列只剩下一个元素为止
看到这里,不知道你有没有思路了呢?
如果还不太懂,那么来看看下面的详细步骤吧
(1)首先,建初堆。即把a[1]…a[n]建成大根堆
(2)然后,把堆顶元素a[1]和无序区的最后一个数a[n]交换,得到了一个新的无序区a[1]…a[n-1],和新的有序区a[n],并且a[1]…a[n-1]均小于a[n]
(3)交换堆顶元素和无序区的最后一个数后,它可能就不是大根堆了。所以我们要重新建大根堆。再重复(2)中的步骤。把a[1]和a[n-1]交换,得到新的无序区a[1]…a[n-2],和新的有序区a[n-1]…a[n]……直到无序区只剩下一个数为止
图我就不画了,我不太会电脑画图哈哈哈哈,可别光看,一定要动手写写画画。随便举个例子都行的啦,比如{1,2,3,4,5,6,7},虽然它是一个有序序列,但是不影响你演算啦。
//代码如下
//我觉得还是很好理解的,有不懂的地方可以在评论区提出来啦,一起学习~
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
using namespace std;
int n,a[100005];
void print()
{
for (int i=1;i<=n;i++)
cout << a[i] << ' ';
cout << endl;
}
void HeapAdjust(int Start,int End)//将a[S...E]调整为以a[S]为根的大根堆
{
int dad=Start;
int son=dad*2;
while(son <= End)//子结点在范围内才能进行比较
{
if(son+1<=End && a[son]<a[son+1])
son++;//选择大的子结点
if(a[dad]>a[son])
return ;
else
{
swap(a[dad],a[son]);
dad=son;
son=dad*2;
}
}
}
void HeapSort()
{
for(int i=n/2;i>=1;i--)
{
HeapAdjust(i,n);//初建堆
}
print();
for(int i=n;i>1;i--)
{
swap(a[1],a[i]);
HeapAdjust(1,i-1);
print();//输出调整堆的结果
}
}
int main()
{
cin >> n;
for(int i=1;i<=n;i++)
cin >> a[i];
HeapSort();
return 0;
}
//这个知乎作者写的很详细很详细,要是没看懂我的就去看看它叭~