(戀上資料結構筆記):計數排序、基數排序 、桶排序
目錄
計數排序(Counting Sort)
- 之前學習的冒泡、選擇、插入、歸併、快速、希爾、堆排序,都是基於比較的排序
- 平均時間複雜度目前最低是O(nlogn)
- 計數排序、桶排序、基數排序,都不是基於比較的排序它們是典型的用空間換時間,在某些時候,平均時間複雜度可以比O(nlogn)更低
- 計數排序於1954年由Harold H.Seward提出,適合對一定範圍內的整數進行排序
- 計數排序的核心思想
- 統計每個整數在序列中出現的次數,進而推導出每個整數在有序序列中的索引
計數排序 - 最簡單的實現
// 找出最大值 int max = array[0]; for (int i = 1; i < array.length; i++) { if (array[i] > max) { max = array[i]; } } // O(n) // 開闢記憶體空間,儲存每個整數出現的次數 int[] counts = new int[1 + max]; // 統計每個整數出現的次數 for (int i = 0; i < array.length; i++) { counts[array[i]]++; } // O(n) // 根據整數的出現次數,對整數進行排序 int index = 0; for (int i = 0; i < counts.length; i++) { while (counts[i]-- > 0) { array[index++] = i; } } // O(n)
- 這個版本的實現存在以下問題
- 無法對負整數進行排序
- 極其浪費記憶體空間
- 是個不穩定的排序
計數排序 - 改進思路
- 逆序遍歷可以保持穩定性
- 依次類推
改進版程式碼實現
// 找出最值 int max = array[0]; int 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]; } } // 開闢記憶體空間,儲存次數 int[] counts = new int[max - min + 1]; // 統計每個整數出現的次數 for (int i = 0; i < array.length; i++) { counts[array[i] - min]++; } // 累加次數 for (int i = 1; i < counts.length; i++) { counts[i] += counts[i - 1]; } // 從後往前遍歷元素,將它放到有序陣列中的合適位置 int[] newArray = new int[array.length]; for (int i = array.length - 1; i >= 0; i--) { newArray[--counts[array[i] - min]] = array[i]; } // 將有序陣列賦值到array for (int i = 0; i < newArray.length; i++) { array[i] = newArray[i]; }
- 最好、最壞、平均時間複雜度:O(n+k)
- 空間複雜度:O(n+k)
- k是整數的取值範圍
- 屬於穩定排序
計數排序 - 對自定義物件排序
Person[] persons = new Person[] { new Person(20, "A"), new Person(-13, "B"), new Person(17, "C"), new Person(12, "D"), new Person(-13, "E"), new Person(20, "F") }; // 找出最值 int max = persons[0].age; int min = persons[0].age; for (int i = 1; i < persons.length; i++) { if (persons[i].age > max) { max = persons[i].age; } if (persons[i].age < min) { min = persons[i].age; } } // 開闢記憶體空間,儲存次數 int[] counts = new int[max - min + 1]; // 統計每個整數出現的次數 for (int i = 0; i < persons.length; i++) { counts[persons[i].age - min]++; } // 累加次數 for (int i = 1; i < counts.length; i++) { counts[i] += counts[i - 1]; } // 從後往前遍歷元素,將它放到有序陣列中的合適位置 Person[] newArray = new Person[persons.length]; for (int i = persons.length - 1; i >= 0; i--) { newArray[--counts[persons[i].age - min]] = persons[i]; } // 將有序陣列賦值到array for (int i = 0; i < newArray.length; i++) { persons[i] = newArray[i]; } for (int i = 0; i < persons.length; i++) { System.out.println(persons[i]); }
基數排序 (Radix Sort)
- 基數排序非常適合用於整數排序(尤其是非負整數),這裡只介紹對非負整數進行基數排序。
- 執行流程:依次對個位數、十位數、百位數、千位數、萬位數…進行排序(從低位到高位)
- 個位數、十位數、百位數的取值範圍都是固定的0~9,可以使用計數排序對它們進行排序。
基數排序程式碼實現
public class RadixSort extends Sort<Integer> { @Override protected void sort() { // 找出最大值 int max = array[0]; for (int i = 1; i < array.length; i++) { if (array[i] > max) { max = array[i]; } } // 個位數: array[i] / 1 % 10 = 3 // 十位數:array[i] / 10 % 10 = 9 // 百位數:array[i] / 100 % 10 = 5 // 千位數:array[i] / 1000 % 10 = ... for (int divider = 1; divider <= max; divider *= 10) { countingSort(divider); } } protected void countingSort(int divider) { // 開闢記憶體空間,儲存次數 int[] counts = new int[10]; // 統計每個整數出現的次數 for (int i = 0; i < array.length; i++) { counts[array[i] / divider % 10]++; } // 累加次數 for (int i = 1; i < counts.length; i++) { counts[i] += counts[i - 1]; } // 從後往前遍歷元素,將它放到有序陣列中的合適位置 int[] newArray = new int[array.length]; for (int i = array.length - 1; i >= 0; i--) { newArray[--counts[array[i] / divider % 10]] = array[i]; } // 將有序陣列賦值到array for (int i = 0; i < newArray.length; i++) { array[i] = newArray[i]; } } }
- 最好、最壞、平均時間複雜度:O(d ∗ (n + k)) ,d 是最大值的位數,k 是進位制
- 空間複雜度:O(n + k),k 是進位制
- 基數排序屬於穩定排序
基數排序-另一種思路
- 複雜度與穩定性
- 空間複雜度是 O(kn + k) ,時間複雜度是 O(dn)
- d 是最大值的位數,k 是進位制
桶排序(Bucket Sort)
- 執行流程:
- ① 建立一定數量的桶(比如用陣列、連結串列作為桶)
- ② 按照一定的規則(不同型別的資料,規則不同),將序列中的元素均勻分配到對應的桶
- ③ 分別對每個桶進行單獨排序
- ④ 將所有非空桶的元素合併成有序序列
- 元素在桶中的索引:元素值 * 元素數量
- 實現
- 複雜度與穩定性
- 空間複雜度:O(n + m),m 是桶的數量
- 時間複雜度:
- 桶排序屬於穩定排序
相關文章
- 【資料結構與演算法】非比較排序(計數排序、桶排序、基數排序)資料結構演算法排序
- 計數排序vs基數排序vs桶排序排序
- 桶排序和基數排序排序
- 基於桶的排序之計數排序排序
- (戀上資料結構筆記):歸併排序(Merge Sort)資料結構筆記排序
- 基於桶的排序之基數排序以及排序方法總結排序
- 非交換排序-計數排序和桶排序排序
- 資料結構之計數排序資料結構排序
- 資料結構(python) —— 【18排序: 桶排序】資料結構Python排序
- 資料結構與演算法——排序演算法-基數排序資料結構演算法排序
- 資料結構學習筆記-堆排序資料結構筆記排序
- 【Java資料結構與演算法】第八章 快速排序、歸併排序和基數排序Java資料結構演算法排序
- Java排序之計數排序Java排序
- 歸併排序和基數排序排序
- 排序演算法__基數排序排序演算法
- 排序演算法__計數排序排序演算法
- 計數排序排序
- 《演算法筆記》5. 字首樹、桶排序、排序演算法總結演算法筆記排序
- 資料結構第九節(排序(上))資料結構排序
- java 基數排序Java排序
- 資料結構與排序資料結構排序
- 桶排序排序
- 排序演算法__桶排序排序演算法
- 資訊學奧賽複賽複習13-CSP-J2021-02插入排序-排序穩定性、插入排序、sort排序、結構圖、計數排序排序
- 資料結構學習筆記-簡單選擇排序資料結構筆記排序
- 資料結構--排序--插入排序--python語言描述資料結構排序Python
- 桶排序 選擇,插入排序排序
- 排序演算法之——桶排序排序演算法
- 基數排序 LSD py排序
- 桶排序2排序
- 1122. 陣列的相對排序(計數排序 / 自定義排序)陣列排序
- 計數排序 - Counting Sort排序
- 經常提及的幾個js排序方法(氣泡排序、選擇排序、計數排序)JS排序
- 對資料結構和演算法的總結和思考(六)--計數排序資料結構演算法排序
- 常用排序演算法之桶排序排序演算法
- 資料結構與演算法 基礎排序資料結構演算法排序
- 【PHP資料結構】插入類排序:簡單插入、希爾排序PHP資料結構排序
- 經典十大排序演算法(含升序降序,基數排序含負數排序)排序演算法