堆是一类特殊的数据结构的统称。堆在逻辑结构上可以看作为树,但是物理结构是通过顺序表来实现的。堆总是满足下列性质:
堆中某个节点的值总是不大于或不小于其父节点的值;
堆总是一棵完全二叉树。
将根节点最大的堆叫做最大堆或大顶堆,根节点最小的堆叫做最小堆或小顶堆。
堆是非线性数据结构,相当于一维数组,但有两个直接后继。
堆的定义如下:n个元素的序列{k1,k2,ki,…,kn}当且仅当满足下关系时,称之为堆。
(ki <= k2i,ki <= k2i+1)或者(ki >= k2i,ki >= k2i+1), (i = 1,2,3,4...n/2)
堆排序算法:按照堆的定义建成小(大)顶堆,输出堆顶的最小(大)数,再对剩余元素重新建堆,再输出,再建堆......
(1)初始时如何建堆?(2)如何调整剩余元素使成为一个堆?
问题的关键在于调整,初始建堆也可以看做是调整所有元素成为一个堆。
堆总是一个完全二叉树,而树的叶子节点自身便是一个堆,不需要调整;只有有孩子的节点才会不符合堆的要求。本文堆排序算法的数组从下标1开始,假设数组有n个数,下标自1至n,则最后一个有孩子的节点的下标是n/2(向下取整)。从最后一个有孩子的节点往前进行查找,n/2-1,n/2-2,.......,1。
比如小顶堆 13 36 24 85 47 30 53 91共8个数,8/2=4,从第四个数85开始,将它自身作为根节点来调整堆,再对第三个数24开始调整堆...直到第一个数,这样这8个数就构成了一个堆。小顶堆建好之后,第一个数便是最小的数,将它与第八个数相交换,再对前7个数调整建堆,建好之后将此时第一个数与第七个数相交换,再对前6个数建堆......
本算法通过建立小顶堆来进行排序,这样得到的输出序列为降序序列。
堆排序算法的时间复杂度为o(nlogn),空间复杂度为0(1)。它是一种不稳定的排序方法。
heapAdjust函数用来调整数组使成为一个堆,如下:该数列除了arr[s]外其余数均满足堆定义,它对数列arr{s...m]进行处理,使得该数列均满足堆定义。
void heapAdjust(int* arr, int s, int m)
{
//数组arr[s...m]中除了arr[s]外其余元素均符合堆的定义,现调整arr[s]使得
//arr[s...m]均满足堆定义
int i;
int rc = arr[s];
for (i = 2 * s; i <= m; i *= 2)
{
if (i < m&&arr[i] > arr[i + 1])
++i;
if (rc<arr[i])
break;
arr[s] = arr[i];
s = i;
}
arr[s] = rc;
}
完整代码如下:
#include<iostream>
#define N 20
void heapSort(int* arr, int num);
void heapAdjust(int* arr, int s, int m);
int main()
{
int a[N+1] = {-0, 3, 2, 4, 6, 7, 5, 18, 9, 0, 1,
16, 8, 20, 33, 28, 64, 19, 31, 30, 25 };
//处理下标1到20之间的数。
for (int i = 1; i <=N; i++)
{
std::cout << a[i] << " ";
}
std::cout << '\n';
heapSort(a,N);
for (int i = 1; i <=N; i++)
{
std::cout << a[i] << " ";
}
std::cout << '\n';
return 0;
}
void heapSort(int* arr, int num)
{
//下面注释的递归处理也是可以的,但是效率不高
/*if (num == 1)
return;*/
int i, temp;
for (i = num / 2; i > 0; i--)
heapAdjust(arr, i, num);
for (i = num; i>1; i--)
{
temp = arr[i];
arr[i] = arr[1];
arr[1] = temp;
heapAdjust(arr, 1, i-1);
}
/*temp = arr[1];
arr[1] = arr[num];
arr[num] = temp;
heapSort(arr, --num);*/
}
void heapAdjust(int* arr, int s, int m)
{
//数组arr[s...m]中除了arr[s]外其余元素均符合堆的定义,现调整arr[s]使得
//arr[s...m]均满足堆定义
int i;
int rc = arr[s];
for (i = 2 * s; i <= m; i *= 2)
{
if (i < m&&arr[i] > arr[i + 1])
++i;
if (rc<arr[i])
break;
arr[s] = arr[i];
s = i;
}
arr[s] = rc;
}