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