数据结构之堆

队列,栈,堆,三大数据巨佬

性质

  • 堆中某个节点的值总是不大于或不小于其父节点的值;
  • 堆总是一棵完全二叉树。
    当节点的值小于父节点的值时,为大根堆,反之为小根堆
    二叉的特性使在堆上操作时间复杂度一般为o(log n)

使用

堆的根节点为1,当一个节点标为i时,其父节点下标为i/2,儿子节点为i2或i2+1
一般存在一个一维数组里。

基本操作

  • 建立一个空堆;
  • 向堆中插入一个新元素;
  • 使节点上浮;
  • 使节点下沉;
  • 获取当前堆顶元素的值;
  • 删除堆顶元素;
建立一个空堆
int heap[1010];
向堆中插入一个新元素

插入后可能会时堆的性质改变,要上浮维护。

heap[++tot]=n;
shift_up(tot);
//tot为堆中节点的个数
使节点上浮

上浮,让节点元素根其父节点元素比较,如果比它大(如果是小根堆就是比其父亲节点小就上浮),就交换两个节点。

void shift_up(int i/*要上(下)浮节点的下标*/){
	while(i/2){
		if(heap[i]>heap[i/2]){
			swap(heap[i],heap[i/2]);
		}
		i/=2;
	}
}
使节点下沉

当你修改堆的根节点后,你要让他上浮,但上帝很不要脸地告诉你,我要让他下沉

下沉,让节点元素根其儿子节点元素比较,如果比它小(如果是小根堆就是比其儿子节点大就下沉),就交换两个节点。

void shift_down(int i){
	while(i*2<=tot){
		int t=i*2;
		if(heap[i*2]>heap[i*2+1]){
			t++;
		}
		if(heap[i]<heap[t]){
			swap(heap[i],heap[t]);
		}
		i=t;
	}
}

有两个孩子,所以有点麻烦。当为小根堆时,要判断是否出界
因为出界的0是算有点小的。

获取当前堆顶元素的值

也就是找根

heap[1];
删除堆顶元素

不能称之为删除,只是把根和下标最后一个元素交换,把元素个数减一
这时下沉操作就有用了,让新的根下沉,继续维持堆的特性。

void kill_root(){
	swap(heap[1],heap[tot+1]);//根节点被换到了后面,又没记录下它,所以不会把他考虑进去,后面插入元素也会覆盖掉它
	shift_down(heap[1]);
}

作用

优化算法

其特性可以与其他算法相结合
如Dij堆优化,spfa堆化,或其他算法的堆优化
将节省大量的时间

在时常加入新的数中找最大(最小)等

保证根节点一定为最大(最小)的特性,可以用于堆优化
当你需要知道许多不断更新的数中,哪个元素最大(最小),相比于sort,堆来找是个不错的选择
因为你可以不停地插入元素,而又能已最少的时间代价知道哪个最大(最小)。

堆排序

可以删除(更新)根,插入元素,保证根一定最大(最小),耗费时间底。
用来排序是个不错的选择

int n;
cin>>n;
int heap[n+10];
for(int i=1;i<=n;i++){
	int x;
	cin>>x;
	heap[++tot]=x;
	shift_up(tot);
}
//以上是输入///
while(tot){
	cout<<heap[1]<<" ";
	kill_root();
}

没错,堆排序就看起来这么简单

效率

朴素操作

普通的树

考虑一个元素在堆中向下移动的距离。大约一半的结点深度为d-1。四分之一的结点深度为d-2,而它们至多能向下移动一层。树中每向上一层,结点的数目为前一层的一半,而子树高度加一。
这种算法时间代价为Ο(n)

由于堆有log n层深,插入结点、删除普通元素和删除最小元素的平均时间代价和时间复杂度都是
Ο(log n)。

堆排序时间复杂度

可与快排相媲美,只是排完序后要用o(n)的空间来存下
但时间复杂度是在操做上多了o(n)插入节点的时间,平均时间复杂度是令Oier垂涎欲滴的o(nlog(n))
虽然是不稳定的排序,时间复杂度有时高,有时底(差别不会很大)

其他

堆也可以有优化操作,堆加平衡树,斜堆,三叉堆,n叉堆等
最后配上图来说明真相

堆在数组里的表达效果

在这里插入图片描述

插入元素后在数组里表达的效果

在这里插入图片描述

上浮效果

在这里插入图片描述交换
在这里插入图片描述### 下沉效果
在这里插入图片描述在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值