​经典算法09 堆排序

​经典算法09 堆排序

活动地址:CSDN21天学习挑战赛

*学习的最大理由是想摆脱平庸,早一天就多一份人生的精彩;迟一天就多一天平庸的困扰。

简介

堆排序和简单选择排序一样属于选择排序

简单排序:每一趟在待排序的元素中选取关键字最小(或最大)的元素加入有序子序列

堆排序:每一趟将堆顶元素加入有序子序列(与待排序序列中的最后一个元素交换)并将待排序元素序列再次调整为大根堆(小元素不断“下坠”)

堆排序基于一个叫做堆的数据结构,所以我们先看什么是堆

定义:

若n个关键字序列L[1…n〕 满足下面某一条性质,则称为堆 (Heap)
①若满足:L(i)≥L(2i)且L≥l(2i+1) (1≤i≤n/2)—— 大根堆(大顶堆)
②若满足:L(i)≤L(2i)且L(i)≤L(2i+1) (1≤i ≤n/2)——小根堆(小顶堆)

可以结合完全二叉树的顺序存储来理解,即大根堆位完全二叉树中,根大于等于左和右的情况,小根堆位根小于等于左右节点情况,如下图所示
(图片来自王道考研)
请添加图片描述

请添加图片描述

请添加图片描述

大根堆

在我们知道了堆的概念之后,我们所需要做的就是对于所给出的初始序列,建立大根堆,即把所有非终端节点都检查一遍,是否满足大根堆的要求,如果不满足,则进行调整

分析:

给定初始序列,在顺序存储的完全二叉树中,非终端节点编号i<=[n/2]
(图片来自王道考研)
请添加图片描述
请添加图片描述

首先,先处理四号节点,,检查当前节点是否满足根>=左、右,若不满足,将当前节点与更大一个孩子互换,他只有一个孩子即和31进行互换

请添加图片描述

接着看三号节点,孩子的下标为2i,2i+1即6和7,78大于67但小于87,不符合大根堆特性,即78和87互换
(图片来自王道考研)
请添加图片描述

接下来处理二号节点,17小于32,也小于45,我们选择更大的45与17互换,如果换32,45大于32,不符合大根堆的特性
(图片来自王道考研)
请添加图片描述

最后处理一号节点,87更大,所以互换
(图片来自王道考研)
请添加图片描述

此时问题发生了,53下落以后,导致下一层的子树不符合大根堆的要求,此时我们需要继续调整,是53和78互换
(图片来自王道考研)
请添加图片描述

##代码实现

//建立大根堆
void BuildMaxHeap(int A[], int len){
for(int i=len/2;i>0;i--)
HeadAdjust (A, i, len);	//从后往前调整所有非终端结点
}
//将以 k 为根的子树调整为大根堆
void HeadAdjust(int All, int k, int len){
A[0]=A[k];	//A[0]暂存子树的根结点
for(int i=2*k;i<=len; i*=2){ //取key较大的子结点向下筛选
if(i<len&&A[i]<A[i+1])
i++;			//取key较大的子结点的下标
if(A[0]>=A[i])
break; //筛选结束
else{
A[k]=A[i]:		//将Ard]调整到双亲结点上
k=i;		//修改k值,以便继续向下筛选
}
A[k]=A[0]:	//被筛选结点的值放入最终位置
}

##基于大根堆进行排序

堆排序:每一趟将堆顶元素加入有序子序列(与待排序序列中的最后一个元素交换)

分析:

基于上述,我们建立了大根堆如下图所示
(图片来自王道考研)
请添加图片描述

我们需要将09和87进行交换,87换到了末尾,87不需要进行变动了

(图片来自王道考研)请添加图片描述

此时09到了堆顶,上面的一部分已经不是一个大根堆了,所以我们需要对09进行下坠,09的右孩子大,所以和78进行互换,再和65进行互换

(图片来自王道考研)请添加图片描述

请添加图片描述

此时,上面部分又成了大根堆,和上面一样,将堆顶的元素和末尾的元素进行交换
(图片来自王道考研)
请添加图片描述

此时53到了堆顶,又不满足大根堆的特性,所以继续上述的操作,将剩余部分变成大根堆
(图片来自王道考研)
请添加图片描述

第三趟,堆顶元素65和堆底元素09进行互换,09继续下坠形成大根堆
(图片来自王道考研)
请添加图片描述

第四趟,堆顶元素53和堆底元素17进行互换,17继续下坠形成大根堆
(图片来自王道考研)
请添加图片描述

第五趟,堆顶元素45和堆底元素17进行互换,17继续下坠形成大根堆
(图片来自王道考研)
请添加图片描述

第六趟,堆顶元素32和堆底元素09进行互换,09继续下坠形成大根堆
(图片来自王道考研)
请添加图片描述

第七趟,堆顶元素17和堆底元素09进行互换,此时已经完成,不需要进行调整了
(图片来自王道考研)
请添加图片描述

对于n个元素的序列,我们进行了n-1趟的处理,得到递增的序列

如果使用小根堆,则得到递减的序列

基于大根堆进行排序(代码)

//建立大根堆
void BuildMaxHeap(int A[], int len)

  //将以 k 为根的子树调整为大根堆
void HeadAdjust (int A[],int k, int len)
  //堆排序的完整逻辑
void HeapSort (int A[], int len){
BuildMaxHeap (A, len);		//初始建堆
for(int i=len;i>1;i--){		//n-1趟的交换和建堆过程
swap(A[i],A[1]) ;					//堆顶元素和堆底元素交换
HeadAdjust (A,1, i-1) ;	//把剩余的待排序元素整理成堆
}
}

算法效率分析:

结论: 一个结点,每“下坠”一层,最多只需对比关键宇2次
若树高为h,某结点在第i层,则将这个结点向下调整最多只需要“下坠”h-i层,关键字对比次数不超过 2(h-i)
(图片来自王道考研)
请添加图片描述

故建堆的过程,关键字对比次数不超过4n,建堆时间复杂度=O(n)
(图片来自王道考研)请添加图片描述

空间复杂度=O(1)

稳定性:不稳定

总结:
(图片来自王道考研)
请添加图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值