排序算法 之 (直接插入排序)

10.6、堆排序

对于n个关键字序列L[1...n],满足下面某一条性质,则称为堆(Heap)

  • 若满足:L(2i)L(i)L(2i+1)L(i)1in大根堆(大顶堆)
  • 若满足:L(i)L(2i)L(i)L(2i+1)1in小根堆(小顶堆)

这里需要了解二叉树的顺序存储,对于n个结点的关键字来说

n2<i这里i就是叶子结点,

in2 这里i就是分支结点(非叶子结点);

分支结点的i,左孩子:2i,右孩子:2i+1

大根堆: 左孩子、右孩子 父节点

小根堆:父节点 左孩子、右孩子

堆排序是不稳定的

图解大根堆、小根堆

那么应该如何建立大根堆(小根堆)了;

思路:看非叶子结点是否满足大根堆(小根堆)要求,如果不满足进行调整

图解过程

小根堆的建立也差不多;

大根堆的排序过程图解

首先,就是交换大根堆堆顶元素和最后一个为排序的元素,交换后最后一个就是该元素的排序的位置,现在需要堆顶元素进行大根堆化;然后重复操作直到全部排序完成

大(小)根堆排序的代码实现

#include <stdio.h>
#include <stdlib.h>
#define boolean int
#define false 0;
#define true 1;
//大根堆排序,将以k为根的子树调整为大根堆
void HeadAdjust(int nums[],int k,int len){
int temp = nums[k];//暂存子树的根结点
for(int i=2*(k+1)-1;i <= len;i = i*2 + 1){//沿着k较大的结点,向下筛选
if(i < len && nums[i] < nums[i+1]){//看左孩子和右孩子那个大,取那个
i++;
}
if(temp >= nums[i]) break;//根结点大,就不需要交换,返回
else{
nums[k] = nums[i]; //否则交换
k = i; //修改k值,继续向下筛选
}
}
nums[k] = temp; //被筛选的结点放入最终位置
}
//建立大根堆
void BuildMaxHeap(int nums[] ,int length){
for(int i = length/2-1;i>=0;i--){ //从后往前非叶子结点进行大根堆初始化
HeadAdjust(nums,i,length-1);
}
}
//堆排序(大根堆)
void MaxHeapSort(int nums[],int length){
BuildMaxHeap(nums,length);//建立大根堆
for(int i = length-1;i > 0;i--){
//交换堆顶和堆堆尾
int temp = nums[i];
nums[i] = nums[0];
nums[0] = temp;
HeadAdjust(nums,0,i-1);//把新的堆变成大根堆
}
}
//小根堆排序,将以k为根的子树调整为小根堆
void HeadAdjust1(int nums[],int k,int len){
int temp = nums[k];//暂存子树的根结点
for(int i=2*(k+1)-1;i <= len;i = i*2 + 1){//沿着k较大的结点,向下筛选
if(i < len && nums[i] > nums[i+1]){//看左孩子和右孩子那个大,取那个
i++;
}
if(temp <= nums[i]) break;//根结点大,就不需要交换,返回
else{
nums[k] = nums[i]; //否则交换
k = i; //修改k值,继续向下筛选
}
}
nums[k] = temp; //被筛选的结点放入最终位置
}
//建立小根堆
void BuildMinHeap(int nums[] ,int length){
for(int i = length/2 - 1;i>=0;i--){ //从后往前非叶子结点进行大根堆初始化
HeadAdjust1(nums,i,length-1);
}
}
//堆排序(小根堆)
void MinHeapSort(int nums[],int length){
BuildMinHeap(nums,length);//建立大根堆
for(int i = length-1;i > 0;i--){
//交换堆顶和堆堆尾
int temp = nums[i];
nums[i] = nums[0];
nums[0] = temp;
HeadAdjust1(nums,0,i-1);//把新的堆变成大根堆
}
}
int main(){
int nums[] = {53,17,78,9,45,65,87,32};
int length = 8;
printf("堆(大根堆)排序前:");
for(int i = 0; i < length ;i++){
printf("%d ",nums[i]);
}
MaxHeapSort(nums,length);
printf("\n");
printf("堆(大根堆)排序后:");
for(int i = 0; i < length ;i++){
printf("%d ",nums[i]);
}
printf("\n");
int nums1[] = {49,38,65,97,76,13,27,47,89,13,48,76,88,88,99};
int length1 = 15;
printf("堆(小根堆)排序前:");
for(int i = 0; i < length1 ;i++){
printf("%d ",nums1[i]);
}
MinHeapSort(nums1,length1);
printf("\n");
printf("堆(小根堆)排序后:");
for(int i = 0; i < length1 ;i++){
printf("%d ",nums1[i]);
}
return 0;
}
//结果:
堆(大根堆)排序前:53 17 78 9 45 65 87 32
堆(大根堆)排序后:9 17 32 45 53 65 78 87
堆(小根堆)排序前:49 38 65 97 76 13 27 47 89 13 48 76 88 88 99
堆(小根堆)排序后:99 97 89 88 88 76 76 65 49 48 47 38 27 13 13

堆中插入一个新的元素

对于小根堆而言,新元素放到表尾,与父节点i2 这个结点进行对比,如果比父结点小就交换,交换后,继续与交换后的父结点对比,直到比它小或者到根了就结束

堆中插入一个元素图解

堆中删除一个元素

删除的元素如果不是最后一个,就使用最后一个代替它,然后对它进行小根堆化

堆中删除元素的图解

posted @   水三丫  阅读(53)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)
点击右上角即可分享
微信分享提示