堆——堆的基本操作

堆的相关操作

堆是用完全二叉树存储一系列值,完全二叉树的父节点都比子节点小则成为最小堆,反之则称为最大堆

根据完全二叉树性质

如果子节点的编号为i,父节点为i/2(向下取整),左子节点为2*i,右子节点为2*i+1

树的最大层数为logN

相关操作

如果在堆顶插入一个新元素,要维护最小堆,则需要将该元素进行向下移动,保证父节点都小于子节点

向下调整

void shiftdown(int i){//i为节点编号 
	int t,flag = 0;//flag标记是否需要继续向下调整
	while(2*i<=n && flag == 0){//至少有左子节点且flag=0 
		//与左子节点比较 
		if(h[i] > h[2*i]){
			t = 2*i;
		}else{
			t = i;
		}
		//如果存在右子节点,比较
		if(2*i+1<=n){
			if(h[t] > h[2*i+1]){
				t = 2*i + 1;
			}
		}
		//如果不是自己,子节点无比它小的节点 
		if(t!=i){
			//交换 
			swap(t,i);
			i = t;//更新i为刚才交换的子节点,继续向下调整 
		}else{
			flag = 1;
		}
	} 
}

如果需要新增一个值,则插入队列的尾部,再进行相上调整,即如果父节点比该节点大则与之交换

相上调整


void shiftup(int i){
	int flag = 0;//记录是否需要相上调整
	if(i == 1) return;//如果为根节点则直接退出
	while(i != 1 && flag == 0){//不在堆顶且需要向上调整 
		if(h[i] < h[i/2]){
			swap(i,i/2);
		}else{
			flag = 1;
		}
		i = i/2;//更新节点 
	} 
}

建立堆(最小堆)

  1. 直接边插入队列边调整
n=0;
for(int i=1;i<=m;i++){
	n++;
    h[n] = a[i]//或者cin>>h[n];
   	shiftup(n);
}
  1. 先将所有值入队,再历遍每一个除叶节点外的所有节点,与子节点进行调整
void create(){
	int i;
	for(int i=n/2;i>=1;i--){
		shiftdown(i);
	}
}

堆排序

例如从小到大排序,先建立最小堆,然后一次删除堆顶并将其存入数组中,然后维护堆,继续此操作

int deletemin(){
	int t;
	t = h[1];//临时变量存储堆顶 
	h[1] = h[n];//将堆的最后一个值放入堆顶 
	n--;//队列size-1 
	shiftdown(1);//堆顶向下调整 
	return t;
}

完整代码

#include <iostream>
using namespace std;
int h[101];//存放堆的数组
int n;//堆的大小 

void swap(int x,int y){
	int t;
	t = h[x];
	h[x] = h[y];
	h[y] = t;
}

void shiftdown(int i){//i为节点编号 
	int t,flag = 0;//flag标记是否需要继续向下调整
	while(2*i<=n && flag == 0){//至少有左子节点且flag=0 
		//与左子节点比较 
		if(h[i] > h[2*i]){
			t = 2*i;
		}else{
			t = i;
		}
		//如果存在右子节点,比较
		if(2*i+1<=n){
			if(h[t] > h[2*i+1]){
				t = 2*i + 1;
			}
		}
		//如果不是自己,子节点无比它小的节点 
		if(t!=i){
			//交换 
			swap(t,i);
			i = t;//更新i为刚才交换的子节点,继续向下调整 
		}else{
			flag = 1;
		}
	} 
}

void shiftup(int i){
	int flag = 0;//记录是否需要相上调整
	if(i == 1) return;//如果为根节点则直接退出
	while(i != 1 && flag == 0){//不在堆顶且需要向上调整 
		if(h[i] < h[i/2]){
			swap(i,i/2);
		}else{
			flag = 1;
		}
		i = i/2;//更新节点 
	} 
}

void create(){
	int i;
	for(int i=n/2;i>=1;i--){
		shiftdown(i);
	}
}

int deletemin(){
	int t;
	t = h[1];//临时变量存储堆顶 
	h[1] = h[n];//将堆的最后一个值放入堆顶 
	n--;//队列size-1 
	shiftdown(1);//堆顶向下调整 
	return t;
}



int main()
{
	int num;
	cin>>num;
	for(int i=1;i<=num;i++){
		cin>>h[i];
	}
	n = num;
	//建立堆 
	create();
	
	//从小到大输出,即连续删除n次
	for(int i=1;i<=num;i++){
		cout<<deletemin()<<" ";
	}	
	return 0;
}

堆排序的优化方法

可以建立最大堆,将对立第n个值与堆顶交换,则队列中第n个为最大的值,然后将堆顶进行调整,再依次进行上述步骤,最后堆大小为1时停止,此时原理的队列即为从小到大的顺序队列

void heapsort(){
    while(n > 1){
        swap(1,n);
        n--;
        shiftdown(1);
    }
}

完整代码

注意此时要建立最大堆才能从小到大排序

#include <iostream>
using namespace std;
int h[101];//存放堆
int n;//堆大小

void swap(int x,int y){
	int t;
	t = h[x];
	h[x] = h[y];
	h[y] = t;
}

void shiftdown(int i){
	int t,flag = 0;
	while(2*i <= n && flag == 0){
		if(h[i] < h[2*i]){
			t = 2*i;
		}else{
			t = i;
		}
		
		if(2*i+1<=n){
			if(h[t] < h[2*i+1]){
				t = 2*i + 1;
			}
		}
		if(t!=i){
			swap(t,i);
			i = t;
		}else{
			flag = 1;
		}
	}
}

void create(){
	for(int i=n/2;i>=1;i--){
		shiftdown(i);
	}
}

//堆排序
void heapsort(){
	while(n > 1){
		swap(1,n);
		n--;
		shiftdown(1);
	}
} 

int main()
{
	int num;
	cin>>num;
	for(int i=1;i<=num;i++){
		cin>>h[i];
	}
	n = num;
	
	create();
	
	heapsort();
	
	for(int i=1;i<=num;i++){
		cout<<h[i]<<" ";
	}
	return 0;
} 

总结

支持插入元素和寻找最大/最小值元素的数据结构为优先队列

同样利用堆可以求第K大的数

只需要建立K大小的最小堆,堆顶即为结果

例如,10个数求第3大的数,任选1-3建立堆,历遍4-10,如果数比堆顶小,则放弃,如果数比堆顶大,则将其与当前堆顶替换,再进行维护,以此类推

利用堆可以求第K小的数

建立K个大小的最大堆,堆顶为求的第K小的数

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值