前言:其实STL中已经定义了有关堆操作的函数:

https://blog.csdn.net/qq_41431457/article/details/88573917

这篇文章主要介绍一下它的原理

一、什么是堆

1、堆是一个二叉树,每个结点都要比两个孩子结点要大或者最小,所以根结点是二叉树中最大或最小的结点

2、因为我们用树状数组来存储,所以堆也是一个完全二叉树,

3、根结点是a[1],左孩子与右孩子结点分别是a[2*n] ,a[2*n+1]

4、大根堆:所有的结点都比它左右孩子结点要大(根结点最大)

5、小根堆:所有的结点都比它的左右孩子结点要小(根结点最小)

二、堆的解题范围

因为堆是只能保证根结点最大或最小,所以题目中如果只需要求一个整个序列里最小或者最大的一个值,就可以考虑用堆结构处理

 

三、基本操作(以大根堆为例子)

1、向下维护堆,从一个节点开始,如果左右孩子比它要大,那么选一个大的孩子结点与之交换,在往下维护

void down(int x)
{
	int s=p<<1;
  	while(s<=size)
  	{ //下面这句话是从左右儿子中选一个更大的做交换
    	if(s+1<=size&&heap[s+1]>heap[s]) s++; 
    	if(heap[s]>heap[p])
    	{
      		swap(heap[s],heap[p]);
      		p=s; s=p<<1; //转移到大的那个孩子结点 
    	}
    	else break;
  	}
}

2、向上维护堆,同上,只不过顺序颠倒过来了

void up(int x)//二叉小根堆向上调整(子节点小于父节点就调整)
{
	while(x>1)
	if(heap[x]>heap[x/2])//如果此节点比父结点大的话 
	{
		swap(heap[x],heap[x>>1]);//交换 
		x>>=1;//转移到该节点的父节点 
	}
	else break; 
}

3、插入堆,先将插入的元素放到堆底,在向上调整堆

void insert(int val)//二叉堆插入,新元素放在堆底,向上调整
{
	heap[++size]=val;//放到堆的末尾 
	up[size];//从底部往上维护堆 
}

4、取出堆顶(将堆顶放到堆地,堆底上移)并维护堆

int extract() //删除堆顶并返回 
{
	swap(heap[1] , heap[size--]);//将堆顶移至堆底,向下调整
	down(1);//从堆顶往下维护 
	return heap[size+1] ;
}

 

四、堆排序

假设序列已经是一个堆,我们将根结点取出,用最后一个结点补上,此时,堆结构被破坏,我就需要调整剩下的结点构成的二叉树,重新将它调整为堆

步骤为:

  1. 初建堆,将原序列序列建成一个堆结构
  2. 取出堆顶(根结点),用最后一个结点补上堆顶
  3. 调整这个棵树,使之重新为堆
  4. 重复2-3步,直到所有的结点都被取出

代码:

#include<bits/stdc++.h>
#define M 10000
using namespace std;
int heap[M],size,n;//定义树状数组,数组长度 
void up(int x)//二叉小根堆向上调整(子节点小于父节点就调整)
{
	while(x>1)
	if(heap[x]>heap[x/2])//如果此节点比父结点大的话 
	{
		swap(heap[x],heap[x>>1]);//交换 
		x>>=1;//转移到该节点的父节点 
	}
	else break; 
}
void down(int x)
{
	int s=p<<1;
  	while(s<=size)
  	{ //下面这句话是从左右儿子中选一个更大的做交换
    	if(s+1<=size&&heap[s+1]>heap[s]) s++; 
    	if(heap[s]>heap[p])
    	{
      		swap(heap[s],heap[p]);
      		p=s; s=p<<1; //转移到大的那个孩子结点 
    	}
    	else break;
  	}
}
void insert(int val)//二叉堆插入,新元素放在堆底,向上调整
{
	heap[++size]=val;//放到堆的末尾 
	up[size];//从底部往上维护堆 
}
int extract() //删除堆顶并返回 
{
	swap(heap[1] , heap[size--]);//将堆顶移至堆底,向下调整
	down(1);//从堆顶往下维护 
	return heap[size+1] ;
}
int main()
{
	cin>>n;
	n=size; 
	for(int i=n/2;i>=1;i--)//初建堆,从下往上维护 
		down(i);
	
	for(int i=1;i<n;i++)//每次将堆顶放到最后,再调整堆 
		extract();
	//此时有序 
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值