堆排序

本文介绍了二叉堆的基本概念,包括最小堆和最大堆,并详细讲解了如何使用二叉堆进行排序。通过具体的代码实例,展示了堆的插入、删除操作及堆排序的过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

堆又叫二叉堆,是一种常用的数据结构。在实现中,有最大堆和最小堆两种。最小堆常常用于优先队列,堆也可以用于排序。
以最小堆为例:
最小堆中,一个结点的值不大于它的孩子。
       1      
      /   \           
     2     3  
    / \       / \
   4   5  6  7 
  / \   /  
 8 9 10
 可以用数组实现堆得结构
 图中位置为:0 1 2 3 4 5 6 7 8 9
 可以看出父结点、左孩子、右孩子关系如下: 
 leftchild(i)=2*i+1
 rightchild(i)=2*i+2
 parent(i)=(i-1)/2; 
 向堆中添加一个元素时,添加到末尾;这时候可能破坏了堆得性质,因此要维护堆。添加在末尾,只要向上维护即可。
 判断添加后是否满足堆的性质,不满足则维护这个结点。判断其父结点和父结点的左右两个孩子结点,最小的作为父结点。
 之后再判断父结点是否满足堆得性质,不满足再向上维护。
 
 从堆中删除结点时,要从堆顶删除。因为这个值是最值(最小堆的话就是最小值),之后把这个末尾结点放到堆顶。这样就破坏了堆

 的性质,向下维护堆,直到叶子结点。

堆排序时,以升序为例,可以用最小堆,每次取出堆顶元素(最小值),放到另外一个地方存储,这对内存要求为O(2N)。
 也可以用最大堆,把堆顶元素(最大值)依次放到存储堆得(数组或vector)的最后一个位置,堆得大小相应的减一。

#include<iostream>
#include<vector>
using namespace std;
class Heap{
public:
	Heap():_size(0){}
	void insert(int n);//插入 
	int delMin();//删除最小结点 
	vector<int> v;
private:
	unsigned int left(int i){return 2*i+1;}//找到左孩子 
	unsigned int right(int i){return 2*i+2;}//找到右孩子
	unsigned int parent(int i){return (i-1)/2;}//找到父节点 
	void up(int i);//向上为维护堆 
	void down(int i);
	
	unsigned int _size;
};
void Heap::up(int i)
{
	if(i==0)
		return ;
	unsigned int p=parent(i);//找到父节点 
	if(right(p)<v.size()) //p有有孩子
	{ 
		 if(v[left(p)]>v[right(p)])//右孩子小于左孩子
		 	{
		 		//交换这两个结点 
			 	int tmp=v[right(p)];
			 	v[right(p)]=v[p]	;
			 	v[p]=tmp;
		 	}
	 	  else{
  	 		int tmp=v[left(p)];
 			v[left(p)]=v[p];
	 		v[p]=tmp;
  	 	}
	}
	else
	{
		int tmp=v[left(p)];
		v[left(p)]=v[p];
		v[p]=tmp;
	}
	
	if(v[p]<v[parent(p)])
		up(p);
} 
void Heap::down(int i)
{
	//向下维护堆
	unsigned int l=left(i);
	unsigned int r=right(i);
	unsigned int minNode; //数值最小的结点 
	if(l>=v.size())//左孩子超出范围 
		return ;
	 if(r>=v.size()) //右孩子超出范围,只有左孩子 
	 { 
	 	if(v[l]<v[i])//左孩子子小于父结点
		  minNode=l; 
	  	else return;
	 }
	 else//既有左孩子又有右孩子
	 {
 		if(v[l]<v[r])//左孩子更小 
 			minNode=l;
		else minNode=r;
 	 }
 	 if(v[i]<v[minNode])// 不用再向下维护堆 
 	 	return ;
 	 int tmp=v[minNode];
 	 v[minNode]=v[i];
	 v[i]=tmp;
	 down(minNode);
	 	
}
void Heap::insert(int n)//插入一个元素到末尾 
{
	v.push_back(n);//最后一个位置插入
	unsigned int position=v.size()-1;//插入的这个数的位置
	if(v[position]<v[parent(position)]) //改变了堆得性质,要维护
		up(position);
}
int Heap::delMin()
{
	if(v.empty())//空的 
		//异常
		return 0;
	 int tmp=v[0]; 
	 v[0]=v[v.size()-1];
	 v.pop_back();
	 down(0);
  	 return tmp;
} 

int main()
{
	Heap H;
	for(int i=0;i<10;i++)
	{
		int k=rand()%100;
		H.insert(k);
		cout<<k<<" ";
	}
	cout<<endl;
	while(!H.v.empty())//无存储排序元素,直接输出 
	cout<<H.delMin()<<" ";
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值