堆排序(堆)

98 阅读2分钟

该排序首先要建立堆以建立最小堆为例,在建立堆的时候用到了向下调整的思想,即该点如果的值a[i],如果小于a[2*i]或a[2*+1],则需要把a[i]与a[2*i]和a[2*+1]的较小者交换,以达到上小下大,需要注意的是在建立最小堆的时候要从后往前建立,也就是最后一个非叶子节点开始建立,即a[n/2],因为这样可以达到下面的数都比上面的数大(如果从根开始建堆的话,可能会出现最下面的数很小,但是中间的数较大,然后中上的数又较小,而最上面的数很大,这样一来,最上面的数与中上换过之后不能与中间的数交换,以导致最下面的最小数无法换到上面,下面给的样例就是这样的)

样例输入:

14
99 5 36 7 22 17 46 12 2 19 25 28 1 92

样例输出:

1 2 5 7 12 17 19 22 25 28 36 46 92 99

代码:

#include<stdio.h>
#include<algorithm>
using namespace std;
int a[110];
int n;
void siftdown(int i);//向下调整 
int main()
{
	int i,j,maxn=-1;
	scanf("%d",&n);
	for(i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
		if(a[i]>maxn)
			maxn=a[i];
	}
	for(i=n/2;i>=1;i--)//建立堆 
		siftdown(i);

	for(i=1;i<=n;i++)
	{
		printf("%d ",a[1]);
		a[1]=maxn;
		siftdown(1);
	}
	printf("\n");
	return 0;
}
void siftdown(int i)//最小堆
{
	int t,flag=0,temp;
	
	while(2*i<=n&&flag==0)
	{
		t=i;
		if(a[t]>a[2*i])
			t=2*i;
		if(2*i+1<=n&&a[t]>a[2*i+1])
			t=2*i+1;
		if(t!=i)
		{
			temp=a[i];
			a[i]=a[t];
			a[t]=temp;
			i=t;
		}
		else
			flag=1;
	}
}

在从小到大排序过程,更多的用的是建立最大堆,因为最大堆中最上面就是最大的数,然后把a[1]和a[n]交换,此时a[n]就是最大的数了,然后将堆的大小减1,即n--,还需要把a[1]进行向下调整,以保持堆的特征,循环操作,直到堆为空即可,当然了,你可能会问建立最小堆为什么不这样操作呢,其实也可以,你可以试试,之所以用最大堆,我感觉是因为在排完序之后输出很方便,直接把数组从1到n输出即可

#include<stdio.h>
#include<algorithm>
using namespace std;
int a[110];
int n;
void siftdown(int i);//向下调整
int main()
{
	int i,j,t;
	scanf("%d",&n);
	for(i=1;i<=n;i++)
		scanf("%d",&a[i]);
		
	for(i=n/2;i>=1;i--)//建立堆 
		siftdown(i);
	t=n;
	while(n>1)//最后一个就不用排了吧,所以是n>1 
	{
		swap(a[1],a[n]);
		n--;
		siftdown(1);
	}
	for(i=1;i<=t;i++)
		printf("%d ",a[i]);
	printf("\n");
	return 0;
}
void siftdown(int i)//最大堆 
{
	int t,flag=0,temp;
	
	while(2*i<=n&&flag==0)
	{
		t=i;
		if(a[t]<a[2*i])
			t=2*i;
		if(2*i+1<=n&&a[t]<a[2*i+1])
			t=2*i+1;
		if(t!=i)
		{
			temp=a[i];
			a[i]=a[t];
			a[t]=temp;
			i=t;
		}
		else
			flag=1;
	}
}