堆排序
链接: 原文.
一,基础知识(记忆)
完全二叉树基础知识
若树的根节点记为 0
i处节点的left_child位置为:2 * i +1
i处节点的left_child位置为:2 * i +2
i处节点的parent位置为:( i - 1 )/ 2
n个元素的堆的最后一个父亲结点的位置n / 2-1
完全二叉树添加新节点的顺序是:从上到下,从左到右
堆定义
堆是一棵顺序存储的完全二叉树。
大根堆:
任意父节点大于其子节点的完全二叉树。
小根堆:
任意父节点小于其子节点的完全二叉树。
构造堆
(以大根堆为例)
过程:
- 从最后一个非叶子节点开始,逐一比较非叶子节点和其左右孩子节点
- 根据比较结果交换节点
- 因为交换可能导致孩子节点不再满足大顶堆的性质,所以对孩子节点进行调整。
heapify:堆调整
作用:
将一个有三个结点的完全二叉树调整成堆;并维护自动维护子树的堆关系。
过程:
1,针对父结点 i,找到其两个子节点。(越界的忽略)
2,找到三个结点的最大值,并将其交换至父节点的位置
3,递归调用,维护交换后子节点与其子结点被破坏的堆关系,
4.递归出口:叶子节点
(不是根结点)
// 交换结点位置 void swap(int a[], int i, int j) { int t = a[i]; a[i] = a[j]; a[j] = t; } // 堆调整 void heapify(int tree[], int n, int i) { if (i >= n) return;//递归出口 int c1 = 2 * i + 1; int c2 = 2 * i + 2; int max = i; if (c1 < n&&tree[c1] > tree[max]) max = c1; if (c2 < n&&tree[c2] > tree[max]) max = c2; if (max != i) { swap(tree, max, i);// 将最大值移至父节点 heapify(tree, n, max);// 递归维护下面的结点 } }
build_heap:构造堆
目的:构造大根堆。
过程:
由于 heapify 会递归调用维护下面的堆,
所以想要构造完整的堆,只需要从下面往上构造:
先找到最后一个父结点,再从最后一个父结点开始向上调用 heapify。
// 构造堆 void build_heap(int tree[], int n) { int parent =n / 2 - 1;// 最后一个父结点 int i; for ( i = parent; i >= 0; i--) // 从最后一个父结点开始向上调用 heapify,一直调用到第一个父节点 heapify(tree, n, i); }
heap_sort:堆排序
算法思路
由堆可以得到什么呢 ? 首先根结点一定是最大结点
于是,利用这点就可以排序了
可以想到:
既然根结点已经是最大的,那么就把这个数提走,剩下的再构造一个新堆。然后再提走根结点,再构造新的堆,循环往复。
2,但是这样就会面临一个问题?拿走根结点之后,根结点的数值由谁取代?
很自然我们希望取代的数:
- 对堆的结构破坏尽可能小。
显然最后一个结点是最好的选择了。
因为:
- 移动后既不会影响到后面的结点,
- 原来的位置还因为空了可以直接取消掉。
(可以用来储存树的根节点,即:最大值)
将根结点存到堆的最后一个结点处。
由于堆的大小越来越小,故根节点会从最后面的位置依次往前放,可以形成升序序列。
所有算法步骤为:
算法步骤
- 构造一个大根堆
- 每次将根节点与最后一个节点交换,每次交换后,将最后一个节点踢出堆的范围。
- 用 heapify 函数维护堆
- 重复多次,直到堆的大小为1
为什么用 heapify 函数维护堆?
因为:
-
- heapify函数会自动维护子树的堆性质。
-
- 没有必要用 buildHeap函数:因为此时除了根节点所在的最小单位的堆出了问题,其它所有最小单位的堆都是正常的。
代码
// 利用堆进行堆排序 //从小到大排序 void heap_sort(int tree[], int n) { build_heap(tree, n); int i; for (i = n - 1; i >= 1; i--)//i>=1即可,倒数第二个是一定比倒数第一个大 // 每次取出根结点后,堆减少一个节点 { swap(tree, i, 0); //交换根节点和最后一个节点 heapify(tree, i, 0); // (假砍断)对从0到i的元素构造一个新堆,(i是指新堆节点数) } }
主函数
int main(void) { int i; int n; int m; scanf("%d %d",&n,&m); int tree[n+1] ; for(i=0; i<n; i++) { scanf("%d",&tree[i]); } heap_sort(tree, n); for ( i = n-1; i >=n-m+1; i--) { printf("%d ", tree[i]); } printf("%d\n", tree[i]); return 0; }
本文作者:kingwzun
本文链接:https://www.cnblogs.com/kingwz/p/15676390.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步