堆排序 _ (HeapSort)

堆排序

第一次写blog,希望尽量能表达清楚。

       堆排序是根据一个数组,可以把它想象成一个按层遍历的完全二叉树,不用在乎它左右孩子的大小关系,只注意孩子与双亲的大小关系,根据双亲的与孩子的大小关系可以分为大根堆小根堆。显而易见,大根堆就是双亲的值比孩子的值都大,而小根堆就是双亲的值比孩子的值都小,通俗一点的说,在双亲,左孩子,右孩子中如果双亲是最大的值就是大根堆,最小的值就是小根堆。

 那搞清楚大根堆和小根堆后,可以把堆排序分成两个过程,一是调整(Adjust)就是把无序的完全二叉树(数组)调整成大根堆或小根堆,在此过程中是只针对父节点的;二就是排序(HeapSort),排序的过程就是把根节点(数组中的最值)与数组中的最后一个值相互交换并调整,在调整的过程向后交换的值不参与调整,就这样再一次调整后就是除了交换的值中的又一个最值,然后重复此过程。

 还有一点值得注意的是:大根堆调整是排序最后是升序,而小根堆调整的最后是降序。很好理解,例如大根堆的大的在根节点,在排序中又与最后的值交换成升序。

 不罗嗦了,上代码。

#include<stdio.h>
#include<stdlib.h>

#define lchild 2*nroot+1
#define rchild 2*nroot+2

void Adjust(int arr[],int length,int nroot)
{
	int max;
	for(max = lchild;max<length;max=lchild)
	{
		//先找到两个孩子中最大的
		if(rchild<length)
		{
			if(arr[rchild]>arr[max])
			{
				max = rchild;
			}
		}
		//拿最大的跟双亲比较,交换
		if(arr[max]>arr[nroot])
		{
			printf("%d %d\n",arr[max],arr[nroot]);
			arr[max] = arr[max]^arr[nroot];
			arr[nroot] = arr[max]^arr[nroot];
			arr[max] = arr[max]^arr[nroot];
			
			//交换后继续交换 已交换的孩子
			nroot = max;
			continue;
		}
		else
		{
			break;
		}
	}
}

void HeapSort(int arr[],int length)
{
	if(arr == NULL ||length <=0) return ;

	int i;
	for(i=length/2-1;i>=0;i--)  //只调整双亲节点 (0-n/2-1 是双亲节点的位置)
	{
		Adjust(arr,length,i);
	}

	for(i=length-1;i>0;i--)
	{
		//arr[0]和最后的元素交换
		arr[0] = arr[0]^arr[i];
		arr[i] = arr[0]^arr[i];
		arr[0] = arr[0]^arr[i];
		
		Adjust(arr,i,0);
	}
}

void Print(int arr[],int length)
{
	int i;
	for(i=0;i<length;i++)
	{
		printf("%d ",arr[i]);
	}
	printf("\n");
}
int main()
{
	int arr[] = {6,5,4,7,4,10};
	HeapSort(arr,sizeof(arr)/sizeof(arr[0]));

	Print(arr,sizeof(arr)/sizeof(arr[0]));
	return 0;
}
   不得不说堆排序是所有排序中效率最高的一个算法了,尤其是在处理成千上万的数据的时候。堆排序在ACM中也是常常会遇到,往往是多数组合并排序,或者是多数组求前k个最大(小)值的问题。我们可以把她统称为Top(k)的问题。

  下面我将用堆排序的思想求11个随机数中前5个最大的数。

 思想:求5个最大的数,可以向创建一个五个数的小根堆,然后用根和后面的6个数依次比较,如果跟比随机的数小,则让根成为该随机数调整,继续直到完成6个数的比较。

#include<stdio.h>
#include <stdlib.h>
#include <sys/time.h>

void Adjust(int arr[],int nLength,int nRoot) //利用小根堆
{
	int nMin;
	for(nMin = 2*nRoot+1 ; nMin < nLength ; nMin = 2*nRoot+1)
	{
		if(2*nRoot+2 < nLength)
		{
			if(arr[2*nRoot+2] < arr[nMin])
			{
				nMin = 2*nRoot+2;
			}
		}


		// 跟父亲交换值

		if(arr[nMin] < arr[nRoot])
		{
			arr[nMin] = arr[nMin]^arr[nRoot];
			arr[nRoot] = arr[nMin]^arr[nRoot];
			arr[nMin] = arr[nMin]^arr[nRoot];

			nRoot = nMin;
			continue ;
		}
		else
		{
			break;
		}

	}
}

int FirstBig(int nBig,int nSmall) //大的交换,便于扩展
{
	return nBig - nSmall;
}

void Insert(int arr[],int nLength,int (*rules)(int ,int ),int nInsertValue)
{
	if((*rules)(nInsertValue,arr[0]) > 0)
	{
		arr[0] = nInsertValue;
		Adjust(arr,nLength,0);
	}
}

void Print(int arr[],int nLength)
{
	for(int i=0;i<nLength;i++)
		printf("%d ",arr[i]);

	printf("\n");
}

int main()
{
	//基础堆
	int arr[5];
	
	int nLength = 5;

	// 随机产生 5 个数 放入堆中
	
	for(int i=0;i<5;i++)
	{
		arr[i] = rand()%100;
	}
    
	// 调整 堆,使根节点为最小值
	for(int i=nLength/2-1 ; i>=0 ; i--)
	{
		Adjust(arr,nLength,i);
	}
	
	printf("初始堆序列为:");
	Print(arr,nLength);
	
	int nInsert;
	printf("插入的序列为:");
	for(int i=0 ; i<10; i++)
	{
		nInsert = rand()%100;
		Insert(arr,nLength,&(FirstBig),nInsert);
		printf("%d ",nInsert);
	}
	printf("\n");
	
	printf("插入调整完后的序列为:");
	Print(arr,nLength);
	printf("\n");
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值