zoukankan      html  css  js  c++  java
  • 桶排序算法

    桶排序的基本思想

    桶排序利用函数的映射关系,将待排序的数组分成了N个块(桶)。实际上,桶排序的f(k)值的计算,其作用就相当于快排中划分,已经把大量数据切割成了基本有序的数据块(桶)。

    然后仅仅须要对每一个桶中的少量数据做比較排序(比較排序:即在比較的基础上进行交换。达到排序效果)就可以。

    假如待排序列K= {49、 38 、 35、 97 、 76、 73 、 27、 49 }。

    这些数据所有在1—100之间。因此我们定制10个桶。然后确定映射函数f(k)=(k*10)/(k.max)。则第一个keyword49将定位到第4个桶中(49*10/97=5)。依次将所有keyword所有堆入桶中,并在每一个非空的桶中进行高速排序。例如以下图所看到的。


    待排序的数组为:


    那么接下来仅仅要对这些子数组排序就可以。

    有用范围:

    数组的数必须是正数,但我们能够通过对每一个数加上一个值a,让它变为正数。排序完毕后再减去a。

    时间复杂度:

    (1) 循环计算每一个keyword的桶映射函数。这个时间复杂度是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) 尽量的增大桶的数量。极限情况下每一个桶仅仅能得到一个数据。这样就全然避开了桶内数据的“比較”排序操作。当然,做到这一点非常不easy,数据量巨大的情况下。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有着非常大的相似之处。

  • 相关阅读:
    Jessica's Reading Problem POJ
    FatMouse and Cheese HDU
    How many ways HDU
    Humble Numbers HDU
    Doing Homework again
    Stacks of Flapjacks UVA
    Party Games UVA
    24. 两两交换链表中的节点
    面试题 03.04. 化栈为队
    999. 可以被一步捕获的棋子数
  • 原文地址:https://www.cnblogs.com/gcczhongduan/p/5215938.html
Copyright © 2011-2022 走看看