桶排序(Bucket sort)或所谓的箱排序,并不是比较排序,它不受到 O(nlogn) 下限的影响。
一、算法基本思想
(1)基本思想
桶排序工作的原理是将数组分到有限数量的桶子里,每个桶子再个别排序,有可能再使用别的排序算法或是以递归方式继续使用桶排序进行排序。桶排序是鸽巢排序的一种归纳结果。当要被排序的数组内的数值是均匀分配的时候,桶排序使用线性时间O(n)。
(2)运行过程
桶排序算法的运行过程如下:
1、设置一个定量的数组当作空桶子。
2、寻访序列,并且把项目一个一个放到对应的桶子去。
3、对每个不是空的桶子进行排序。
4、从不是空的桶子里把项目再放回原来的序列中。
(3)示例
二、算法实现(核心代码)
C++实现:假设数据分布在[0,100)之间,每个桶内部用链表表示,在数据入桶的同时插入排序。然后把各个桶中的数据合并。
#include<iterator> #include<iostream> #include<vector> using namespace std; const int BUCKET_NUM = 10;struct ListNode{explicit ListNode(int i=0):mData(i),mNext(NULL){}ListNode* mNext;int mData; };ListNode* insert(ListNode* head,int val){ListNode dummyNode;ListNode *newNode = new ListNode(val);ListNode *pre,*curr;dummyNode.mNext = head;pre = &dummyNode;curr = head;while(NULL!=curr && curr->mData<=val){pre = curr;curr = curr->mNext;}newNode->mNext = curr;pre->mNext = newNode;return dummyNode.mNext; }ListNode* Merge(ListNode *head1,ListNode *head2){ListNode dummyNode;ListNode *dummy = &dummyNode;while(NULL!=head1 && NULL!=head2){if(head1->mData <= head2->mData){dummy->mNext = head1;head1 = head1->mNext;}else{dummy->mNext = head2;head2 = head2->mNext;}dummy = dummy->mNext;}if(NULL!=head1) dummy->mNext = head1;if(NULL!=head2) dummy->mNext = head2;return dummyNode.mNext; }void BucketSort(int n,int arr[]){vector<ListNode*> buckets(BUCKET_NUM,(ListNode*)(0));for(int i=0;i<n;++i){int index = arr[i]/BUCKET_NUM;ListNode *head = buckets.at(index);buckets.at(index) = insert(head,arr[i]);}ListNode *head = buckets.at(0);for(int i=1;i<BUCKET_NUM;++i){head = Merge(head,buckets.at(i));}for(int i=0;i<n;++i){arr[i] = head->mData;head = head->mNext;} }
三、性能(算法时间、空间复杂度、稳定性)分析
假设有n个数字,有m个桶,如果数字是平均分布的,则每个桶里面平均有n/m个数字。如果 对每个桶中的数字采用快速排序,那么整个算法的复杂度是 O(n + m * (n/m) * log(n/m)) =O(n + nlogn – nlogm) 。
从上式看出,当m接近n的时候,桶排序时间复杂度接近O(n)。当然,以上复杂度的计算是基于输入的n个数字是平均分布这个假设的。这个假设是很强的 ,实际应用中效果并没有这么好。
如果所有的数字都落在同一个桶中,那就退化成一般的排序了。 前面说的几大排序算法 ,大部分时间复杂度都是O(n^2),也有部分排序算法时间复杂度是O(nlogn)。而桶式排序却能实现O(n)的时间复杂度。
但桶排序的缺点是: 1)首先是空间复杂度比较高,需要的额外开销大。排序有两个数组的空间开销,一个存放待排序数组,一个就是所谓的桶,比如待排序值是从0到m-1,那就需要m个桶,这个桶数组就要至少m个空间。 2)其次待排序的元素都要在一定的范围内等等。
通排序的空间复杂度为O(n+m)。
桶排序的稳定性依赖于桶内排序。如果我们使用了快排,显然,算法是不稳定的。