标签:桶排序
桶排序的基本思想
桶排序利用函数的映射关系,将待排序的数组分成了N个块(桶)。实际上,桶排序的f(k)值的计算,其作用就相当于快排中划分,已经把大量数据分割成了基本有序的数据块(桶)。然后只需要对每个桶中的少量数据做比较排序(比较排序:即在比较的基础上进行交换,达到排序效果)即可。
假如待排序列K= {49、 38 、 35、 97 、 76、 73 、 27、 49 }。这些数据全部在1—100之间。因此我们定制10个桶,然后确定映射函数f(k)=(k*10)/(k.max)。则第一个关键字49将定位到第4个桶中(49*10/97=5)。依次将所有关键字全部堆入桶中,并在每个非空的桶中进行快速排序,如下图所示。
待排序的数组为:
那么接下来只要对这些子数组排序即可。
实用范围:
数组的数必须是正数,但我们可以通过对每个数加上一个值a,让它变为正数,排序完成后再减去a。
时间复杂度:
(1) 循环计算每个关键字的桶映射函数,这个时间复杂度是O(N)。
(2) 利用先进的比较排序算法对每个桶内的所有数据进行排序,其时间复杂度为:对于N个待排数据,M个桶,平均每个桶[N/M]个数据的桶排序平均时间复杂度为:O(N)+O(M*(N/M)*log(N/M)) = O(N+N*(logN-logM)) = O(N+N*logN-N*logM)。
提高算法在于:
(a) 映射函数f(k)能够将N个数据平均的分配到M个桶中,这样每个桶就有[N/M]个数据量。当N=M时,即极限情况下每个桶只有一个数据时。桶排序的最好效率能够达到O(N),最坏(所有元素在一个桶中)。
(b) 尽量的增大桶的数量。极限情况下每个桶只能得到一个数据,这样就完全避开了桶内数据的“比较”排序操作。当然,做到这一点很不容易,数据量巨大的情况下,f(k)函数会使得桶集合的数量巨大,空间浪费严重。这就是一个时间代价和空间代价的权衡问题了。
C++代码:
#include <iostream> using namespace std; namespace mySort { float Max(float *array, int begin,int end) {//获取数组中最大的数 float ret = -1; for (int i = begin; i <= end; ++i) ret = (ret < array[i]) ? array[i] : ret; return ret; } int getIndex(float a, float max) {//获取桶的索引位置。 return (int) ((a * 10) / max); } void insertSort(float * array, int begin, int end) { for (int i = begin; i < end; ++i) { int j = i + 1 ; float tmp = array[j]; for (; j > 0;j--) { if (tmp < array[j - 1]) array[j] = array[j - 1]; else break; } array[j] = tmp; } } void radixSorting(float array[], int begin, int end) { int arraySize = end - begin + 1; float max = Max(array, begin, end); const int countSize = 11; //(N * 10 / M && N < M ) 有 11 种可能 int count[countSize]; float * temp = new float[arraySize]; memset((void*)count,0, sizeof(count)); //初始化为0 for (int i = begin; i <= end; ++i) //记录每个桶中的元素个数 { count[getIndex(array[i], max)] += 1; } for (int i = 0; i < countSize-1; ++i) { count[i + 1] += count[i]; } for (int i = end; i >= begin; --i) { int j = getIndex(array[i], max); temp[count[j]-1] = array[i]; --count[j]; } for (int i = 0; i < countSize - 1 ; ++i) { if (count[i] < count[i + 1]) { mySort::insertSort(temp, count[i], count[i + 1] - 1); } } memcpy((void*)array, (void*)temp, arraySize*sizeof(float)); delete [] temp; } }; int main() { float a[] = { 49, 38 , 35, 97 , 76, 73 , 27, 49 }; int length = sizeof(a) / sizeof(float); // mySort::insertSort(a, 0, length - 2); mySort::radixSorting(a, 0, length - 1); return 0; }
总结:桶排序和计数排序有着惊人的相似之处。计数排序将每个元素投射于数组的每个空间,但有着:必须是整数,并且空间耗费大,与具体待排序的数有关。而桶排序则用更小的空间(O(N)且与具体待排序数无关),只记录了每个桶的索引,但需要对每个桶的数据进行排序,适用范围也更广泛。计数排序这种用空间换时间的方法和Hash有着很大的相似之处。
标签:桶排序
原文地址:http://blog.csdn.net/denglfs/article/details/38445549