桶排序

基本思想:是将序列分到有限数量的桶子里。每个桶子再个别排序(有可能再使用别的排序算法或是以递回方式继续使用桶排序进行排序)。桶排序是鸽巢排序的一种归纳结果。当要被排序的序列内的数值是均匀分布的时候,桶排序使用线性时间(Θ(n))。但桶排序并不是比较排序,他不受到 O(n log n) 下限的影响。

简单来说,就是把数据分组,放在一个个的桶中,然后对每个桶里面的在进行排序。

例如要对大小为[1..1000]范围内的n个整数A[1..n]排序

首先,可以把桶设为大小为10的范围(该桶能装10个数字),具体而言,设集合B[1]存储[1..10]的整数,集合B[2]存储   (10..20]的整数,……集合B[i]存储(   (i-1)*10,   i*10]的整数,i   =   1,2,..100。总共有  100个桶。

然后,对A[1..n]从头到尾扫描一遍,把每个A[i]放入对应的桶B[j]中。  再对这100个桶中每个桶里的数字排序,这时可用冒泡,选择,乃至快排,一般来说任何排序法都可以。

最后,依次输出每个桶里面的数字,且每个桶中的数字从小到大输出,这样就得到所有数字排好序的一个序列了。

假设有n个数字,有m个桶,如果数字是平均分布的,则每个桶里面平均有n/m个数字。如果对每个桶中的数字采用快速排序,那么整个算法的复杂度是:

O(n   +   m   *   n/m*log(n/m))   =   O(n   +   nlogn   –   nlogm)

从上式看出,当m接近n的时候,桶排序复杂度接近O(n)

当然,以上复杂度的计算是基于输入的n个数字是平均分布这个假设的。这个假设是很强的  ,实际应用中效果并没有这么好。如果所有的数字都落在同一个桶中,那就退化成一般的排序了。

前面说的几大排序算法 ,大部分时间复杂度都是O(n2),也有部分排序算法时间复杂度是O(nlogn)。而桶式排序却能实现O(n)的时间复杂度。但桶排序的缺点是:

1)首先是空间复杂度比较高,需要的额外开销大。排序有两个数组的空间开销,一个存放待排序数组,一个就是所谓的桶,比如待排序值是从0到m-1,那就需要m个桶,这个桶数组就要至少m个空间。

2)其次待排序的元素都要在一定的范围内等等。

桶式排序是一种分配排序。分配排序的特点是不需要进行关键码的比较,但前提是要知道待排序列的一些具体情况。

分配排序的基本思想:说白了就是进行多次的桶式排序。

#include <iostream>  
using namespace std;  
  
/* 
------桶排序------- 
以下列程序进行: 
    设置一个定量的序列当作空桶子。 
    寻访序列,并且把数据一个一个放到对应的桶子去。 
    对每个不是空的桶子进行--(插入)排序--。 
    从不是空的桶子里把数据再放回原来的序列中。 
 
   适用于位数相同,均匀分布的数列 
   尤其例如: 
         13,52,16,31,64,16,24,34,82,67,43,56,68,46,90,84,98,26 
 
*/  
#define radix 10  
#define interval 10  
  
struct Bucket{  
    int key;  
    Bucket *next;  
    Bucket(){  
        key=NULL;  
        next=NULL;}  
};  
  
void bucket_sort(int A[],int len)  
{  
    Bucket buckets[radix];  
    int i;  
    Bucket *temp,*parent,*newNode;  
    for(i=0;i<len;i++)  
    {  
        temp = &buckets[A[i]/interval]; // A[i]/interval表示第A[i]/interval+1个桶        
        if(temp->key==NULL)//如果这个桶还是空的,就填入数据  
        {  
            temp->key = A[i];  
            cout<<"--1--A[i]:"<<A[i]<<endl;  
        }else{  
            if(A[i]<temp->key)             //在已存在数据的桶的最前端插入数据  
            {  
                newNode=new Bucket;  
                newNode->key=temp->key;  
                newNode->next=temp->next;  
                temp->key=A[i];  
                temp->next=newNode;  
                cout<<"--2--A[i]:"<<A[i]<<endl;  
            }else{                              //从后面插入排序  
                parent=temp;  
                temp=temp->next;  
                while(temp)  
                {  
                    if(A[i]<temp->key) break;  
                    parent = temp; //parent,temp指针往前移动 
                    temp=temp->next;  
                }  
                newNode=new Bucket;  
                newNode->key=A[i];  
                newNode->next=temp;  
                parent->next=newNode;  
                cout<<"--3--A[i]:"<<A[i]<<endl;  
            }  
        }  
    }  
    int j=0;  
    for(i=0;i<radix;i++)         //遍历每个桶 便得到有序数列  
    {  
        temp=&buckets[i];  
        while(temp && temp->key!=NULL)      //要加上temp->key 因为可能temp即使没有数据,其也不为空  
        {  
            A[j]=temp->key;  
            temp=temp->next;  
            j++;  
        }  
    }  
}  
int main()  
{  
    //int A[]={15,9,8,1,4,11,7,12,13,16,5,3,6,2,10,14};  
    int A[]={13,14,94,33,82,25,59,94,65,23,45,27,43,25,39,10,35,54,90,58};  
    int i;  
    int len=sizeof(A)/sizeof(int);   
    bucket_sort(A,len);   
    for(i=0;i<len;i++)  
    {  
        cout<<A[i]<<"  ";  
    }  
    cout<<endl;  
    return 0;  
} 


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值