資料結構---九大排序演算法再總結
排序:對一序列物件根據某個關鍵字進行排序;
穩定:如果a原本在b前面,而a=b,排序之後a仍然在b的前面;
不穩定:如果a原本在b的前面,而a=b,排序之後a可能會出現在b的後面;
內排序:所有排序操作都在記憶體中完成;
外排序:由於資料太大,因此把資料放在磁碟中,而排序通過磁碟和記憶體的資料傳輸才能進行;
排序耗時的操作:比較、移動;
排序分類:
(1)交換類:氣泡排序、快速排序;此類的特點是通過不斷的比較和交換進行排序;
(2)插入類:簡單插入排序、希爾排序;此類的特點是通過插入的手段進行排序;
(3)選擇類:簡單選擇排序、堆排序;此類的特點是看準了再移動;
(4)歸併類:歸併排序;此類的特點是先分割後合併;
歷史程序:一開始排序演算法的複雜度都在O(n^2),希爾排序的出現打破了這個僵局;
以下視訊是Sapientia University創作的,用跳舞的形式演示排序步驟,這些視訊就可以當作複習排序的資料~
上面介紹的排序演算法都是基於排序的,還有一類演算法不是基於比較的排序演算法,即計數排序、基數排序;
預備:最簡單的排序
此種實現方法是最簡單的排序實現;
缺點是每次找最小值都是單純的找,而沒有為下一次尋找做出鋪墊;
演算法如下:
-
public
- for (int i = 0; i < arr.length; i++) {
- for (int j = i + 1; j < arr.length; j++) {
- if (arr[i] > arr[j]) {
- swap(arr, i, j);
- }
- }
- }
- return arr;
- }
一、氣泡排序
氣泡排序相對於最簡單的排序有了改進,即每次交換都是對後續有幫助的,大數將會越來越大,小的數將會越來越小;
氣泡排序思想:兩兩相鄰元素之間的比較,如果前者大於後者,則交換;
因此此排序屬於交換排序一類,同類的還有現在最常用的排序方法:快速排序;
1.標準氣泡排序
此種方法是最一般的氣泡排序實現,思想就是兩兩相鄰比較並交換;
演算法實現如下:
- publicstaticint[] bubble_sort2(int[] arr) {
- for (int i = 0; i < arr.length; i++) {
- for (int j = arr.length - 1; j > i; j--) {
- if (arr[j] < arr[j - 1]) {
- swap(arr, j, j - 1);
- }
- }
- }
- return arr;
- }
2.改進氣泡排序
改進在於如果出現一個序列,此序列基本是排好序的,如果是標準的氣泡排序,則還是需要進行不斷的比較;
改進方法:通過一個boolean isChanged,如果一次迴圈中沒有交換過元素,則說明已經排好序;
演算法實現如下:
- // 最好:n-1次比較,不移動,因此時間複雜度為O(n),不佔用輔助空間
- // 最壞:n(n-1)/2次比較和移動,因此O(n^2),佔用交換的臨時空間,大小為1;
- publicstaticint[] bubble_sort3(int[] arr) {
- boolean isChanged = true;
- for (int i = 0; i < arr.length && isChanged; i++) {
- isChanged = false;
- for (int j = i + 1; j < arr.length; j++) {
- if (arr[i] > arr[j]) {
- swap(arr, i, j);
- isChanged = true;
- }
- }
- }
- return arr;
- }
二、簡單選擇排序
簡單選擇排序特點:每次迴圈找到最小值,並交換,因此交換次數始終為n-1次;
相對於最簡單的排序,對於很多不必要的交換做了改進,每個迴圈不斷比較後記錄最小值,只做了一次交換(當然也可能不交換,當最小值已經在正確位置)
演算法如下:
- //最差:n(n-1)/2次比較,n-1次交換,因此時間複雜度為O(n^2)
- //最好:n(n-1)/2次比較,不交換,因此時間複雜度為O(n^2)
- //好於氣泡排序
- publicstaticint[] selection_sort(int[] arr) {
- for (int i = 0; i < arr.length - 1; i++) {
- int min = i;
- for (int j = i + 1; j < arr.length; j++) {
- if (arr[min] > arr[j]) {
- min = j;
- }
- }
- if (min != i)
- swap(arr, min, i);
- }
- return arr;
- }
三、簡單插入排序
思想: 給定序列,存在一個分界線,分界線的左邊被認為是有序的,分界線的右邊還沒被排序,每次取沒被排序的最左邊一個和已排序的做比較,並插入到正確位置;我們預設索引0的子陣列有序;每次迴圈將分界線右邊的一個元素插入有序陣列中,並將分界線向右移一位;
演算法如下:
- // 最好:n-1次比較,0次移動 ,時間複雜度為O(n)
- // 最差:(n+2)(n-1)/2次比較,(n+4)(n-1)/2次移動,時間複雜度為 O(n^2)
- publicstaticint[] insertion_sort(int[] arr) {
- int j;
- for (int i = 1; i < arr.length; i++) {
- if (arr[i] < arr[i - 1]) {
- int tmp = arr[i];
- for (j = i - 1; j >= 0 && arr[j] > tmp; j--) {
- arr[j + 1] = arr[j];
- }
- arr[j + 1] = tmp;
- }
- }
- return arr;
- }
簡單插入排序比選擇排序和氣泡排序好!
四、希爾排序
1959年Shell發明;
第一個突破O(n^2)的排序演算法;是簡單插入排序的改進版;
思想:由於簡單插入排序對於記錄較少或基本有序時很有效,因此我們可以通過將序列進行分組排序使得每組容量變小,再進行分組排序,然後進行一次簡單插入排序即可;
這裡的分組是跳躍分組,即第1,4,7位置為一組,第2,5,8位置為一組,第3,6,9位置為一組;
索引 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
此時,如果increment=3,則i%3相等的索引為一組,比如索引1,1+3,1+3*2
一般增量公式為:increment = increment/3+1;
演算法實現如下:
- // O(n^(3/2))
- //不穩定排序演算法
- publicstaticint[] shell_sort(int[] arr) {
- int j;
- int increment = arr.length;
- do {
- increment = increment / 3 + 1;
- for (int i = increment; i < arr.length; i++) { //i=increment 因為插入排序預設每組的第一個記錄都是已排序的
- if (arr[i] < arr[i - increment]) {
- int tmp = arr[i];
- for (j = i - increment; j >= 0