1.堆排序定义
堆排序是一种树形选择排序方法,特点是在排序的时候,可以将拥有n个关键字的数组或其他结构看做一个完全二叉树。
如图:
堆的定义:n个关键字序列L(1...n)称为堆,当满足:
- L(i) <= L(2i) 且 L(i) <= L(2i+1) ( 1 <= i <= [n/2] ) 也就是说小于左右孩子 ,称之为小顶堆
- L(i) >= L(2i) 且 L(i) >= L(2i+1) ( 1 <= i <= [n/2] ) 也就是说大于左右孩子 ,称之为大顶堆
2.堆的性质
因为堆是一种基于完全二叉树的结构,所以具有完全二叉树的性质,如n个节点的完全二叉树:
- 左孩子为2n
- 右孩子为2n+1
- 父节点为[ n/2 ]
- 在顺序存储的完全二叉树中,非终端节点的编号i<=[n/2]
3.堆排序算法(以大根堆为例)
第一步:先建立一个大根堆
假设从n/2开始筛选,此后每次减1,如图从819开始,筛选两个左右孩子最大的一个与之交换,交换完毕后检查交换后关键字是否满足大根堆的性质,不满足,继续重复操作,直到建立好一个大根堆
第二步:交换堆顶与堆底元素
第三步:除去堆底的元素剩余元素重复第一步的操作,且从堆顶元素开始
如图:
堆顶948与堆底435交换,除去948剩下重复第一步操作
4.代码实现
以带哨兵为例(带哨兵指第一个元素用来存储关键字),以下代码有解释
//基于大根堆(带哨兵)排序,
void headSort(int arr[],int length) {
bulidMaxHeap(arr, length); //首次建大根堆
for (int i = length; i > 1; i--) { //n-1趟交换和建堆
swap(arr, 1, i); //将堆顶与堆底元素交换
adjustDown(arr, 1, i-1); //剩余元素继续调整
}
}
void swap(int arr[], int i, int j) {
int temp;
temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
void bulidMaxHeap(int arr[], int length) {
for (int i = length / 2; i >0; i--) { //从最大的 i<length/2 开始一次往前调整
adjustDown(arr, i, length);
}
}
void adjustDown(int arr[], int key, int length) {
arr[0] = arr[key]; //arr[0]做哨兵 ,暂时存储关键字元素防止被覆盖
int i;
for ( i = 2 * key; i <=length; i=i*2) //从左孩子开始扫描
{
if (i < length && arr[i] < arr[i + 1]) { //取左右孩子的最大值,( i<length 防止左孩子没有右兄弟 )
++i;
}
if (arr[0] >= arr[i]) { //判断关键字是否大于左右孩子中最大的那一个
break;
}
else {
arr[key] = arr[i]; //将左右孩子最大的赋值给关键字
key = i; //调整指针位置,接下来继续循环,判断调整后的元素是否符合堆
}
}
arr[key] = arr[0]; //将关键字放在最终位置
}
int main()
{
int arr[] = { 0,11,10,2,9,8,7,1,4,5,6 };
int length = sizeof(arr) / sizeof(arr[0]);
headSort(arr, length-1);
for (int i = 1; i < length; i++)
{
printf("%d\n", arr[i]);
}
}
排序后:
水平有限,如有不正,请指点