什麼是排序?
排序是計算機內經常進行的一種操作,其目的是將一組“無序”的記錄序列調整為“有序”的記錄序列。
1.排序的分類
排序分為兩類:
- 內部排序:若整個排序過程不需要訪問外存便能完成,則稱此類排序問題為內部排序。
- 外部排序:若參加排序的記錄數量很大,整個序列的排序過程不可能在記憶體中完成,則稱此類排序問題為外部排序。
一般來說,外部排序只有資料量極大時會使用,一般情況下排序指的都是內部排序。
2.空間複雜度
1.類似於時間複雜度的討論,一個演算法的空間複雜度(Space Complexity)定義為該演算法所耗費的儲存空間,它也是問題規模n的函式。
2.空間複雜度(Space Complexity)是對一個演算法在執行過程中臨時佔用儲存空間大小的量度。有的演算法需要佔用的臨時工作單元數與解決問題的規模n有關,它隨著n的增大而增大,當n較大時,將佔用較多的儲存單元,例如快速排序和歸併排序演算法就屬於這種情況。
3.在做演算法分析時,主要討論的是時間複雜度。從使用者使用體驗上看,更看重的程式執行的速度。一些快取產品(redis, memcache)和演算法(基數排序)本質就是用空間換時間。
3.排序的穩定
待排序的記錄序列中可能存在兩個或兩個以上關鍵字相等的記錄。排序前的序列中arr[i]領先於arr[j](即i<j)。若在排序後的序列中arr[i]仍然領先於arr[j],則稱所用的方法是穩定的。
比如int陣列[1,1,1,6,4]中arr[0],arr[1],arr[2]的值相等,在排序時不改變其序列,則稱所用的方法是穩定的。
- 穩定的排序:氣泡排序,插入排序,歸併排序,基數排序,計數排序
- 不穩定的排序:快速排序,希爾排序,選擇排序,堆排序
穩定性設計到排序的現實意義,舉個例子:
例如要排序的內容是一組原本按照價格高低排序的物件,如今需要按照銷量高低排序,使用穩定性演算法,可以使得想同銷量的物件依舊保持著價格高低的排序展現,只有銷量不同的才會重新排序。
更多關於穩定性的理解可以參考這個
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<s<2 | 不穩定 | s是所選分組 |
快速排序 | O(nlogn) | O(n2) | 不穩定 | n大時較好 |
歸併排序 | O(nlogn) | O(nlogn) | 穩定 | n大時較好 |
堆排序 | O(nlogn) | O(nlogn) | 不穩定 | n大時較好 |
一、氣泡排序
氣泡排序是一種簡單的排序演算法,它也是一種穩定排序演算法。其實現原理是重複掃描待排序序列,並比較每一對相鄰的元素,當該對元素順序不正確時進行交換。一直重複這個過程,直到沒有任何兩個相鄰元素可以交換,就表明完成了排序。
1.舉個例子
要對10,-1,8,3這四個數進行排序:
第一次排序:
-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.實現程式碼
/**
* 輸入一串無序陣列,對其進行氣泡排序
* @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次排序已為有序!
二、選擇排序
選擇排序,是從欲排序的資料中,按指定的規則選出某一元素,再依規定交換位置後達到排序的目的。
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.程式碼實現
/**
* 輸入一串無序陣列,對其進行選擇排序
* @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的數字進行排序,選擇排序比氣泡排序快不少,原因在於選擇排序每次排序只移動指標,找到位置後才進行一次元素交換,而冒泡需要多次交換。
換句話說,要排序的陣列越長,冒泡每次排序元素要移動的次數就越多,與選擇排序的差距就越明顯。
這個同樣能解釋希爾排序的兩種實現方式的速度差距。
//氣泡排序交換值,交換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的有序表
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的位置。排序完成。
2.思路分析
- 從第一個元素開始,該元素可以認為已經被排序;
- 取出下一個元素,在已經排序的元素序列中從後向前掃描;
- 如果該元素(已排序)大於新元素,將該元素移到下一位置;
- 重複步驟3,直到找到已排序的元素小於或者等於新元素的位置;
- 將新元素插入到該位置後;
- 重複步驟2~5。
3.程式碼實現
/**
* 輸入一串無序陣列,對其進行插入排序
* @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時,整個檔案恰被分成一組,演算法便終止。
1.舉個例子
2.思路
- 將陣列除於2進行分組,得到gap組數字
- 對gap組數字進行插入排序,由於資料共分為gap組,所以同一組相鄰的數字在陣列中的位置總是相隔gap
- 遍歷gap組數字,表現在陣列上就是從gap遍歷到arr.length
3.程式碼實現
有兩種實現思路,一種是交換法,一種是移位法,
先說結論:移位法比交換法快,原因在於:
交換法思路有點類似於氣泡排序,需要不斷的比較並交換數值,
而移位法即選擇排序,僅僅遍歷賦值後移動指標,找到插入位置後,再把元素插入到有序表,而不是多次交換加入有序表。
//交換法交換n次值
temp = arr[j];
arr[j + gap] = temp;
arr[j] = arr[j + gap]
//位移法移動n次指標後只交換一次值
arr[j] = arr[j - gap]
3.1交換法實現
/**
* 輸入一串無序陣列,對其進行希爾排序
* 注意,此方法為移位法
* @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 移位法實現
下面是通過移位法實現的排序,比交換法更快更穩定:
/**
* 輸入一串無序陣列,對其進行希爾排序
* 注意,此方法為移位法
* @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;
}
五、快速排序
快速排序的基本思想:通過一趟排序將待排記錄分隔成獨立的兩部分,其中一部分記錄的關鍵字均比另一部分的關鍵字小,則可分別對這兩部分記錄繼續進行排序,以達到整個序列有序。
1.舉個例子
- 先選擇一箇中間位置數11,分別將陣列中比11大和比11小的數放到左右兩個陣列中
- 對兩個陣列分別選一箇中間位置數,也就是5和21,各自再根據比中間數小或者比中間數大再次分為兩個陣列。以此類推
2.思路
- 把長度為n的輸入序列分成兩個長度為n/2的子序列
- 對這兩個子序列分別採用歸併排序;
- 將兩個排序好的子序列合併成一個最終的排序序列
- 對左支和右支可通過遞迴實現排序
3.程式碼實現
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-路歸併。
1.舉個例子
我們以上圖最後一次合併為例:
以上多個有序陣列間的合併需要進行多次,通過遞迴完成
2.思路
- 把長度為n的陣列分成兩個長度為n/2的子陣列;
- 如果子陣列仍然長度大於1,就重複步驟1直到所有陣列都被拆分完畢
- 將拆分後的元素兩兩合併為一個有序陣列,然後相鄰兩個陣列A,B進行合併:
- 建立一個新陣列,然後遍歷B陣列並與A陣列第一位進行比較,如果該數字比A陣列第一小則放入新陣列第一位
- 否則將A陣列第一位放入新陣列
- 重複上面步驟3直到A,B陣列所有元素都有序放入新陣列,即合併完成
- 重複步驟3,直到所有陣列都最終都被合併為一個有序陣列
3.程式碼實現
/**
* @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;
}
}
}
七、基數排序
基數排序是按照低位先排序,然後收集;再按照高位排序,然後再收集;依次類推,直到最高位。有時候有些屬性是有優先順序順序的,先按低優先順序排序,再按高優先順序排序。最後的次序就是高優先順序高的在前,高優先順序相同的低優先順序高的在前。
1.舉個例子
2.思路
- 取得陣列中的最大數,並取得位數
- 準備一個長度為10,內部一位陣列長度為arr.length的二維陣列,可以理解為10個高度為arr.length的桶
- 遍歷陣列,根據陣列中個位數決定要放在哪個桶,即如果個位數為1就放入1號桶,為2就放入2號桶,直到陣列所有元素分配完畢
- 遍歷桶將桶中數字放回原陣列,然後清空桶。即完成了個位數的排序
- 重複步驟3和步驟4,但是排序依據從個位數換成十位數,然後百位數.....以此類推,直到陣列最大位數
3.程式碼實現
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
(實測八千萬時報錯) -
基數排序要排負數的時候需要加以改進:
將陣列中的負數單獨摘出並取絕對值後進行排序,然後倒序插入排序完的整數陣列,並且在插入過程加上負號
八、堆排序
參照二叉樹部分