1. 程式人生 > >資料結構與演算法(八):排序

資料結構與演算法(八):排序

## 什麼是排序? >排序是計算機內經常進行的一種操作,其目的是將一組“無序”的記錄序列調整為“有序”的記錄序列。 ### 1.排序的分類 排序分為兩類: - 內部排序:若整個排序過程不需要訪問外存便能完成,則稱此類排序問題為內部排序。 - 外部排序:若參加排序的記錄數量很大,整個序列的排序過程不可能在記憶體中完成,則稱此類排序問題為外部排序。 一般來說,外部排序只有資料量極大時會使用,一般情況下排序指的都是內部排序。 ![image-20200627110216492](http://img.xiajibagao.top/20200630224748.png) ### 2.空間複雜度 > 1.類似於時間複雜度的討論,一個演算法的空間複雜度(Space Complexity)定義為該演算法所耗費的儲存空間,它也是問題規模n的函式。 > > 2.空間複雜度(Space Complexity)是對一個演算法在執行過程中臨時佔用儲存空間大小的量度。有的演算法需要佔用的臨時工作單元數與解決問題的規模n有關,它隨著n的增大而增大,當n較大時,將佔用較多的儲存單元,例如快速排序和歸併排序演算法就屬於這種情況。 > > 3.在做演算法分析時,主要討論的是時間複雜度。從使用者使用體驗上看,更看重的程式執行的速度。一些快取產品(redis, memcache)和演算法(基數排序)本質就是用空間換時間。 ### 3.排序的穩定 > 待排序的記錄序列中可能存在兩個或兩個以上關鍵字相等的記錄。排序前的序列中arr[i]領先於arr[j](即i > 比如int陣列[1,1,1,6,4]中arr[0],arr[1],arr[2]的值相等,在排序時不改變其序列,則稱所用的方法是穩定的。 - 穩定的排序:氣泡排序,插入排序,歸併排序,基數排序,計數排序 - 不穩定的排序:快速排序,希爾排序,選擇排序,堆排序 穩定性設計到排序的現實意義,舉個例子: > 例如要排序的內容是一組原本按照價格高低排序的物件,如今需要按照銷量高低排序,使用穩定性演算法,可以使得想同銷量的物件依舊保持著價格高低的排序展現,只有銷量不同的才會重新排序。 更多關於穩定性的理解可以[參考這個](https://blog.csdn.net/csdn_kou/article/details/82965310) ### 4.各排序時間複雜度概覽 | 排序法 | 平均時間 | 最差情形 | 是否穩定 | 優先選擇條件 | | :------- | -------- | :--------- | -------- | ------------------------------ | | 氣泡排序 | O(n^2) | O(n^2) | 穩定 | n小時較好 | | 交換排序 | O(n^2) | O(n^2) | 不穩定 | n小時較好 | | 選擇排序 | O(n^2) | O(n^2) | 不穩定 | n小時較好 | | 插入排序 | O(n^2) | O(n^2) | 穩定 | 大部分已排序時較好 | | 基數排序 | O(logRB) | O(logRB) | 穩定 | B是真數(0-9),R是基數(個十百) | | 希爾排序 | O(nlogn) | O(ns)1
第一次排序: > > - -1,10,8,3 //比較10和-1,逆序則交換 > > - -1,8,10,3 //比較10和8 > > - -1,8,3,**10** //比較10和3 > > 第一次排序結束,確定了四個數裡最大的數的位置 > > 第二次排序: > > - -1,8,3,**10** //比較-1和8,不逆序所以不需要移動 > > - -1,3,**8**,**10** //比較8和3 > > 由於已經確定了10為最大數,所以只需要比較到倒數第二位。 > > 第二次排序結束,確定了第二大的數的位置 > > 第三次排序: > > - -1,**3**,**8**,**10** //比較-1和3 > > 由於已經確定了第三和第四大的數,所以只需要比較到倒數第三位。 >
> 第三次排序結束,確定了第三大的數,故第一大的數也隨之確定 ### 2.思路 - 比較相鄰的元素。如果第一個比第二個大,就交換它們兩個; - 對每一對相鄰元素作同樣的工作,從開始第一對到結尾的最後一對,這樣在最後的元素應該會是最大的數; - 針對所有的元素重複以上的步驟,除了最後一個; - 重複步驟1~3,直到排序完成。 - ### 3.實現程式碼 ~~~java /** * 輸入一串無序陣列,對其進行氣泡排序 * @param arr * @return */ public static int[] sort(int[] arr) { //如果某次排序不發生交換,說明上一次排序前已經為有序 boolean isChange = false; //根據陣列長度決定一共需要排序幾次 int round = arr.length - 1; for (int i = 0; i < round; i++) { //每次排序需要對比到第幾位 for (int j = 0; j < round - i; j++) { //對比大小 if (arr[j] >
arr[j + 1]) { int temp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = temp; //發生交換 isChange = true; } } //判斷本次排序是否發生交換 if (!isChange) { System.out.println("第" + (i + 1) + "次排序無交換,第" + i + "次排序已為有序!"); return arr; }else { isChange = false; } System.out.println("第" + (i + 1) + "次排序:" + Arrays.toString(arr)); } return arr; } //{-1, 52, 9, 13, -5, 7}排序執行結果 第1次排序:[-1, 9, 13, -5, 7, 52] 第2次排序:[-1, 9, -5, 7, 13, 52] 第3次排序:[-1, -5, 7, 9, 13, 52] 第4次排序:[-5, -1, 7, 9, 13, 52] 第5次排序無交換,第4次排序已為有序! ~~~ ## 二、選擇排序 選擇排序,是從欲排序的資料中,按指定的規則選出某一元素,再依規定交換位置後達到排序的目的。 ![](http://img.xiajibagao.top/20200630224739.gif) ### 1.舉個例子 >選擇排序(select sorting)也是一種簡單的排序方法。 >它的基本思想是: >第一次從arr[0]~arr[n-1]中選取最小值,與arr[0]交換; >第二次從arr[1]~arr[n-1]中選取最小值,與arr[1]交換; >第三次從arr[2]~arr[n-1]中選取最小值,與arr[2]交換; >… >第i次從arr[i-1]~arr[n-1]中選取最小值,與arr[i-1]交換; >… >第n-1次從arr[n-2]~arr[n-1]中選取最小值,與arr[n-2]交換, >總共通過n-1次,得到一個按排序碼從小到大排列的有序序列。 ### 2.思路 - 需要進行n-1輪排序 - 若要為第i個數排序,就先預設第i個元素為最小數,記錄其大小和下標 - 接著從第i+1到第n個數開始依次比較,如果有數小於最小數,則用該數替換原最小數和其下標 - 第i輪比較結束後,讓找出的最小數與第i個數交換位置 ### 3.程式碼實現 ~~~java /** * 輸入一串無序陣列,對其進行選擇排序 * @param arr * @return */ public static int[] sort(int[] arr) { for (int i = 0; i < arr.length -1; i++) { //用於存放每次選擇中最小數的下標,最小值預設為第一個數為i int minNumIndex = i; //從(i+1,arr.length)的範圍中篩選最小的數 for (int j = i + 1; j < arr.length; j++) { //如果範圍內有數比現有minNum小,則替換下標 if (arr[j] < arr[minNumIndex]) { minNumIndex = j; } } //一次選擇結束,將(i+1,arr.length)的範圍中的最小數與arr[i]交換位置 int temp = arr[minNumIndex]; arr[minNumIndex] = arr[i]; arr[i] = temp; System.out.println("第" + (i + 1) + "輪:" + Arrays.toString(arr)); } return arr; } //{-1, 52, 9, 13, -5, 7}排序執行結果 第1輪後:[-5, 52, 9, 13, -1, 7] 第2輪後:[-5, -1, 9, 13, 52, 7] 第3輪後:[-5, -1, 7, 13, 52, 9] 第4輪後:[-5, -1, 7, 9, 52, 13] 第5輪後:[-5, -1, 7, 9, 13, 52] ~~~ ### 4.與氣泡排序比較 同樣對長度80000的數字進行排序,選擇排序比氣泡排序快不少,原因在於選擇排序每次排序只移動指標,找到位置後才進行一次元素交換,而冒泡需要多次交換。 換句話說,要排序的陣列越長,冒泡每次排序元素要移動的次數就越多,與選擇排序的差距就越明顯。 這個同樣能解釋希爾排序的兩種實現方式的速度差距。 ~~~java //氣泡排序交換值,交換n次值 temp = arr[j]; arr[j + gap] = temp; arr[j] = arr[j + gap] //插入排序交換值,交換n次指標 arr[j] = arr[j - gap] //然後交換1次值 temp = arr[j]; arr[j + gap] = temp; arr[j] = arr[j + gap] ~~~ ## 三、插入排序 插入排序,是將一個記錄插入到已經排好序的有序表中,從而一個新的、記錄數增1的有序表 ![](http://img.xiajibagao.top/20200630224735.gif) ### 1.舉個例子 > 有陣列{5,2,4,6,1,3}要進行排序, > > - 從第二位往前看,5比2大,且5為第一個數,於是5後移,把2插入5前。現在是{2,5,4,6,1,3} > - 從第三位往前看,5比4大,於是5後移,繼續往前看,2比4小,所以把4插入原先5的位置。現在是{2,4,5,6,1,3} > - 從第四位往前看,4比6小,於是6就不動了。現在是{2,4,5,6,1,3} > - 從第五位往前看,6比1小,於是6後移,繼續往前看,5比1小,5後移,繼續往前看.....2比1小,2後移,又2為第一個數,於是把1插入原本2的位置。現在是{1,2,4,5,6,3} > - 從第六位往前看,6比3小,於是6後移,繼續往前看,.....3比2大,於是把3插入原先4的位置。排序完成。 ![](http://img.xiajibagao.top/20200630224731.png) ### 2.思路分析 - 從第一個元素開始,該元素可以認為已經被排序; - 取出下一個元素,在已經排序的元素序列中從後向前掃描; - 如果該元素(已排序)大於新元素,將該元素移到下一位置; - 重複步驟3,直到找到已排序的元素小於或者等於新元素的位置; - 將新元素插入到該位置後; - 重複步驟2~5。 ### 3.程式碼實現 ~~~java /** * 輸入一串無序陣列,對其進行插入排序 * @param arr * @return */ public static int[] sort(int[] arr) { //需要從第二位開始,避免i-1出現控制針 for (int i = 1; i < arr.length; i++) { //指向當前插入位置前一個元素的指標 int pre = i - 1; //當前處理元素的值 int val = arr[i]; // 讓比當前元素大的元素不斷的後移,直到到頭了或者找到了比當前元素小的元素 while (pre >= 0 && arr[pre] > val) { //前一個元素往後移 arr[pre + 1] = arr[pre]; //繼續往前移動 pre--; } //跳出迴圈時即找到了當前元素的正確插入位置 //將該位置的值賦成處理元素的值 arr[pre + 1] = val; System.out.println("第" + (i + 1) + "輪:" + Arrays.toString(arr)); } return arr; } //{-1, 52, 9, 13, -5, 7}排序執行結果 第2輪:[-1, 52, 9, 13, -5, 7] 第3輪:[-1, 9, 52, 13, -5, 7] 第4輪:[-1, 9, 13, 52, -5, 7] 第5輪:[-5, -1, 9, 13, 52, 7] 第6輪:[-5, -1, 7, 9, 13, 52] ~~~ ## 四、希爾排序 希爾排序是希爾(Donald Shell)於1959年提出的一種排序演算法。希爾排序也是一種**插入排序**,它是簡單插入排序經過改進之後的一個**更高效的版本**,也稱為縮小增量排序。 > 希爾排序是把記錄按下標的一定增量分組, 對每組使用直接插入排序演算法排序; 隨著增量逐漸減少,每組包含的關鍵詞越來越多, 當增量減至1時,整個檔案恰被分成一組,演算法便終止。 ![](http://img.xiajibagao.top/20200630224726.gif) ### 1.舉個例子 ![](http://img.xiajibagao.top/20200630224723.png) ### 2.思路 - 將陣列除於2進行分組,得到gap組數字 - 對gap組數字進行插入排序,由於資料共分為gap組,所以同一組相鄰的數字在陣列中的位置總是相隔gap - 遍歷gap組數字,表現在陣列上就是從gap遍歷到arr.length ### 3.程式碼實現 有兩種實現思路,一種是**交換法**,一種是**移位法**, 先說結論:**移位法比交換法快**,原因在於: 交換法思路有點類似於氣泡排序,需要不斷的比較並交換數值, 而移位法即選擇排序,僅僅遍歷賦值後移動指標,找到插入位置後,再把元素插入到有序表,而不是多次交換加入有序表。 ~~~java //交換法交換n次值 temp = arr[j]; arr[j + gap] = temp; arr[j] = arr[j + gap] //位移法移動n次指標後只交換一次值 arr[j] = arr[j - gap] ~~~ #### 3.1交換法實現 ~~~java /** * 輸入一串無序陣列,對其進行希爾排序 * 注意,此方法為移位法 * @param arr * @return */ public static int[] sort(int[] arr) { int temp = 0; int count = 0; // 將陣列每次對半分分成多組,組數逐漸縮小 // gap為每次分組後的同組元素的間隔,比如分成5組,那同組元素間隔即為5,即位置是i和i+5的元素是一組 for (int gap = arr.length / 2; gap > 0; gap /= 2) { // 從第gap個元素開始,逐步遍歷其所在的組,即從第一組開始向後遍歷 // 第gap個元素即為第一組的最後一個元素,也就是i即表示某組的最後一個位置 for (int i = gap; i < arr.length; i++) { // 接著遍歷同組元素,即一組有幾個元素就遍歷幾次 // j=i-gap即獲得第某組的倒數第二個元素位置 // 向前遍歷,每隔gap個元素就對比一次大小 for (int j = i - gap; j >= 0; j = j - gap) { //如果當前元素大於後一個元素,就交換位置 if (arr[j] > arr[j + gap]) { temp = arr[j]; arr[j] = arr[j + gap]; arr[j + gap] = temp; } } } System.out.println("第" + (++count) + "輪:" + Arrays.toString(arr)); } return arr; } //{-1, 52, 9, 13, -5, 7}排序執行結果 第1輪:[-1, -5, 7, 13, 52, 9] 第2輪:[-5, -1, 7, 9, 13, 52] ~~~ #### 3.2 移位法實現 **下面是通過移位法實現的排序,比交換法更快更穩定:** ~~~java /** * 輸入一串無序陣列,對其進行希爾排序 * 注意,此方法為移位法 * @param arr * @return */ public static int[] sortByMove(int[] arr) { int count = 0; // 將陣列每次對半分分成多組,組數逐漸縮小 // gap為每次分組後的同組元素的間隔,比如分成5組,那同組元素間隔即為5,即位置是i和i+5的元素是一組 for (int gap = arr.length / 2; gap > 0; gap /= 2) { //從第gap個元素開始,逐個對其所在組進行插入排序 for (int i = gap; i < arr.length; i++) { int j = i; int temp = arr[j]; //如果某組最後一個元素比前一個元素小 if (arr[j] < arr[j - gap]) { //將同組元素不斷後移,直到該元素找到位置或者到頭為止 while (j - gap >= 0 && arr[j - gap] > temp) { arr[j] = arr[j - gap]; j = j - gap; } //當找到位置時插入元素 arr[j] = temp; } } System.out.println("第" + (++count) + "輪:" + Arrays.toString(arr)); } return arr; } ~~~ ## 五、快速排序 快速排序的基本思想:通過一趟排序將待排記錄分隔成獨立的兩部分,其中一部分記錄的關鍵字均比另一部分的關鍵字小,則可分別對這兩部分記錄繼續進行排序,以達到整個序列有序。 ![](http://img.xiajibagao.top/20200630224718.gif) ### 1.舉個例子 ![](http://img.xiajibagao.top/20200630224715.png) > - 先選擇一箇中間位置數11,分別將陣列中比11大和比11小的數放到左右兩個陣列中 > - 對兩個陣列分別選一箇中間位置數,也就是5和21,各自再根據比中間數小或者比中間數大再次分為兩個陣列。以此類推 ### 2.思路 - 把長度為n的輸入序列分成兩個長度為n/2的子序列 - 對這兩個子序列分別採用歸併排序; - 將兩個排序好的子序列合併成一個最終的排序序列 - 對左支和右支可通過遞迴實現排序 ### 3.程式碼實現 ~~~java public static int[] sort(int[] arr) { sort(arr, 0, arr.length - 1); return arr; } /** * 輸入一串無序陣列,並根據給定的左右指標對指定的範圍其進行排序 * @param arr * @param left * @param right * @return */ public static int[] sort(int[] arr, int left, int right) { //左右指標 int l = left; int r = right; //找到中間數 int pivot = arr[(left + right) / 2]; //用於元素交換的臨時變數 int temp; //將比中間數小的放左邊,比中間數大的放右邊 while (l < r) { // 從左往右遍歷,尋找比中間數大的數 while (arr[l] < pivot) { l++; } // 從右往左遍歷,尋找比中間數小的數 while (arr[r] > pivot) { r--; } // 如果l > r,即左指標右指標都越過了中間數,說明兩邊數都已經有序 // 如果l = r,即可能存在多個與中間數同值的元素的情況下,左右指標一起指向了同一邊的同一個元素,也說明兩邊數都已經有序 if (l >= r) { break; } //交換元素 temp = arr[l]; arr[l] = arr[r]; arr[r] = temp; //如果交換完後,發現現在右側有一個與中間數相同的數,右指標前移一位 if (arr[l] == pivot) { r -= 1; } //如果交換完後,發現現在左側有一個與中間數相同的數,左指標後移一位 if (arr[r] == pivot) { l += 1; } } //防止死迴圈 if (l == r) { l += 1; r -= 1; } //向右遞迴 if (left < r) { sort(arr, l, right); } //向左遞迴 if (right > l) { sort(arr, left, r); } return arr; } ~~~ ## 六、歸併排序 歸併排序是建立在歸併操作上的一種有效的排序演算法。該演算法是採用分治法(Divide and Conquer)的一個非常典型的應用。將已有序的子序列合併,得到完全有序的序列;即先使每個子序列有序,再使子序列段間有序。若將兩個有序表合併成一個有序表,稱為2-路歸併。 ![](http://img.xiajibagao.top/20200630224710.gif) ### 1.舉個例子 ![](http://img.xiajibagao.top/20200630224706.png) 我們以上圖最後一次合併為例: ![](http://img.xiajibagao.top/20200630224703.png) ![](http://img.xiajibagao.top/20200630224700.png) 以上多個有序陣列間的合併需要進行多次,通過遞迴完成 ### 2.思路 - 把長度為n的陣列分成兩個長度為n/2的子陣列; - 如果子陣列仍然長度大於1,就重複步驟1直到所有陣列都被拆分完畢 - 將拆分後的元素兩兩合併為一個有序陣列,然後相鄰兩個陣列A,B進行合併: 1. 建立一個新陣列,然後遍歷B陣列並與A陣列第一位進行比較,如果該數字比A陣列第一小則放入新陣列第一位 2. 否則將A陣列第一位放入新陣列 - 重複上面步驟3直到A,B陣列所有元素都有序放入新陣列,即合併完成 - 重複步驟3,直到所有陣列都最終都被合併為一個有序陣列 ### 3.程式碼實現 ~~~java /** * @Author:黃成興 * @Date:2020-06-29 21:45 * @Description:歸併排序 */ public class MergeSort { public static void main(String[] args) { int arr[] = {8, 4, 5, 7}; System.out.println(Arrays.toString(sort(arr))); } public static int[] sort(int[] arr) { int temp[] = new int[arr.length]; return sort(arr, 0, arr.length - 1, temp); } /** * 合併排序 * @param arr 排序的原始陣列 * @param left 左邊有序序列的初始索引 * @param right 右邊索引 * @param temp 臨時儲存的中轉陣列 */ public static int[] sort(int[] arr, int left, int right, int[] temp) { if (left < right) { //獲取中間索引 int mid = (left + right) / 2; //向左遞迴分解 sort(arr, left, mid, temp); //向右遞迴分解 sort(arr, mid + 1, right, temp); // 先左遍歷到最左邊,然後向右遍歷,當l=r時觸發排序 merge(arr, left, mid, right, temp); } return arr; } /** * 合併的方法 * * @param arr 排序的原始陣列 * @param left 左邊有序序列的初始索引 * @param mid 中間索引 * @param right 右邊索引 * @param temp 臨時儲存的中轉陣列 */ public static void merge(int[] arr, int left, int mid, int right, int[] temp) { //左邊有序序列的初始索引 int i = left; //中間索引 int j = mid+1; //temp的索引 int t = 0; //先把左右兩邊有序的資料按照規則填充到temp陣列,直到左右兩邊的有序序列,有一邊處理完畢為止 while (i <= mid && j <= right) { //如果左邊的有序序列的當前元素小於等於右邊有序序列的當前元素 if (arr[i] <= arr[j]) { temp[t] = arr[i]; t += 1; i += 1; } else { //否則將右邊有序序列的當前元素填充到temp陣列 temp[t] = arr[j]; t += 1; j += 1; } } //左邊的有序序列還有剩餘的元素,就全部填充到temp陣列 while (i <= mid) { temp[t] = arr[i]; t += 1; i += 1; } //右邊的有序序列還有剩餘的元素,就全部填充到temp陣列 while (j <= right) { temp[t] = arr[j]; t += 1; j += 1; } //將temp數組裡的有序元素拷貝回arr陣列 //從左邊開始拷貝, 注意:不是每次都拷貝所有 t = 0; int tempLeft = left; //第一次合併:templeft = 0,right = 1。 第二次合併:templeft = 2,right = 3。 最後一次:templeft = 0,right = 7 while (tempLeft <= right) { arr[tempLeft] = temp[t]; t += 1; tempLeft += 1; } } } ~~~ ## 七、基數排序 基數排序是按照低位先排序,然後收集;再按照高位排序,然後再收集;依次類推,直到最高位。有時候有些屬性是有優先順序順序的,先按低優先順序排序,再按高優先順序排序。最後的次序就是高優先順序高的在前,高優先順序相同的低優先順序高的在前。 ![](http://img.xiajibagao.top/20200630224653.gif) ### 1.舉個例子 ![](http://img.xiajibagao.top/20200630224650.png) ![](http://img.xiajibagao.top/20200630224648.png) ![](http://img.xiajibagao.top/20200630224641.png) ### 2.思路 - 取得陣列中的最大數,並取得位數 - 準備一個長度為10,內部一位陣列長度為arr.length的二維陣列,可以理解為10個高度為arr.length的桶 - 遍歷陣列,根據陣列中個位數決定要放在哪個桶,即如果個位數為1就放入1號桶,為2就放入2號桶,直到陣列所有元素分配完畢 - 遍歷桶將桶中數字放回原陣列,然後清空桶。即完成了個位數的排序 - 重複步驟3和步驟4,但是排序依據從個位數換成十位數,然後百位數.....以此類推,直到陣列最大位數 ### 3.程式碼實現 ~~~java package com.huang.example.sort; import java.util.Arrays; /** * @Author:黃成興 * @Date:2020-06-30 21:39 * @Description:基數排序 */ public class RadixSort { public static void main(String[] args) { int[] arr = {53, 3, 542, 748, 14, 214 }; System.out.println("最大位數:" + getMaxDigit(arr)); System.out.println(Arrays.toString(sort(arr))); } /** * 獲取陣列中的最大數的位數 * @param arr * @return */ public static int getMaxDigit(int[] arr) { int max = arr[0]; for (int i = 1; i < arr.length; i++) { if (arr[i] > max) { max = arr[i]; } } return String.valueOf(max).length(); } public static int[] sort(int[] arr) { //設定二維陣列用於表示桶 //第一層陣列下標即表示存放某數位為x的數字的桶,比如下標為2的桶用於存放個位數為2的數字;下標為0的桶用於存放十位數為0的數字 //第二層陣列即表示桶高度 int[][] bucket = new int[10][arr.length]; //表示某個桶內用幾個元素,比如bucketElementCount[0]即表示bucket[0]桶有幾個元素 //由於數字下標從0開始,所以這同時也表示了桶下一個元素的插入下標,比如bucketElementCount[0]=1,就意味著bucket[0]下一個元素應該插到bucket[0][1]去 int[] bucketElementCount = new int[10]; //最大數有幾位就迴圈排序幾次 for (int i = 0, n = 1; i <= getMaxDigit(arr); i++, n *= 10) { //遍歷元素並歸類到桶中 for (int j = 0; j < arr.length; j++) { //獲取元素某數位的數字 //根據遍歷,獲取數字的個位,十位數,百位數...... int digitOfElement = arr[j] / n % 10; //將其歸類到桶中 bucket[digitOfElement][bucketElementCount[digitOfElement]] = arr[j]; //提高桶高度 bucketElementCount[digitOfElement]++; } //按順序將每一個桶中元素取出並放入原集合 int index = 0; for (int k = 0; k < bucketElementCount.length; k++) { //如果桶中有元素就取出 if (bucketElementCount[k] != 0) { //遍歷桶中元素並放入陣列 for (int l = 0; l < bucketElementCount[k]; l++) { arr[index++] = bucket[k][l]; } } //清空桶 bucketElementCount[k] = 0; } } return arr; } } ~~~ ### 4.基數排序注意事項: - 基數排序是典型的空間換時間,當排序的數字過多的時候可能會發生`OutOfMemoryError`(實測八千萬時報錯) - 基數排序要排負數的時候需要加以改進: 將陣列中的負數單獨摘出並取絕對值後進行排序,然後倒序插入排序完的整數陣列,並且在插入過程加上負號 ## 八、堆排序 參照[二叉樹