堆排序:
堆的标准定义如下:
n个元素的序列{k1, k2, …, kn}当且仅当满足以下关系时,称为堆。
(Ki<=K2i&&Ki<=K2i+1)或(Ki>=K2i&&Ki>=K2i+1)
(i = 1, 2, ..., n/2)
可以简单理解为就是一个完全二叉树,但是比较特殊,即完全二叉树中所有非终端结点的值均不大于(或不小于)其左、右孩子结点的值。
示例:
小顶堆:意思就是堆顶的值是最小的
大顶堆:意思就是堆顶的值是最大的
知道了堆的性质,我们自然可以推出怎么排序,首先需要把输入的数组构建成一个小顶堆/大顶堆,只需要每次把堆顶和无序的最后一个元素交换,然后更新堆,再交换,再更新。。。。就可以达到有序了。
具体步骤:
1. 先将输入的数据构建成小顶堆/大顶堆
2. 将堆顶a[1]和最后一个元素a[i]交换,i–
3. 更新堆,使其重新成为小顶堆/大顶堆
4. 一直重复第2步和第3步,直到i==1为止。
代码如下:
#include <iostream>
using namespace std;
#include <cstdio>
int min(int x, int y)
{
if(x < y)
return 0;
return 1;
}
void HeapAdjust(int a[], int s, int m) //调整堆,使其有序
{
int temp = a[s];
for(int j = 2*s; j <= m; j *= 2)
{
if(j < m && min(a[j+1], a[j]))
{
j++;
}
if(!min(a[j], temp)) break;
a[s] = a[j];
s = j;
}
a[s] = temp;
}
void HeapSort(int a[], int len)
{
for(int i = len/2; i > 0; --i) //先将输入的乱序数组构建成堆,对应第1步
{
HeapAdjust(a, i, len);
}
for(int i = len; i > 1; --i) //交换a[1]和a[i]的值,更新堆。对应第2,3步
{
int temp = a[1];
a[1] = a[i];
a[i] = temp;
HeapAdjust(a, 1, i-1);
}
}
int main()
{
int a[11] = {0};
for(int i = 1; i <= 10; i++)
{
scanf("%d", &a[i]);
}
HeapSort(a, 10);
for(int i = 1; i <= 10; i++)
{
printf("%d\n", a[i]);
}
return 0;
}
以该图为例:
第一步我们输入{13,38,27,49,76,65},希望输出升序排列,即{13,27,38,49,65,76}
第二步先构建成大顶堆。HeapAdjust(a, 3, 6)
,比较27和65的大小,27<65,则两者互换。HeapAdjust(a, 2, 6)
,比较49和76的大小,再将其中的较大者和38比较,于是76和38互换。HeapAdjust(a, 1, 6)
,比较76和65的大小,再将其中较大者和13比较,于是76和13互换,此时的13比它的子结点小,于是又进行了一次互换,将49和38的较大者和13互换了。此时就得到了一个大顶堆。
第三步将76和27互换,HeapAdjust(a, 1, 6-1)
,将65和49做比较,取较大者和a[1]的27交换,完成了第一次更新。将65和38互换位置,HeapAdjust(a, 1, 5-1)
,由于38比它的子结点小,于是又要做交换,将49和38做交换,完成了第二次更新。将49和13互换位置,HeapAdjust(a, 1, 4-1)
,38和27做比较,将较大者和13互换位置,即38和13互换,完成了第三次更新。将38和27互换位置,此时27就是最大元素了,完成了第四次更新。将27和13交换,此时只剩下13最后一个元素,完成了第五次更新,达到有序。
最后还有一点,堆排序适用于大文件,而不适用于小数据。