9.12 堆、二叉堆插入、删除操作模板代码+《算法进阶指南》例题


一、堆 概念

堆是一棵树,每个节点都有一个键值
每个节点的键值 >= 父亲节点的键值,叫小根堆;每个节点的键值 < 父亲节点的键值,叫大根堆

(小根)堆主要支持的操作有:插入一个数、查询最小值、删除最小值,合并两个堆、减小一个元素的值

在这里插入图片描述
(表中“支持可持久化”是指可以对任意的历史版本进行查询或操作,产生新版本)

通常情况下,“堆”指的是二叉堆

二、二叉堆

二叉堆其实是一棵满足“堆性质”(大、小根堆)的完全二叉树
直接使用一个数组来保存二叉堆,采用层次序列存储方式,即逐层从左到右为树中的节点依次编号,把此编号作为节点在数组中存储的位置即下标
在这里插入图片描述

插入操作 insert

向二叉堆插入一个带有权值val的新节点:时间复杂度为堆的深度,即O(log N)

  1. 把新节点直接放在存储二叉堆的数组末尾
  2. 然后通过交换的方式向上调整,直至满足堆的性质
int heap[SIZE],n;

void up(int p){//把下标编号为p的数 向上调整 
	while(p>1){
		if(heap[p]>heap[p/2]){//子节点>父亲节点,不满足大根堆性质 
			swap(heap[p],heap[p/2]);
			p/=2; 
		}
		else break;
	}
}

void insert(int val){//插入值为val的数 
	heap[++n]=val;
	up(n);
} 

删除堆顶操作 extract

时间复杂度为堆的深度,即O(log N)

  1. 把堆顶heap [1] 与存储在数组末尾的节点 heap [n] 交换
  2. 然后移除数组末尾的节点 n–
  3. 把堆顶通过交换的方式向下调整,直至满足堆的性质
void down(int p){//向下调整
	int s=p*2;//p的左子节点 
	while(s<=n){
		if(s<n&&heap[s]<heap[s+1]) s++;//左右子节点取较大者 
		if(heap[s]>heap[p]){//子节点>父亲节点,不满足大根堆性质 
			swap(heap[s],heap[p]);
			p=s,s=p*2;
		}
	}
}

void extract(){ //删除堆顶操作
	heap[1]=heap[n--];
	down(1);
} 

删除操作 remove

把存储在数组下标为p的位置的节点从二叉堆里删除 remove§,与extract 类似,不同的是先把heap [p] 与存储在数组末尾的节点 heap [n] 交换,
注意,此时heap [p] 可能需要向下调整,也可能需要向上调整,需要分别进行检查和处理;

void remove(int p){ //删除下标为p的位置的节点操作
	heap[p]=heap[n--];
	up(p),down(p);
} 

三、《算法进阶指南》例题

145. 超市

链接: 145. 超市

/*贪心+小根堆*/
#include<bits/stdc++.h>
using namespace std;
int n;
int main(){
	while(cin>>n){
		vector<pair<int,int> >product(n);
		for(int i=0;i<n;i++){
			cin>>product[i].second>>product[i].first;
		}
		sort(product.begin(),product.end());//先按照product[i].first排序 ;product[i].first相同,再按 product[i].second排 
		/*for(int i=0;i<n;i++){
			cout<<product[i].second<<' '<<product[i].first<<endl;
		}*/
		priority_queue<int,vector<int>,greater<int> >heap;
		for(auto p:product){//贪心:动态维护一个小根堆,
			heap.push(p.second);
			if(heap.size()>p.first) heap.pop();//如果当前的小根堆存储的商品数量>天数(也就是应该要卖的商品数量) 就把利润最小的商品删除掉 
		}
		/*while(heap.size()){
			cout<<heap.top()<<endl;heap.pop();
		} */
		int ans=0;
		while(heap.size()){//小根堆里剩余的就是要卖的商品 
			ans+=heap.top();heap.pop();
		}
		cout<<ans<<endl;
	}
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值