排序之桶排序

桶排序 (Bucket sort)或所谓的箱排序,是一个排序算法,工作的原理是将数组分到有限数量的桶子里。每个桶子再个别排序(有可能再使用别的排序算法或是以递归方式继续使用桶排序进行排序)。桶排序是鸽巢排序的一种归纳结果。当要被排序的数组内的数值是均匀分配的时候,桶排序使用线性时间(Θn))。但桶排序并不是 比较排序,他不受到 O(n log n) 下限的影响。

中文名
桶排序
要    求
数据的长度必须完全一样
公    式
Data=rand()/10000+10000
数据结构设计
链表可以采用很多种方式实现
性    质
平均情况下桶排序以线性时间运行
原    理
桶排序利用函数的映射关系
领    域
计算机算法




假定:输入是由一个随机过程产生的[0, 1)区间上均匀分布的实数。将区间[0, 1)划分为n个大小相等的子区间(桶),每桶大小1/n:[0, 1/n), [1/n, 2/n), [2/n, 3/n),…,[k/n, (k+1)/n ),…将n个输入元素分配到这些桶中,对桶中元素进行排序,然后依次连接桶输入0 ≤A[1..n] <1辅助数组B[0..n-1]是一指针数组,指向桶(链表)。

数据结构设计:链表可以采用很多种方式实现,通常的方法是动态申请内存建立结点,但是针对这个算法,桶里面的链表结果每次扫描后都不同,就有很多链表的分离和重建。如果使用动态分配内存,则由于指针的使用,安全性低。所以,笔者设计时使用了数组来模拟链表(当然牺牲了部分的空间,但是操作却是简单了很多,稳定性也大大提高了)。共十个桶,所以建立一个二维数组,行向量的下标0—9代表了10个桶,每个行形成的一维数组则是桶的空间。

平均情况下桶排序以线性时间运行。像基数排序一样,桶排序也对输入作了某种假设, 因而运行得很快。具 体来说,基数排序假设输入是由一个小范围内的整数构成,而桶排序则 假设输入由一个随机过程产生,该过程将元素一致地分布在区间[0,1)上。 桶排序的思想就是把区间[0,1)划分成n个相同大小的子区间,或称桶,然后将n个输入数分布到各个桶中去。因为输入数均匀分布在[0,1)上,所以一般不会有很多数落在一个桶中的情况。为得到结果,先对各个桶中的数进行排序,然后按次序把各桶中的元素列出来即可。
在桶排序算法的代码中,假设输入是含n个元素的 数组A,且每个元素满足0≤ A[i]<1。另外还需要一个辅助 数组B[O..n-1]来存放 链表实现的桶,并假设可以用某种机制来维护这些表。

桶排序利用函数的映射关系,减少了几乎所有的比较工作。实际上,桶排序的f(k)值的计算,其作用就相当于快排中划分,已经把大量 数据分割成了基本有序的 数据块(桶)。然后只需要对桶中的少量数据做先进的比较排序即可。
对N个 关键字进行桶排序的 时间复杂度分为两个部分:
(1) 循环计算每个关键字的桶映射函数,这个 时间复杂度是O(N)。
(2) 利用先进的比较 排序算法对每个桶内的所有数据进行排序,其 时间复杂度为 ∑ O(Ni*logNi) 。其中Ni 为第i个桶的数据量。
很显然,第(2)部分是桶排序性能好坏的决定因素。尽量减少桶内数据的数量是提高效率的唯一办法(因为基于比较排序的最好平均 时间复杂度只能达到O(N*logN)了)。因此,我们需要尽量做到下面两点:
(1) 映射函数f(k)能够将N个数据平均的分配到M个桶中,这样每个桶就有[N/M]个数据量。
(2) 尽量的增大桶的数量。极限情况下每个桶只能得到一个数据,这样就完全避开了桶内数据的“比较”排序操作。 当然,做到这一点很不容易,数据量巨大的情况下,f(k)函数会使得桶集合的数量巨大,空间浪费严重。这就是一个时间代价和空间代价的权衡问题了。
对于N个待排数据,M个桶,平均每个桶[N/M]个数据的桶排序平均 时间复杂度为:
O(N)+O(M*(N/M)*log(N/M))=O(N+N*(logN-logM))=O(N+N*logN-N*logM)
当N=M时,即极限情况下每个桶只有一个数据时。桶排序的最好效率能够达到O(N)。
总结:桶排序的平均 时间复杂度为线性的O(N+C),其中C=N*(logN-logM)。如果相对于同样的N,桶数量M越大,其效率越高,最好的 时间复杂度达到O(N)。当然桶排序的 空间复杂度为O(N+M),如果输入数据非常庞大,而桶的数量也非常多,则空间代价无疑是昂贵的。此外,桶排序是稳定的。

源码

编辑
io.open();//打开控制台
/**-------------------------------------------------------* 桶排序**------------------------------------------------------*/
/*
桶排序假设输入元素均匀而独立分布在区间[0,1) 即 0 <= x and x < 1;将区间划分成n个相同大小的子区间(桶),然后将n个输入按大小分布到各个桶中去,对每个桶内部进行排序。最后将所有桶的排序结果合并起来.
*/
//插入排序算法
insert_sort = function( array ){
for( right=2;#array ) {
var top = array[right];
//Insert array[right] into the sorted seqquence array[1....right-1]
var left = right -1;
while( left and array[left]>top){
array[left+1] = array[left];
left--;
}
array[left+1] = top;
}
return array;
}
//桶 排序算法
bucket_sort = function( array ){
var n = #array;
var bucket ={}
for(i=0;n;1){
bucket[i] = {} //创建一个桶
}
var bucket_index
for(i=1;n;1){
bucket_index = math.floor(n * array[i]);
table.push( bucket [ bucket_index ],array[i] );//放到桶里去
}
for(i=1;n;1){
insert_sort( bucket[i] ); //对每个桶进行 插入排序
}
return table.concat( table.unpack(bucket) )
}
io.print("----------------")
io.print("桶排序")
io.print("----------------")
array={};
//桶排序假设输入是由一个随机过程产生的小数.
math.randomize()
for(i=1;20;1){
table.push( array,math.random() )
}
//排序
array = bucket_sort( array )
//输出结果
for(i=1;#array;1){
io.print( array[i] )
}
execute("pause") //按 任意键继续
io.close();//关闭控制台

C++

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
#include<iostream>
usingnamespace std;
int  a[]={1,255,8,6,25,47,14,35,58,75,96,158,657};
const  int  len= sizeof (a)/ sizeof ( int );
int  b[10][len+1]={0}; //将b全部置0
void  bucketSort( int  a[]); //桶排序函数
void  distribute Elments( int  a[], int  b[10][len+1], int  digits);
void  collectElments( int  a[], int  b[10][len+1]);
int  numOfDigits( int  a[]);
void  zeroBucket( int  b[10][len+1]); //将b数组中的全部元素置0
int  main()
{
cout<< "原始数组:" ;
for ( int  i=0;i<len;i++)
cout<<a[i]<< "," ;
cout<<endl;
bucketSort(a);
cout<< "排序后数组:" ;
for ( int  i=0;i<len;i++)
cout<<a[i]<< "," ;
cout<<endl;
return  0;
}
void  bucketSort( int  a[])
{
int  digits=numOfDigits(a);
for ( int  i=1;i<=digits;i++)
{
distributeElments(a,b,i);
collectElments(a,b);
if (i!=digits)
zeroBucket(b);
}
}
int  numOfDigits( int  a[])
{
int  largest=0;
for ( int  i=0;i<len;i++) //获取最大值
if (a[i]>largest)
largest=a[i];
int  digits=0; //digits为最大值的位数
while (largest)
{
digits++;
largest/=10;
}
return  digits;
}
void  distributeElments( int  a[], int  b[10][len+1], int  digits)
{
int  divisor=10; //除数
for ( int  i=1;i<digits;i++)
divisor*=10;
for ( int  j=0;j<len;j++)
{
int  numOfDigist=(a[j]%divisor-a[j]%(divisor/10))/(divisor/10);
//numOfDigits为相应的(divisor/10)位的值,如当divisor=10时,求的是个位数
int  num=++b[numOfDigist][0]; //用b中第一列的元素来储存每行中元素的个数
b[numOfDigist][num]=a[j];
}
}
void  collectElments( int  a[], int  b[10][len+1])
{
int  k=0;
for ( int  i=0;i<10;i++)
for ( int  j=1;j<=b[i][0];j++)
a[k++]=b[i][j];
}
void  zeroBucket( int  b[][len+1])
{
for ( int  i=0;i<10;i++)
for ( int  j=0;j<len+1;j++)
b[i][j]=0;
}

JAVA

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public  static  void  basket( int  data[]) //data为待排序数组
{
int  n=data.length;
int  bask[][]= new  int [ 10 ][n];
int  index[]= new  int [ 10 ];
int  max=Integer.MIN_VALUE;
for ( int  i= 0 ;i<n;i++)
{
max=max>(Integer.toString(data[i]).length())?max:(Integer.toString(data[i]).length());
}
String str;
for ( int  i=max- 1 ;i>= 0 ;i--)
{
for ( int  j= 0 ;j<n;j++)
{
str= "" ;
if (Integer.toString(data[j]).length()<max)
{
for ( int  k= 0 ;k<max-Integer.toString(data[j]).length();k++)
str+= "0" ;
}
str+=Integer.toString(data[j]);
bask[str.charAt(i)- '0' ][index[str.charAt(i)- '0' ]++]=data[j];
}
int  pos= 0 ;
for ( int  j= 0 ;j< 10 ;j++)
{
for ( int  k= 0 ;k<index[j];k++)
{
data[pos++]=bask[j][k];
}
}
for (intx= 0 ;x< 10 ;x++)index[x]= 0 ;
}
}



桶排序有其局限性,适合元素值集合并不大的情况。‘


以上实现复杂,目前竞赛只用到一维数组的桶排序,复杂度O(M+N),M为桶数,N为元素个数(见啊哈算法)


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值