关于堆排序

关于堆排序

Preface:

本来这个蒟蒻并不想水这一篇水水的博客,但在昨天看到某人在听完课之后,自信地发了一篇关于并查集的拓展域的博客(知识点还漏了不少)后脑洞大开,于是在刚学完之后水了一篇关于堆排序的博客。(初学堆,讲不好别打我)

Part 1:堆(heap)

堆排序的重点就是利用堆的性质,在这之前,首先要了解堆。堆在本质上是一棵完全二叉树。一棵完全二叉树中,左儿子=父亲×2,右儿子=父亲×2+1。
堆分为两种,大根堆和小根堆。大根堆就是在这棵完全二叉树中,任意一个父亲节点大于等于它的两个儿子节点。小根堆同理,就是在这棵完全二叉树中,任意一个父亲节点小于等于它的两个儿子节点。如下图所示


蒟蒻画的图,将就着看吧
仔细观察这两张图,不难发现大根堆中的最大数就是它的根节点,小根堆中的最小数就是它的根节点。

Part 2:Solution

根据,大根堆中的最大数就是它的根节点,小根堆中的最小数就是它的根节点这一结论,我们就来分析一下堆排序的基本思路(这里讲从大到小的排序)。
1. 读入n,再读入n个数,每读入一个数,就把它们插入到堆中。只要每次把输入元素扔到最后,再利用堆的性质与它的父亲比较,如果不满足性质就交换,直到满足性质
2.这里因为从大到小,所以用小根堆。每次把堆顶元素(一定是最小的)扔到最后,实际上是把堆顶和最后一个元素交换,再次利用堆的性质,把堆顶元素不停和他的儿子比较,如果不满足性质就交换,直到满足性质。
3.每次取出的堆顶元素一定是该堆中最小的,并且该堆中的最大元素在最后一定是堆顶,所以只要把堆中的每个数按顺序依次输出即可

Part 3:Code

#include<bits/stdc++.h>
using namespace std;
const int maxn=1000000;
int n,i,t,x,a[maxn];
void up(int x);
void down();
int main(){
	cin>>n;
	for(i=1;i<=n;i++)
	{
		cin>>x;
		up(x);   //每读入一个数,就插入到堆中 
	}
	for(i=1;i<=n;i++)
	{		
		swap(a[1],a[t]);//交换堆顶和最后一个元素 
		t--;//堆中剩下元素个数-1 
		down();
	}
	for(i=1;i<=n;i++) cout<<a[i]<<" ";//从大到小依次输出 
	cout<<"\n";
	return 0;//养成好习惯
}
void up(int x){
	int i;
	t++;
	a[t]=x;//把新元素默认成最后一个 
	i=t;
	while(i>1&&a[i]<a[i/2]) //当不满足时 
	{
		swap(a[i],a[i/2]);//交换父亲,儿子 
		i/=2;
	}
}
void down(){
	int i=1;
	int son;
	while(i*2<=t)//当左儿子还在 
	{
		son=i*2;//默认左儿子小 
		if(son+1<=t&&a[son+1]<a[son]) son++;
		//贪心,如果右儿子还在且它比左儿子小 
		if(a[i]>a[son]) swap(a[i],a[son]),i=son;
		//父亲一定要比儿子大才能交换 
		else break;
		//已经满足要求 
	}
}
讲了从大到小堆排序,从小到大只要用大根堆即可。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值