內容介紹
桶排序簡介
前面學過計數排序,計數排序是一個非基於比較的排序算法,它的優點在於在對必定範圍內的整數排序時,它的複雜度爲Ο(n+k)。java
所謂桶就是存放多個數據的容器,桶排序也是非基於比較的排序算法,將待排序數據按照必定的規則存放到對應的桶中,再進行排序與合併。桶排序能夠對必定範圍內的數據包括小數進行排序。桶排序能夠突破基於比較排序算法的時間複雜度O(nlogn)。算法
桶排序的思想
將待排序數據分配到有限數量的桶裏。每一個桶再單獨進行子排序,最後按順序將多個桶中的數據進行合併,排序完成。當待排序的數組內的數值是均勻分配的時候,桶排序的時間複雜度爲O(n)。編程
桶排序動畫演示
假設有10我的給某個商品進行評分,評分的範圍是0~1之間的小數,例如{0.6, 0.85, 0.9, 0.5, 0.35, 0.2, 0.1, 0.85, 0.8, 0.5} 這10個數據。通常沒有特殊要求排序算法都是升序排序,小的在前,大的在後,效果以下: 數組
桶排序分析
經過上面動畫能夠看出桶排序分爲4個步驟:微信
- 劃分合適數量的桶。
- 將全部待排序數據放入到對應的桶中。
- 使用合理的算法對每一個非空桶進行子排序。
- 按順序將每一個桶中數據進行合併。
第一步:劃分合適數量的桶動畫
關於如何劃分合適數量的桶,根據不一樣規模和不一樣範圍的數據,咱們採起不一樣的劃分方式。目前數據都是0到1之間小數,數據比較均勻,咱們等分紅4個桶,每一個桶的範圍都是0.25,效果以下: 3d
第二步:將全部待排序數據放入到對應的桶中code
遍歷待排序數據,將每一個數據放入對應範圍的桶中,效果以下: blog
第三步:使用合理的算法對每一個非空桶進行子排序排序
待排序數據劃分到不一樣桶中後,每一個桶中的數據仍是無序的,以下圖所示: 從上圖能夠看到,第二個桶不用排序,其餘桶須要進行排序,分別對每一個桶中的數據再使用合適的排序算法,好比快速排序。排序後效果以下圖:
第四步:4.按順序將每一個桶中數據進行合併
合併前效果以下圖: 如今範圍小的桶在前面,範圍大的桶在後面,而且每一個桶中的數據也是從小到大排序的,所以咱們只須要從左往右將每一個桶中的數據取出放到數組中便可。取出第一個桶後的數據以下: 取出第二個桶後的數據以下:
取出第三個桶後的數據以下:
取出第四個桶後的數據以下:
當全部桶中的數據都取出後排序就完成了。
桶排序代碼編寫
public class BucketSort { public static void main(String[] args) { double[] arr = {0.6, 0.85, 0.9, 0.5, 0.35, 0.2, 0.1, 0.85, 0.8, 0.5}; bucketSort(arr); System.out.println("排序後: " + Arrays.toString(arr)); } public static void bucketSort(double[] arr) { // 獲取最大值和最小值 double max = arr[0]; double min = arr[0]; for (int i = 1; i < arr.length; i++) { double num = arr[i]; if (num > max) max = num; else if (num < min) min = num; } // 桶的數量 int bucketNumber = 4; // 每一個桶的範圍 double range = (max - min) / bucketNumber; // 1.初始化全部桶,每一個桶都是LinkedList,方便增長數據 LinkedList<Double>[] buckets = new LinkedList[bucketNumber]; for (int i = 0; i < buckets.length; i++) { buckets[i] = new LinkedList<>(); } // 2.將全部待排序數據放入到對應的桶中 for (int i = 0; i < arr.length; i++) { double num = arr[i]; int index = (int) ((num - min) / range); if (index == bucketNumber) index -= 1; // 若是這個數字正好是最大值,計算出索引就是number,會數組越界,放到最後一個桶中 buckets[index].add(num); } // 3.使用合理的算法對每一個非空桶進行子排序 for (int i = 0; i < buckets.length; i++) { // 對每一個桶中的數據進行排序 Collections.sort(buckets[i]); } // 4.按順序將每一個桶中數據進行合併 int index = 0; for (int i = 0; i < buckets.length; i++) { LinkedList<Double> bucket = buckets[i]; for (int j = 0; j < bucket.size(); j++) { arr[index] = bucket.get(j); index++; } } } }
桶排序的侷限性
若是數據通過桶的劃分以後,有些桶裏的數據很是多,有些很是少,很不平均,那桶內數據排序的時間複雜度就不是常量級了。在極端狀況下,若是數據都被劃分到一個桶裏,桶排序就退化爲O(nlogn)的排序算法,以下圖所示: 桶排序對待排序數據的要求是很是苛刻的,適用場景是在數據分佈相對比較均勻或者數據跨度範圍並非很大時。若是數據跨度很是大,空間消耗就會很大。因此桶排序不多使用。
桶排序比較適合用在外部排序中
所謂的外部排序就是數據存儲在外部磁盤中、數據量比較大、內存有限,沒法將數據所有加載到內存中。好比說咱們有50GB的訂單數據,咱們但願按訂單金額進行排序,可是咱們的內存有限只有幾GB,沒辦法一次性把50GB的數據都加載到內存中。假設訂單金額最小是0.1元,最大是10萬元。能夠將全部訂單根據金額劃分到100個桶裏,第一個桶存儲金額在0.1元到100元以內的訂單,第二桶存儲金額在101元到200元以內的訂單,依次類推。每個桶對應一個文件。訂單金額在0.1元到10萬元之間並不必定是均勻分佈的,因此50GB訂單數據是沒法均勻地被劃分到100個文件中的。有可能某個金額區間的數據特別多,劃分以後對應的文件很大,無法一次性讀入內存,針對某些劃分以後仍是比較大的文件,咱們能夠繼續劃分,好比訂單金額在0.1元到100元之間的比較多,咱們就將這個區間繼續劃分爲10個小區間,0.1元到10元,11元到20元,21元到30元依次類推,而後將每一個桶對應的數據讀取到內存中使用快速排序進行排序,結果保存在文件中,最後合併到已排序的文件中。
桶排序的複雜度
- 空間複雜度:數據規模爲n,劃分到k個桶中,總空間複雜度
O(n + k)
。 - 時間複雜度:1.獲取最大值和最小值,遍歷數組,操做次數爲n。2.初始化全部桶,操做次數爲k。3.將全部待排序數據放入到對應的桶中操做次數爲n。4.每一個非空桶進行子排序使用時間複雜度爲O(nlogn)的快速排序總操做次數爲(k/n)*log(k/n)*k。5.按順序將每一個桶中數據進行合併操做次數n。因此總的時間複雜度爲:3n+k+(k/n)*log(k/n)*k,所以時間複雜度爲:
O(n+k+logn-logk)
。 - 穩定性:穩定
總結
桶排序也是非基於比較的排序算法,將待排序數據按照必定的規則存放到對應的桶中,再進行排序與合併。桶排序能夠對必定範圍內的數據包括小數進行排序。桶排序能夠突破基於比較排序算法的時間複雜度O(nlogn)。
桶排序的思想:將待排序數據分配到有限數量的桶裏。每一個桶再單獨進行子排序,最後按順序將多個桶中的數據進行合併,排序完成。當要被排序的數組內的數值是均勻分配的時候,桶排序的時間複雜度爲O(n)。
桶排序對待排序數據的要求是很是苛刻的,適用場景是在數據分佈相對比較均勻或者數據跨度範圍並非很大時。若是數據跨度很是大,空間消耗就會很大,若是數據都被劃分到一個桶裏,桶排序就退化爲O(nlogn)的排序算法,因此桶排序不多使用。
桶排序比較適合用在外部排序中。
原創文章和動畫製做真心不易,您的點贊就是最大的支持! 想了解更多文章請關注微信公衆號:表哥動畫學編程