八大排序学习之八分配排序(计数排序、桶排序、基数排序}

分配排序,常见的有三种,计数排序和桶排序和基数排序,他们的时间复杂度最好都是O(dn), d是指数组中最大的数

计数排序、桶排序和基数排序都用了一种方法,把想要排序的数据放到桶子里,再进行排序,这是一种用空间换取效率的方法。

首先讲计数排序,是一种执行效率非常高的算法,缺点是应用限制太多,只能用于正整数。

计数排序思想很简单,就像中国好声音那样,评委举了多少张10分的牌,就计算得到多少次10分,再按照那些牌是举过的,从低到高遍历,举多少次就输出多少次。

以下面的数组A[7]={2,3,5,6,1,10,100}为例:

我们知道了大概排序的策略,但是应该分多少个张牌才行呢?为了不漏,必须每个分数分一张牌。

所以我们只能从最大数处确认需要分配多少张牌。

获取最大数:

int Getmaxnum(int *arr,int n)
{
	int max=arr[0];
	for(int i=0;i<n;i++)
		if(arr[i]>max)
			max=arr[i];
		return max;
}
关键代码:

void countsort(int *arr,int n)
{
	int max=Getmaxnum(arr,n);
	int *a=new int[max+1];
	for(int i=0;i<max+1;i++)
		a[i]=0;
	for( i=0;i<n;i++)
		a[arr[i]]++; //第i张牌加1
	int j=0;
		for(i=0;i<max+1;i++)
		{
			for(int k=a[i];k!=0;k--)//把所有的牌按照出现次数存起来
			{
				arr[j]=i;
				j++;
			}
		}
		delete []a;
}
下面对于20000个数的排序情况:


细心的朋友可以看到这个时间比之前的快了很多,这个要澄清一下,前面的占用时间比较多的原因是计算时间把printf的时间算进去了,其实一般都是0.01秒左右计算完2000个数据的排序的了,在我这台电脑。


下面讲桶排序:

桶排序,的思想是把待排的数据按照某中映射到指定的区间,就是桶,举个例子,我们在上体育课回收哑铃的时候是按照哑铃的重量进行回收的,同一个型号的放到同一个桶,我们排序仅仅需要从桶的编号排序便可(从型号到桶编号说的就是这种映射)

在这里我们建立一个映射如下:把小于10的放到0号桶,10到100的放到1号桶,再对桶里面的采用我们常见的排序方法,可以用插入排序等等。

但是一个桶怎么装的下那么多数据呢,这里我们采用链表的方式,来访问同一个同的数据。所以桶的个数的选择是看数据的大小,我们这里采用映射是m(桶编号)=n(数组的值)/10,则0-10的数据,采用1个桶就够了,免的浪费空间。

关键代码:

typedef struct Node
{
	int key;
	struct Node *next;
}KeyNode;
void bucketsort(int *arr, int n,int bucketsize)
{
	//arr为数组首地址,n为数组元素,bucketsize为桶的个数
	KeyNode **bucket=(KeyNode**)malloc(sizeof(KeyNode*)*bucketsize);
	for(int i=0;i<bucketsize;i++)
	{
		//初始化每一个桶
		bucket[i]=(KeyNode*)malloc(sizeof(KeyNode));
		bucket[i]->key=0;
		bucket[i]->next=NULL;
	}
	for(int j=0;j<n;j++)
	{
		KeyNode *node=(KeyNode*)malloc(sizeof(KeyNode));
		node->key=arr[j];
		node->next=NULL;
		int index=arr[j]/10;
		KeyNode *p=bucket[index];  //桶中链表的头结点
		if(p->key==0)
		{
			bucket[index]->next=node;
			bucket[index]->key++;
			
		}
		else
		{
			//对同一个桶的数据采用插入排序
			while(p->next!=NULL&&p->next->key<=node->key)
				p=p->next;
			node->next=p->next;
			p->next=node;
			bucket[index]->key++;
		}
	}
	//输出所有桶中的数据
    for(int k=0;k<bucketsize;k++)
		for(KeyNode *N=bucket[k]->next;N!=NULL;N=N->next)
			printf("%d\n",N->key);	
}
20000个数据的执行时间如下:


一口气讲了两种排序,下面继续讲基数排序

为什么叫做基数排序呢,因为我们要通过获取每一种进制他们的基数进行获取每一位的数值。,例如10进制的基数就是10.

基数排序的思想呢,是从数的低位开始对整个数组排序,首先排个位数,再排十位数,再排百位数,分别把每位数存到桶里面,收集,直接排到最高位,便可得有序数组。举个例子,例如我要排列一组数A[7]={110,123,301,20,10,1,2,11}

110,123,301,20,10,1,2,11,我们对它进行修饰一下,变成110,123,301,020,010,001,002,011

个位数:

0号桶   1号桶     2号桶      3号桶

110      301        002          123

020     001

010     011

把按照个位分配好的从零0号桶开始收集得,110,020,010,301,001,011,002,123

十位数:

0号桶   1号桶     2号桶      3号桶

301     110        020          

001     010       123

002     011

把按照10位分配好的从零0号桶开始收集得,301,001,002,110,010,011,020,123

百位数:

0号桶   1号桶     2号桶      3号桶

001      110                       301

002      123

010    

011

020

把按照百位分配好的从零0号桶开始收集得,001,002,010,011,020,110,123,301

得出有序数组。

因此我们要把每个数的每个分位进行取出来


求每个分位的函数:

int GetNumPos(int num,int pos)
{
	int temp=1;
	for(int i=0;i<pos;i++)
		temp*=10;
	return (num/temp)%10;
}
关键代码 :
#define LEN10 10
#define KEYNUM 10
void Radixsort(int *arr,int n)
{
	int *bucket[LEN10];
	for(int i=0;i<LEN10;i++)
	{
		bucket[i]=(int*)malloc(sizeof(int)*(n+1));
        bucket[i][0]=0;
	}
	for(int pos=1;pos<KEYNUM;pos++)
	{
		for(int i=0;i<n;i++)
		{
			int num=GetNumPos(arr[i],pos);
			int index=++bucket[num][0];
			//分配到指定编号的桶
			bucket[num][index]=arr[i];
		}
		for(int k=0,j=0;k<LEN10;k++)
		{
			//重新收集分配好的数据
			for(int c=1;c<=bucket[k][0];c++)
				arr[j++]=bucket[k][c];
			bucket[k][0]=0;
		}
	}
}

20000个数的排序时间情况如下:




由此看出最简单和最快的是计数排序,最复杂的是桶排序,但是他们都是属于分配排序的一种,因为受限制太多,因此用的比较少。




  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值