桶排序(箱排序)

思想: 把区间[0,1)划分成n个相同大小的子区间,或称桶,然后将n个输入数分布到各个桶中去。因为输入数均匀分布在[0,1)上,所以一般不会有很多数落在 一个桶中的情况。为得到结果,先对各个桶中的数进行排序,然后按次序把各桶中的元素列 出来即可。

复杂度:平均情况下桶排序以线性时间运行。像计数排序一样,桶排序也对输入作了某种假设, 因而运行得很快。桶排序的时间复杂度,取决与对各个桶之间数据进行排序的时间复杂度,因为其它部分部分的时间复杂度都为O(n);很显然,桶划分的越小,各个桶之间的数据越少,排序所用的时间也会越少。但相应的空间消耗就会增大。

可以证明,即使选用插入排序作为桶内排序的方法,桶排序的平均时间复杂度为线性。具体证明,请参考算法导论。其空间复杂度也为线性。

假设有一组长度为N的待排关键字序列K[1....n],首先将这个序列划分成M个的子区间(桶) 。然后基于某种映射函数,将待排序列的关键字k映射到第i个桶中(即桶数组B的下标 i),那么该关键字k就作为B[i]中的元素(每个桶B[i]都是一组大小为N/M的序列)。接着对每个桶B[i]中的所有元素进行比较排序(可以使用快排)。然后依次枚举输出B[0]....B[M]中的全部内容即是一个有序序列。

比如考试分数通常为0-100分,我们可以建立11个桶,然后确定映射函数f(k)=k/10。则分数49将定位到第4个桶中(49/10=4)。(类似哈希表)


桶排序的f(k)值的计算,其作用就相当于快排中划分,已经把大量数据分割成了基本有序的数据块(桶)。然后只需要对桶中的少量数据做先进的比较排序即可。应该尽量做到以下两点:
(1) 映射函数f(k)能够将N个数据平均的分配到M个桶中,这样每个桶就有[N/M]个数据量。
(2) 尽量的增大桶的数量。极限情况下每个桶只能得到一个数据,这样就完全避开了桶内数据的“比较”排序操作。当然,做到这一点很不容易,数据量巨大的情况下,f(k)函数会使得桶集合的数量巨大,空间浪费严重。这就是一个时间代价和空间代价的权衡问题了。

伪代码:

利用C++ STL的vector容器,我们就可以很容易地实现桶排序。

代码:

// 桶排序
#include<iostream>
#include<vector>
#include<iterator>
using namespace std;
void BucketSort(int *pData, int size)
{
	vector<int> Bucket[11];//vector数组,每一个vector作为一个桶 
	memset(Bucket,0,sizeof(Bucket));
	int i,j,k,pos,key;
	for(i=0; i<size; ++i){ // 将每个元素插入到相应的桶中
		key=pData[i];
		pos = key/10;  // 求出该元素在哪个桶
		j=Bucket[pos].size()-1;
		Bucket[pos].push_back(key);  // 把该元素放入某个桶中
		while(j>=0 && Bucket[pos][j]>key){ // 用插入排序在某个桶里排序
			swap(Bucket[pos][j],Bucket[pos][j+1]);
			--j;
		}
	}
	//一次从每个桶中取出所以元素 
	k=0;
	for(i=0; i<11; ++i){
//		for(j=0; j<Bucket[i].size(); ++j)
//			pData[k++] = Bucket[i][j];
		vector<int>::const_iterator cit = Bucket[i].begin();
		for(; cit != Bucket[i].end(); ++cit)
			pData[k++] = *cit; 
	}
}
int main()
{
	int arr[]={3,5,45,34,2,78,67,34,56,98};
	BucketSort(arr,10);
	for(int i=0; i<10; ++i)
		printf("%d ",arr[i]);
	printf("\n");
	return 0;
}

 

结果:

QQ截图20140228231922

最后,对三种线性排序(计数排序,基数排序,桶排序)进行比较:

QQ截图20140228231652

从整体上来说,计数排序,桶排序都是非基于比较的排序算法,而其时间复杂度依赖于数据的范围,桶排序还依赖于空间的开销和数据的分布。而基数排序是一种对多元组排序的有效方法,具体实现要用到计数排序或桶排序。

相对于快速排序、堆排序等基于比较的排序算法,计数排序、桶排序和基数排序限制较多,不如快速排序、堆排序等算法灵活性好。但反过来讲,这三种线性排序算法之所以能够达到线性时间,是因为充分利用了待排序数据的特性,如果生硬得使用快速排序、堆排序等算法,就相当于浪费了这些特性,因而达不到更高的效率。

posted @ 2014-02-28 23:21  mickole  阅读(369)  评论(0)    收藏  举报