基本思想
是对简单选择排序的改进,是一种树形结构的排序。
利用堆的特性,快速选择出序列中的最大最小元素。
堆的定义:
用树表示更加直观:
即:父节点 不大于/不小于 其子节点的完全二叉树。
降序时称为 大堆顶,升序时称为 小堆顶。
这样的堆,其堆顶就是整个序列中最大/最小的元素。
不断的取出堆顶、将剩下序列重组成堆的过程就叫堆排序。
树采用顺序存储:{96,83,27,38,11,09}, {12,36,24,85,47,30,53,91}
序列是树的顺序存储,因此 节点、父、子 在序列中的位置为 i,2i,2i+1, 反映到序列中的 index 为 i - 1, 2*i-1, 2*i。
重点
1. 初始化时将整个序列转换成堆
2. 取出堆顶后,将剩下的元素快速组成堆
先说第二点的算法思路:
1. 取出堆顶后 i 后,将堆底元素取出补为堆顶
2. 检查到堆被破坏,将 堆顶 与其子节点中小的元素交换
3. 对交换后的子树重复2
示例图:
再说第一点的算法思路:
1. 假设 长度 n 的 序列 k 为堆
2. 最后一个节点是 节点 n/2 的子节点,那么从 n/2 开始向上筛选
3. 从 父子节点 中选举最小的为 父节点
4. 对交换后的子节点 重复 3,直到叶子节点
5. 检查下一个 n/2 - 1 ,重复 3, 直到 0
示例图:
代码:
void printList(int *l, int n) {
for (int i = 0; i < n; i++) {
printf("%d ", l[i]);
}
printf("\n");
}
/*
* 修复堆
*
* 从 父子 节点中选举最小的为父节点。
* 如果最小的不是父节点的话,那么交换过的子节点也需要检查。
*/
void adjustHeap(int *l, int n, int topIndex, int adjustIndex) {
// 要检查的元素 index
int minIndex = adjustIndex;
// 堆顶不为 0 时,计算 adjustIndex 的子节点 index 要考虑到偏移
int lcIndex = 2 * (adjustIndex - topIndex + 1) - 1 + topIndex;
int rcIndex = 2 * (adjustIndex - topIndex + 1) + topIndex;
// 跟 左子节点比较,替换 小的 index
if (lcIndex < n && l[minIndex] > l[lcIndex] ){
minIndex = lcIndex;
}
// 跟 右子节点比较,替换 小的 index
if (rcIndex < n && l[minIndex] > l[rcIndex]){
minIndex = rcIndex;
}
// 如果小的index 不是 堆顶的话,那么换顶,然后检查子堆
if ( minIndex != adjustIndex ){
int k = l[minIndex];
l[minIndex] = l[adjustIndex];
l[adjustIndex] = k;
// 子堆 minIndex 换了 堆顶,要检查一下
adjustHeap(l, n, topIndex, minIndex);
}
}
/*
* 创建堆
*
* 1、假设 长度 n 的 序列 k 为堆
* 2、最后一个节点为 n / 2 的子节点,从 n / 2 开始向上筛选
* 3、从 父子节点中选举最小的为 父节点
* 4、检查下一个 n / 2 - 1 ,重复 3, 直到 0
*/
void buildHeap(int *l, int n) {
for (int i = n / 2; i > 0;i-- ) {
// 节点 i 对应的 index 是 i - 1, 子节点 index 2 * i - 1、 2 * i
int min = i - 1;
int lc = 2 * i - 1;
int rc = 2 * i;
// 选举最小的为 堆顶
if (l[i - 1] > l[lc]){
min = lc;
}
if ( 2*i < n && l[min] > l[rc]){
min = rc;
}
if ( min != i-1 ) {
int k = l[i - 1];
l[i - 1] = l[min];
l[min] = k;
// 子堆 min 换了堆顶,要检查堆 min
adjustHeap(l, n, 0, min);
}
}
}
int main() {
int list[50] = { 10, 7, 1 , 8, 5, 12, 6, 3, 9 };
int n = 9;
printList(list, n);
// 创建小堆顶
buildHeap(list, n);
printList(list, n);
printf("======\n");
// 建好堆后,初始堆顶就是最小的
for (int i = n - 1; i > 0; i--) {
//将堆顶换到堆底. 这是小堆顶,每次都将最小的元素换到堆底,然后 堆缩小 1,最后得出降序
int exchange = list[0];
list[0] = list[i];
list[i] = exchange;
// 堆 0 被破坏,执行检查
adjustHeap(list, i, 0, 0);
printList(list, n);
}
system("pause");
return 0;
}
以上
原文链接 http://blog.csdn.net/u011546766/article/details/74045324