漫畫:什麼是桶排序?

2021-02-19 程序人生

點擊上方「程序人生」,選擇「置頂公眾號」

第一時間關注程序猿(媛)身邊的故事

作者

小灰

已獲原作者授權,如需轉載,請聯繫原作者。


—————  第二天  —————

————————————

讓我們先來回顧一下計數排序:

計數排序需要根據原始數列的取值範圍,創建一個統計數組,用來統計原始數列中每一個可能的整數值所出現的次數。

原始數列中的整數值,和統計數組的下標是一一對應的,以數列的最小值作為偏移量。比如原始數列的最小值是90, 那麼整數95對應的統計數組下標就是 95-90 = 5。

那麼,桶排序當中所謂的「桶」,又是什麼概念呢?

每一個桶(bucket)代表一個區間範圍,裡面可以承載一個或多個元素。桶排序的第一步,就是創建這些桶,確定每一個桶的區間範圍:

具體建立多少個桶,如何確定桶的區間範圍,有很多不同的方式。我們這裡創建的桶數量等於原始數列的元素數量,除了最後一個桶只包含數列最大值,前面各個桶的區間按照比例確定。

區間跨度 = (最大值-最小值)/ (桶的數量 - 1)

第二步,遍歷原始數列,把元素對號入座放入各個桶中:

第三步,每個桶內部的元素分別排序(顯然,只有第一個桶需要排序):

第四步,遍歷所有的桶,輸出所有元素:

0.5,0.84,2.18,3.25,4.5

到此為止,排序結束。

public static double[] bucketSort(double[] array){

   //1.得到數列的最大值和最小值,並算出差值d

   double max = array[0];

   double min = array[0];

   for(int i=1; i<array.length; i++) {

       if(array[i] > max) {

           max = array[i];

       }

       if(array[i] < min) {

           min = array[i];

       }

   }

   double d = max - min;

   //2.初始化桶

   int bucketNum = array.length;

   ArrayList<LinkedList<Double>> bucketList = new ArrayList<LinkedList<Double>>(bucketNum);

   for(int i = 0; i < bucketNum; i++){

       bucketList.add(new LinkedList<Double>());

   }

   //3.遍歷原始數組,將每個元素放入桶中

   for(int i = 0; i < array.length; i++){

       int num = (int)((array[i] - min)  * (bucketNum-1) / d);

       bucketList.get(num).add(array[i]);

   }

   //4.對每個通內部進行排序

   for(int i = 0; i < bucketList.size(); i++){

       //JDK底層採用了歸併排序或歸併的優化版本

       Collections.sort(bucketList.get(i));

   }

   //5.輸出全部元素

   double[] sortedArray = new double[array.length];

   int index = 0;

   for(LinkedList<Double> list : bucketList){

       for(double element : list){

           sortedArray[index] = element;

           index++;

       }

   }

   return sortedArray;

}

public static void main(String[] args) {

   double[] array = new double[] {4.12,6.421,0.0023,3.0,2.123,8.122,4.12, 10.09};

   double[] sortedArray = bucketSort(array);

   System.out.println(Arrays.toString(sortedArray));

}

代碼中,所有的桶保存在ArrayList集合當中,每一個桶被定義成一個鍊表(LinkedList<Double>),這樣便於在尾部插入元素。

定位元素屬於第幾個桶,是按照比例來定位:

(array[i] - min)  * (bucketNum-1) / d

同時,代碼使用了JDK的集合工具類Collections.sort來為桶內部的元素進行排序。Collections.sort底層採用的是歸併排序或Timsort,小夥伴們可以簡單地把它們當做是一種時間複雜度 O(nlogn)的排序。

假設原始數列有n個元素,分成m個桶(我們採用的分桶方式 m=n),平均每個桶的元素個數為n/m。

下面我們來逐步分析算法複雜度:

第一步求數列最大最小值,運算量為n。

第二步創建空桶,運算量為m。

第三步遍歷原始數列,運算量為n。

第四步在每個桶內部做排序,由於使用了O(nlogn)的排序算法,所以運算量為 n/m * log(n/m ) * m。

第五步輸出排序數列,運算量為n。

加起來,總的運算量為 3n+m+ n/m * log(n/m ) * m = 3n+m+n(logn-logm) 。

去掉係數,時間複雜度為:

O(n+m+n(logn-logm)) 

至於空間複雜度就很明顯了:

空桶佔用的空間 + 數列在桶中佔用的空間 = O(m+n)


有關計數排序的知識,可以看看這一篇漫畫:

漫畫:什麼是計數排序?

點擊文末閱讀全文,看『程序人生』其他精彩文章推薦

「若你有原創文章想與大家分享,歡迎投稿。」

加編輯微信ID,備註#投稿#:

程序 丨 druidlost  

小七 丨 duoshangshuang

推薦閱讀:

print_r('點個讚吧');
var_dump('點個讚吧');
NSLog(@"點個讚吧!")
System.out.println("點個讚吧!");
console.log("點個讚吧!");
print("點個讚吧!");
printf("點個讚吧!\n");
cout << "點個讚吧!" << endl;
Console.WriteLine("點個讚吧!");
fmt.Println("點個讚吧!")
Response.Write("點個讚吧");
alert(』點個讚吧』)

相關焦點

  • 不基於比較的排序——桶排序
    桶排序是時間複雜度為O(N),額外空間複雜度O(N)的一個排序。他是一個與原始數據狀況有很大關係的一個排序。所以實際中並不經常使用。但是我還是想給大家介紹一下桶排序。我在下一篇文章裡會借用到桶這個概念來解決一些算法問題。
  • golang實現桶排序
    本來這次是想分享一下跳表的go版本實現,後來發現跳表的實現有點複雜,想要完全搞清楚還是需要點時間,等後面文章再分享,這次就先分享桶排序了
  • ...選擇,插入,希爾,歸併,快排,堆排序,計數排序,桶排序,基數排序)
    無論是什麼數據進去,時間複雜度都是O(n*2)。數據規模越小越好,唯一的好處就是不佔用額外的空間。思路:在未排序序列中找到最小(大)元素,存放到排序序列的起始位置。然後再從剩餘未排序的數組匯總繼續尋找最小(大)元素,依次類推。
  • 漫畫:什麼是歸併排序?
    歸併排序和擂臺賽,有什麼相同和不同之處呢?讓我們以下面這個數組來舉例說明:2.歸併既然分了組,接下來就要開始「比武」了。歸併排序和擂臺賽有一個很大的不同,就是擂臺賽只需要決定誰是老大,而並不關心誰做老二和老三;歸併排序的要求複雜一些,需要確定每一個元素的排列位置。因此,當每個小組內部比較出先後順序以後,小組之間會展開進一步的比較和排序,合併成一個大組;大組之間繼續比較和排序,再合併成更大的組......最終,所有元素合併成了一個有序的集合。
  • 什麼是基數排序?
    但是還是有少數的排序算法沒寫,下面的一篇就是。這篇文章用漫畫的形式講解了基數排序,通俗易懂。什麼是計數排序呢?讓我們舉例說明一下。計數排序有什麼局限呢?
  • 動畫 | 什麼是基數排序?
    ,和桶排序一樣利用分布和收集兩種基本操作進行排序。基數排序是把每一個元素拆成多個關鍵字,一個關鍵字可以在每一個元素上同等的位置進行計數排序,一個元素拆成多個關鍵字可以看作是要進行幾輪分桶,以一個元素最長的長度為準。 基數排序可以看成多(單)關鍵字的排序,可以想像成桶排序那樣分桶排序,也可以像計數排序那樣歸約化分治。 基數排序的思想是將待排序序列中的每組關鍵字進行桶排序。
  • 百萬考生分數如何排序 - 計數排序
    百萬考生分數如何排序 - 計數排序其實計數排序是桶排序的一種特殊情況。 桶排序的核心思想是將要排序的數據分到幾個有序的桶裡,每個桶裡的數據再單獨進行排序。桶內排完序之後,再把每個桶裡的數據按照順序依次取出,組成的序列就是有序的了。
  • JS家的排序算法 十大經典算法排序總結
    冒泡排序還有一種優化算法,就是立一個flag,當在一趟序列遍歷中元素沒有發生交換,則證明該序列已經有序。但這種改進對於提升性能來說並沒有什麼太大作用。。。什麼時候最快(Best Cases):當輸入的數據已經是正序時(都已經是正序了,我還要你冒泡排序有何用啊。。。。)
  • 動畫:什麼是基數排序?
    基數排序 與基於比較的排序算法(歸併排序、堆排序、快速排序、冒泡排序、插入排序等等)相比,基於比較的排序算法的時間複雜度最好也就是 基數排序的總體思想就是從待排序數組當中,元素的最低有效位到最高有效位 逐位 進行比較排序;此外,基數排序使用計數排序作為一個排序的子過程。下面我們就以數組  [170, 45, 75, 90, 802, 24, 2, 66] ,快樂學習基數排序!
  • 一場排序引發的風波——計數排序(O(n)複雜度)
    或許上面的代碼你看起來還有點懵逼,但是不要緊,我們在這裡給你講明白什麼是計數排序。對於計數排序,百度百科是這麼說的:計數排序是一個非基於比較的排序算法,該算法於1954年由 Harold H. Seward 提出。它的優勢在於在對一定範圍內的整數排序時,它的複雜度為Ο(n+k)(其中k是整數的範圍),快於任何比較排序算法。
  • 排序算法之高效排序法
    高效排序算法桶排序桶排序 (Bucket sort)或所謂的箱排序,是一個排序算法,工作的原理是將數組分到有限數量的桶子裡。每個桶子再個別排序(有可能再使用別的排序算法或是以遞歸方式繼續使用桶排序進行排序)。桶排序原理介紹桶排序是計數排序的升級版。
  • 排序算法---基數排序,動圖詳解,Java實現!
    int[] bucketElementCounts = new int[10]; //桶中元素個數在此,我們還可以發現一個問題就是可能會存在空桶佔用空間資源,所以基數排序典型的佔用空間換取時間,文章末尾會對這種排序時間和空間複雜度的分析!
  • Python實現基數排序
    基數排序將數據按位進行分桶,然後將桶中的數據合併。每次分桶只關注其中一位數據,其他位的數據不管,最大的數據有多少位,就進行多少次分桶和合併。基數排序除了用於對整數進行排序,也可以用於對浮點數、字符串進行排序。基數排序可以分為最高位優先法和最低位優先法,兩種方法的結果相同。最高位優先(Most Significant Digit first)法,簡稱MSD法。
  • 經典排序算法全攻略
    今天小編就來和大家聊聊排序算法。在開始今天的話題前,我們先來get一下,到底什麼是排序呢?排序是指將雜亂無章的數據元素,通過一定的方法按關鍵字順序排列的過程。作為計算機內經常進行的一種操作,簡而言之,排序就是幫助它們回歸自己的正確位置。
  • 基數排序
    它是根據關鍵字中各位的值,通過對排序的N個元素進行若干趟「分配」與「收集」來實現排序的。 不妨通過一個具體的實例來展示一下,基數排序是如何進行的。 設有一個初始序列為: R {50, 123, 543, 187, 49, 30, 0, 2, 11, 100}。我們知道,任何一個阿拉伯數,它的各個位數上的基數都是以0~9來表示的。
  • 十大經典排序算法(下)
    桶排序 (Bucket sort)的工作的原理:假設輸入數據服從均勻分布,將數據分到有限數量的桶裡,每個桶再分別排序(有可能再使用別的排序算法或是以遞歸方式繼續使用桶排序進行排。人為設置一個BucketSize,作為每個桶所能放置多少個不同數值(例如當BucketSize==5時,該桶可以存放{1,2,3,4,5}這幾種數字,但是容量不限,即可以存放100個3);遍歷輸入數據,並且把數據一個一個放到對應的桶裡去;對每個不是空的桶進行排序,可以使用其它排序方法,也可以遞歸使用桶排序;注意,如果遞歸使用桶排序為各個桶排序,則當桶數量為1時要手動減小BucketSize增加下一循環桶的數量
  • 統治世界的十大排序算法!
    什麼時候最快當輸入的數據已經是正序時(都已經是正序了,我還要你冒泡排序有何用啊)。4. 什麼時候最慢當輸入的數據是反序時(寫一個 for 循環反序輸出數據不就行了,幹嘛要用你冒泡排序呢,我是閒的嗎)。5.
  • 十大經典排序算法最強總結
    (Selection Sort)表現最穩定的排序算法之一,因為無論什麼數據進去都是 O(n2) 的時間複雜度,所以用到它的時候,數據規模越小越好。3);遍歷輸入數據,並且把數據一個一個放到對應的桶裡去;對每個不是空的桶進行排序,可以使用其它排序方法,也可以遞歸使用桶排序;從不是空的桶裡把排好序的數據拼接起來。
  • 程式設計師必知必會的十大排序算法
    我們正常講的十大排序算法是內部排序,我們更多是基於「比較和非比較」這個維度將它們分為兩大類:「非比較類的有桶排序、基數排序、計數排序」。也有很多人將排序歸納為8大排序,那就是因為基數排序、計數排序是建立在桶排序之上或者是一種特殊的桶排序,但是基數排序和計數排序有它特有的特徵,所以在這裡就將它們分別列出來。
  • 常用排序算法之JavaScript實現
    桶排序 (Bucket sort)的工作的原理:假設輸入數據服從均勻分布,將數據分到有限數量的桶裡,每個桶再分別排序(有可能再使用別的排序算法或是以遞歸方式繼續使用桶排序進行排序)。設置一個定量的數組當作空桶;遍歷輸入數據,並且把數據一個一個放到對應的桶裡去;對每個不是空的桶進行排序;/*方法說明:桶排序@param array 數組@param num 桶的數量*/functionbucketSort(array,num){ if(array.length <=