堆,向下调整的递归和非递归实现,堆排序,STL中的优先队列,

什么是堆?

堆可以定义为一棵完全二叉树,且树的节点包含键值,用数组来实现的数据结构。
完全二叉树是指除了最底层,其他每一层都被完全填充,并且在最底层从左到右依次填充。

在这里插入图片描述

堆的类型

堆被分为两种类型:
大根堆:对于每个节点 i,父节点的值始终大于或等于其子节点的值。
小根堆:对于每个节点 i,父节点的值始终小于或等于其子节点的值。

在这里插入图片描述
这就保证了堆的第一个元素对于大根堆来说一定是所有数的最大值,对于小根堆来说一定是最小值
使得查询最大值(或最小值)的时间复杂度为O(1)。

堆的作用

1.插入一个值
2.输出最小值(小根堆)或最大值(大根堆)
3.删除最小值(小根堆)或最大值(大根堆)
4.删除任意一个数
5.修改任意一个数

如何建堆

对于一开始一堆散乱无序的数据来说,是还不形成堆的,所以要建堆。要建堆的话,就要掌握向下调整的操作了。
向下调整操作是指:如果是小根堆,当父亲节点比最小的孩子节点大时,应该要将父亲节点的值最小的孩子节点进行交换,一直交换到父亲节点的值小于孩子节点为止(小根堆)
我们拿一组数据模拟一下该过程(在建小根堆的情况下,向下调整的前提是该父亲节点的子树都是小根堆)
在这里插入图片描述
1.对于4的节点来说,左子树和右子树都满足了小根堆(父节点大于两个孩子节点),但4大于2,找到最小的孩子节点也就是2,然后对其进行交换,进入第二个过程。
2.4的节点仍然没有满足大于两个孩子节点的条件,所以继续调整,到了最终状态,满足小根堆的条件

在这里插入图片描述

观察这张图,这既不符合小根堆,也不符合大根堆,如果我想让这些数据变成符合小根堆的性质,该如何操作呢。
我们知道堆是数组来实现的,同时堆又是完全二叉树,那就可以用数组下标关系来表示节点之间的父子关系

父亲节点*2=左孩子节点
父亲节点*2+1=右孩子节点(一定要下数据从下标为1开始,不然无法正常查找父子关系)

上面可知,如果要建堆,就要利用向下调整的操作,但无序的数据又不像上面的例子一样,除了根节点以外的左子树和右子树都形成了小根堆。应该要如何操作呢?从上到下的进行多次向下调整不行,那么我们可以先对最后一个父亲节点进行向下调整。

最后一个父亲节点=数据总数n/2

这组数据来说最后一个父亲节点是3=6(数据总数)/2,也就是23的位置。
接下来画图分析从最后一个父亲节点向下调整的过程。
在这里插入图片描述

从最后一个父母节点往后依次调整,最终将会形成小根堆。

堆的实现

接下来用代码实现建堆,实现一个堆排序的过程吧,这里使用递归和非递归两种方式进行向下调整操作

在这里插入图片描述

#include<iostream>
using namespace std;
const int N=1e5+10;
int h[N];//实现堆的数组
int nums;//记录堆中元素个数
int m;//操作次数
void down1(int u){
   
   
	//递归做法
	int t=u;
	if(u*2<=nums&&h[t]>h[u*2]) t=u*2;
	//如果左孩子存在,并且父亲节点存储值比左孩子存储值大,t记录左孩子节点
	if(u*2+1<=nums&&h[t]>h[u*2+1]) t=u*2+1;
	//如果右孩子存在,并且上面t节点的存储值比右孩子大,说明找到更小的值了,用t记录该节点
	if(u!=t){
   
   
	//说明有孩子并且是存储值最小的孩子节点
	swap(
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值