堆排序
堆排序Heapsort是指利用堆这种数据结构所设计的一种排序算法。堆积是一个近似完全二叉树的结构,并同时满足堆积的性质:
即子结点的键值或索引总是小于(或者大于)它的父节点。
首先要说一下什么是完全二叉树:
- 每个节点最多有两个子节点(左,右儿子)
- 若设二叉树的深度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第h层从左边第 h 层所有的结点都连续集中在最左边,这就是完全二叉树。
注意完全二叉树有一个性质:最后一个父结点是第n/2个结点。
然后我们回过头来说算法:
首先看一下堆排序的图片演示
算法步骤:
- 将初始待排序数组(R1,R2….Rn)构建成大顶堆,此堆为初始的无序区;
- 从最后一个非父节点开始,依次调整堆为大顶堆,若调整过程中,某次交换元素后与子节点不构成大顶堆,那么就进行这个堆的调整
- 将堆顶元素R[1]与最后一个元素R[n]交换,此时得到新的无序区(R1,R2,……Rn-1)和新的有序区(Rn),且满足R[1,2…n-1]<=R[n];
- 由于交换后新的堆顶R[1]可能违反堆的性质,因此需要对当前无序区(R1,R2,……Rn-1)调整为新堆,然后再次将R[1]与无序区最后一个元素交换,得到新的无序区(R1,R2….Rn-2)和新的有序区(Rn-1,Rn)。不断重复此过程直到有序区的元素个数为n-1,则整个排序过程完成。
文字说明可能会有点枯燥,接下来用图片讲解
找到最后一个父节点(n/2),将这个最小堆进行调整变为大顶堆结果如下图:
接着找倒数第二个父节点(往前1个),将其调整为大顶堆
经过调整,最后一个父节点所在行的所有父节点都变成大顶堆
继续往前(此时调整R[3])
发现本来就是一个大顶堆,不需要调整,继续往前,看R[2]
变为
然后看R1
变为
各个节点都满足大顶堆,就将最后一个元素与堆顶交换
交换后,发现堆顶不是大顶堆,然后接着往下调整
变为
此时下图这个堆也不满足大顶堆,继续往下调整。
变为
变为
重复此操作,将整个变换成有序堆。
代码如下:
#include <iostream>
#define N 1000
using namespace std;
int arr[N];
void Print(int n)
{
cout << arr[1];
for(int i=2;i<=n;i++)
{
cout << " " << arr[i];
}
cout << endl;
}
void adjust(int p, int n)
{
int t = 0, flag = 0;
while (p * 2 <= n && flag == 0)
{
if (arr[p] < arr[p * 2]) //先判断左儿子
t = p * 2;
else
t = p;
if (p * 2 + 1 <= n) //看是否存在右儿子
if (arr[t] < arr[p * 2 + 1])
t = p * 2 + 1;
if (t != p)//如果最大的不是自己 就进行交换
{
swap(arr[t], arr[p]);
p = t;//最大的改为 t 方便进行接下来的调整
}
else
flag = 1;
}
}
void HeapSort(int n)
{
for (int i = n / 2; i >= 1; i--)
{//从最后一个父节点开始调整
adjust(i, n);
}
while (n > 0)
{
swap(arr[1], arr[n]);//与最后一个交换
n--;
adjust(1, n);
}
}
int main()
{
int n;
cin >> n;
for (int i = 1; i <= n; ++i)
{
cin >> arr[i];
}
HeapSort(n);
Print(n);
}
感谢您的观看