赞
踩
【排序算法】—— 堆排序
堆(heap):一个完全二叉树,每个节点都比子节点的值大或小
大根堆:每个节点的值都比其子节点大
小根堆:每个节点的值都比其子节点小
小根堆向上调整
代码实现:
typedef int HPDataType; //HPDataType是堆存储的数据的类型,这里是int型
void AdjustUp(HPDataType* data, int child) { int parent = 0; while (child > 0) { parent = (child - 1) / 2; if (data[child] < data[parent]) { Swap(&data[child], &data[parent]); child = parent; } else { break; } } }
大根堆向上调整
if (data[child] > data[parent])
...
小根堆向下调整
代码实现:
void AdjustDown(HPDataType* data, int size, int parent) { int minchild = parent * 2 + 1; while (minchild < size) { //比较左右子节点 if (minchild + 1 < size && data[minchild] > data[minchild + 1]) { minchild++; } if (data[minchild] < data[parent]) { Swap(&data[minchild], &data[parent]); parent = minchild; minchild = parent * 2 + 1; } else { break; } } }
大根堆向下调整
if (maxchild + 1 < size && data[maxchild] < data[maxchild + 1])
...
if (data[maxchild] > data[parent])
...
堆排序的步骤可以分为2个部分
将数组构建成堆时,可以使用向上调整或向下调整
向上调整
将数组第1个元素作为一个堆,从第2个元素到第n-1个元素依次进行向上调整。
int i = 0;
for (i=1; i<n; i++)
{
AdjustUp(arr, i); //从1到n-1进行调整
}
每一次向上调整的时间复杂度为 O ( l o g 2 n ) O(log_2n) O(log2n)
所以使用向上调整建好堆的时间复杂度为 O ( n l o g 2 n ) O(nlog_2n) O(nlog2n)
向下调整
int i = 0;
for (i=(n-2)/2; i>=0; i--)
{
AdjustDown(arr, n, i); //从 (n-2)/2 到 0 进行调整
}
时间复杂度计算
则需要移动节点的步数为
T n = 2 0 × ( h − 1 ) + 2 1 × ( h − 2 ) + 2 2 × ( h − 3 ) + . . . + 2 h − 2 × 1 T_n=2^0\times(h-1)+2^1\times(h-2)+2^2\times(h-3)+...+2^{h-2}\times1 Tn=20×(h−1)+21×(h−2)+22×(h−3)+...+2h−2×1
这是一个等差数列乘以一个等比数列,使用裂项相消法进行化简
2 T n = 2 1 × ( h − 1 ) + 2 2 × ( h − 2 ) + 2 3 × ( h − 3 ) + . . . + 2 h − 1 × 1 2T_n=2^1\times(h-1)+2^2\times(h-2)+2^3\times(h-3)+...+2^{h-1}\times1 2Tn=21×(h−1)+22×(h−2)+23×(h−3)+...+2h−1×1
2 T n − T n = − h + 2 0 + 2 1 + 2 2 + . . . + 2 h − 1 + 2 h − 2 × 1 2T_n-T_n=-h+2^0+2^1+2^2+...+2^{h-1}+2^{h-2}\times1 2Tn−Tn=−h+20+21+22+...+2h−1+2h−2×1
T n = − h + 2 h − 1 T_n=-h+2^h-1 Tn=−h+2h−1
因为 n = 2 h − 1 n = 2^h-1 n=2h−1, h = l o g 2 ( n + 1 ) h=log_2{(n+1)} h=log2(n+1)
T n = n − l o g 2 ( n + 1 ) ≈ n T_n=n-log_2(n+1)\approx n Tn=n−log2(n+1)≈n
向下调整的时间复杂度为 O ( n ) O(n) O(n)
因此,堆排序都是使用向下调整的方式
进行选数时,我们通过大根堆选出最大的数,利用小根堆选出最小的数
升序排序
利用小根堆排序
利用大根堆排序
所以升序排序是使用大根堆选出最大数放到最后一个位置的方式进行排序
for (i=n-1; i>0; i--) //从最后一个位置开始交换并调整
{
Swap(&arr[0], &arr[i]);
AdjustDown(arr, i-1, 0); //此处为大根堆向下调整方式
}
降序排序
for (i=n-1; i>0; i--)
{
Swap(&arr[0], &arr[i]);
AdjustDown(arr, i-1, 0); //此处为小根堆向下调整方式
}
以下为整型数组升序排序的实现
typedef int HPDataType; //堆存储的数据的类型 //交换数据 void Swap(int* a, int* b) { int temp = *a; *a = *b; *b = temp; } //向下调整(大根堆) void AdjustDown(HPDataType* data, int size, int parent) { int maxchild = parent * 2 + 1; while (maxchild < size) { //比较左右子节点 if (maxchild + 1 < size && data[maxchild] < data[maxchild + 1]) { maxchild++; } if (data[maxchild] > data[parent]) { Swap(&data[maxchild], &data[parent]); parent = maxchild; maxchild = parent * 2 + 1; } else { break; } } } //堆排序 void HeapSort(int arr, int n) { int i = 0; //建堆 for (i=(n-2)/2; i>=0; i--) { AdjustDown(arr, n, i); } //选数 for (i=n-1; i>0; i--) { Swap(&arr[0], arr[i]); AdjustDwon(arr, i-1, 0); } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。