【每日算法】桶排序算法
1)算法簡介
桶排序 (Bucket sort)或所謂的箱排序,是一個排序算法,工作的原理是將數組分到有限數量的桶子里。每個桶子再個別排序(有可能再使用別的排序算法或是以遞歸方式繼續使用桶排序進行排序)。
桶排序是穩定的,且在大多數情況下常見排序里最快的一種,比快排還要快,缺點是非常耗空間,基本上是最耗空間的一種排序算法,而且只能在某些情形下使用。
2)算法描述和分析
桶排序具體算法描述如下:
1、設置一個定量的數組當作空桶子。
2、尋訪串行,并且把項目一個一個放到對應的桶子去。
3、對每個不是空的桶子進行排序。
4、從不是空的桶子里把項目再放回原來的串行中。
桶排序最好情況下使用線性時間O(n),很顯然桶排序的時間復雜度,取決與對各個桶之間數據進行排序的時間復雜度,因為 其它部分的時間復雜度都為O(n);很顯然,桶劃分的越小,各個桶之間的數據越少,排 序所用的時間也會越少。但相應的空間消耗就會增大。
可以證明,即使選用插入排序作為桶內排序的方法,桶排序的平均時間復雜度為線性。 具體證明,請參考算法導論。其空間復雜度也為線性。
3)算法圖解、flash演示、視頻演示
圖解
Flash:
可以參考http://ds.fzu.edu.cn/fine/resources/FlashContent.asp?id=90中的過程
視頻:
這里就不給出桶排序的視頻了,見上flash吧
4)算法代碼
#include <time.h> #include <iostream> #include <iomanip> using namespace std; /*initial arr*/ void InitialArr(double *arr,int n) { srand((unsigned)time(NULL)); for (int i = 0; i<n;i++) { arr[i] = rand()/double(RAND_MAX+1); //(0.1) } } /* print arr*/ void PrintArr(double *arr,int n) { for (int i = 0;i < n; i++) { cout<<setw(15)<<arr[i]; if ((i+1)%5 == 0 || i == n-1) { cout<<endl; } } } void BucketSort(double * arr,int n) { double **bucket = new double*[10]; for (int i = 0;i<10;i++) { bucket[i] = new double[n]; } int count[10] = {0}; for (int i = 0 ; i < n ; i++) { double temp = arr[i]; int flag = (int)(arr[i]*10); //flag標識小樹的第一位 bucket[flag][count[flag]] = temp; //用二維數組的每個向量來存放小樹第一位相同的數據 int j = count[flag]++; /* 利用插入排序對每一行進行排序 */ for(;j > 0 && temp < bucket[flag][j - 1]; --j) { bucket[flag][j] = bucket[flag][j-1]; } bucket[flag][j] =temp; } /* 所有數據重新鏈接 */ int k=0; for (int i = 0 ; i < 10 ; i++) { for (int j = 0 ; j< count[i];j++) { arr[k] = bucket[i][j]; k++; } } for (int i = 0 ; i<10 ;i++) { delete bucket[i]; bucket[i] =NULL; } delete []bucket; bucket = NULL; } void main() { double *arr=new double[10]; InitialArr(arr, 10); BucketSort(arr, 10); PrintArr(arr,10); delete [] arr; }5)考察點、重點和頻度分析
桶排序是一種很巧妙的排序方法,在處理密集型數排序的時候有比較好的效果(主要是這種情況下空間復雜度不高),其思想也可用在很多算法題上,詳見后續筆試面試算法例題。
6)筆試面試題
例題1、一年的全國高考考生人數為500 萬,分數使用標準分,最低100 ,最高900 ,沒有小數,你把這500 萬元素的數組排個序。
對500W數據排序,如果基于比較的先進排序,平均比較次數為O(5000000*log5000000)≈1.112億。但是我們發現,這些數據都有特殊的條件: 100=<score<=900。那么我們就可以考慮桶排序這樣一個“投機取巧”的辦法、讓其在毫秒級別就完成500萬排序。
創建801(900-100)個桶。將每個考生的分數丟進f(score)=score-100的桶中。這個過程從頭到尾遍歷一遍數據只需要500W次。然后根據桶號大小依次將桶中數值輸出,即可以得到一個有序的序列。而且可以很容易的得到100分有人,501分有人。
實際上,桶排序對數據的條件有特殊要求,如果上面的分數不是從100-900,而是從0-2億,那么分配2億個桶顯然是不可能的。所以桶排序有其局限性,適合元素值集合并不大的情況。
例題2、在一個文件中有 10G 個整數,亂序排列,要求找出中位數。內存限制為 2G。只寫出思路即可(內存限制為 2G的意思就是,可以使用2G的空間來運行程序,而不考慮這臺機器上的其他軟件的占用內存)。
分析: 既然要找中位數,很簡單就是排序的想法。那么基于字節的桶排序是一個可行的方法。
思想:將整型的每1byte作為一個關鍵字,也就是說一個整形可以拆成4個keys,而且最高位的keys越大,整數越大。如果高位keys相同,則比較次高位的keys。整個比較過程類似于字符串的字典序。按以下步驟實施:
1、把10G整數每2G讀入一次內存,然后一次遍歷這536,870,912即(102410241024)*2 /4個數據。每個數據用位運算">>"取出最高8位(31-24)。這8bits(0-255)最多表示255個桶,那么可以根據8bit的值來確定丟入第幾個桶。最后把每個桶寫入一個磁盤文件中,同時在內存中統計每個桶內數據的數量,自然這個數量只需要255個整形空間即可。
2、繼續以內存中的整數的次高8bit進行桶排序(23-16)。過程和第一步相同,也是255個桶。
3、一直下去,直到最低字節(7-0bit)的桶排序結束。我相信這個時候完全可以在內存中使用一次快排就可以了。
例題3、給定n個實數x1,x2,...,xn,求這n個實數在實軸上相鄰2個數之間的最大差值M,要求設計線性的時間算法
典型的最大間隙問題。
要求線性時間算法。需要使用桶排序。桶排序的平均時間復發度是O(N).如果桶排序的數據分布不均勻,假設都分配到同一個桶中,最壞情況下的時間復雜度將變為O(N^2).
桶排序: 最關鍵的建桶,如果桶設計得不好的話桶排序是幾乎沒有作用的。通常情況下,上下界有兩種取法,第一種是取一個10^n或者是2^n的數,方便實現。另一種是取數列的最大值和最小值然后均分作桶。
對于這個題,最關鍵的一步是:由抽屜原理知:最大差值M>= (Max(V[n])-Min(V[n]))/(n-1)!所以,假如以(Max(V[n])-Min(V[n]))/(n-1)為桶寬的話,答案一定不是屬于同一個桶的兩元素之差。因此,這樣建桶,每次只保留桶里面的最大值和最小值即可。
代碼如下:
//距離平均值為offset = (arrayMax - arrayMin) / (n - 1), 則距離最大的數必然大于這個值 //每個桶只要記住桶中的最大值和最小值,依次比較上一個桶的最大值與下一個桶的最小值的差值,找最大的即可. #include <iostream> #define MAXSIZE 100 //實數的個數 #define MAXNUM 32767 using namespace std; struct Barrel { double min; //桶中最小的數 double max; //桶中最大的數 bool flag; //標記桶中有數 }; int BarrelOperation(double* array, int n) { Barrel barrel[MAXSIZE]; //實際使用的桶 int nBarrel = 0; //實際使用桶的個數 Barrel tmp[MAXSIZE]; //臨時桶,用于暫存數據 double arrayMax = -MAXNUM, arrayMin = MAXNUM; for(int i = 0; i < n; i++) { if(array[i] > arrayMax) arrayMax = array[i]; if(array[i] < arrayMin) arrayMin = array[i]; } double offset = (arrayMax - arrayMin) / (n - 1); //所有數的平均間隔 //對桶進行初始化 for(i = 0; i < n; i++) { tmp[i].flag = false; tmp[i].max = arrayMin; tmp[i].min = arrayMax; } //對數據進行分桶 for(i = 0; i < n; i++) { int pos = (int)((array[i] - arrayMin) / offset); if(!tmp[pos].flag) { tmp[pos].max = tmp[pos].min = array[i]; tmp[pos].flag = true; } else { if(array[i] > tmp[pos].max) tmp[pos].max = array[i]; if(array[i] < tmp[pos].min) tmp[pos].min = array[i]; } } for(i = 0; i <= n; i++) { if(tmp[i].flag) barrel[nBarrel++] = tmp[i]; } int maxOffset = 0.0; for(i = 0; i < nBarrel - 1; i++) { if((barrel[i+1].min - barrel[i].max) > maxOffset) maxOffset = barrel[i+1].min - barrel[i].max; } return maxOffset; } int main() { double array[MAXSIZE] = {1, 8, 6, 11, 7, 13, 16, 5}; //所需處理的數據 int n = 8; //數的個數 //double array[MAXSIZE] = {8, 6, 11}; //int n = 3; int maxOffset = BarrelOperation(array, n); cout << maxOffset << endl; return 0; }轉載于:https://www.cnblogs.com/shih/p/6660275.html
《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀總結
以上是生活随笔為你收集整理的【每日算法】桶排序算法的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: [No0000D2]ClearCShar
- 下一篇: 杭州西湖边的海洋馆叫什么?