資料結構複習之 排序

2021-08-26 17:37:20 字數 4143 閱讀 8462

排序:對一串行物件根據某個關鍵字進行排序;

穩定:如果a原本在b前面,而a=b,排序之後a仍然在b的前面;

不穩定:如果a原本在b的前面,而a=b,排序之後a可能會出現在b的後面;

內排序:所有排序操作都在記憶體中完成;

外排序:由於資料太大,因此把資料放在磁碟中,而排序通過磁碟和記憶體的資料傳輸才能進行;

排序耗時的操作:比較、移動;

排序分類:

(1)交換類:氣泡排序、快速排序;此類的特點是通過不斷的比較和交換進行排序;

(2)插入類:簡單插入排序、希爾排序;此類的特點是通過插入的手段進行排序;

(3)選擇類:簡單選擇排序、堆排序;此類的特點是看準了再移動;

(4)歸併類:歸併排序;此類的特點是先分割後合併;

歷史程序:一開始排序演算法的複雜度都在o(n^2),希爾排序的出現打破了這個僵局;

此種實現方法是最簡單的排序實現;

缺點是每次找最小值都是單純的找,而沒有為下一次尋找做出鋪墊;

演算法如下:

public static int ******_sort(int arr) } } return arr; }

氣泡排序相對於最簡單的排序有了改進,即每次交換都是對後續有幫助的,大數將會越來越大,小的數將會越來越小;

氣泡排序思想:兩兩相鄰元素之間的比較,如果前者大於後者,則交換

因此此排序屬於交換排序一類,同類的還有現在最常用的排序方法:快速排序;

此種方法是最一般的氣泡排序實現,思想就是兩兩相鄰比較並交換;

演算法實現如下:

public static int bubble_sort2(int arr) } } return arr; }

改進在於如果出現乙個序列,此序列基本是排好序的,如果是標準的氣泡排序,則還是需要進行不斷的比較;

改進方法:通過乙個boolean ischanged,如果一次迴圈中沒有交換過元素,則說明已經排好序;

演算法實現如下:

// 最好:n-1次比較,不移動,因此時間複雜度為o(n),不占用輔助空間 // 最壞:n(n-1)/2次比較和移動,因此o(n^2),占用交換的臨時空間,大小為1; public static int bubble_sort3(int arr) } } return arr; }

簡單選擇排序特點:每次迴圈找到最小值,並交換,因此交換次數始終為n-1次;

相對於最簡單的排序,對於很多不必要的交換做了改進,每個迴圈不斷比較後記錄最小值,只做了一次交換(當然也可能不交換,當最小值已經在正確位置)

演算法如下:

//最差:n(n-1)/2次比較,n-1次交換,因此時間複雜度為o(n^2) //最好:n(n-1)/2次比較,不交換,因此時間複雜度為o(n^2) //好於氣泡排序 public static int selection_sort(int arr) } 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) public static int insertion_sort(int arr) arr[j + 1] = tmp; } } return arr; }

簡單插入排序比選擇排序和氣泡排序好!

2023年shell發明;

第乙個突破o(n^2)的排序演算法;是簡單插入排序的改進版;

思想:由於簡單插入排序對於記錄較少或基本有序時很有效,因此我們可以通過將序列進行分組排序使得每組容量變小,再進行分組排序,然後進行一次簡單插入排序即可;

這裡的分組是跳躍分組,即第1,4,7位置為一組,第2,5,8位置為一組,第3,6,9位置為一組;

索引此時,如果increment=3,則i%3相等的索引為一組,比如索引1,1+3,1+3*2

一般增量公式為:increment = increment/3+1;

演算法實現如下:

// o(n^(3/2)) //不穩定排序演算法 public static int shell_sort(int arr) arr[j + increment] = tmp; } } } while (increment > 1); return arr; }

floyd和williams在2023年發明;

大根堆:任意父節點都比子節點大;

小根堆:任意父節點都比子節點小;

不穩定排序演算法,是簡單選擇排序的改進版;

思想:構建一棵完全二叉樹,首先構建大根堆,然後每次都把根節點即最大值移除,並用編號最後的節點替代,這時陣列長度減一,然後重新構建大根堆,以此類推;

注意:此排序方法不適用於個數少的序列,因為初始構建堆需要時間;

演算法實現如下:

// 時間複雜度為o(nlogn) //不穩定排序演算法 //輔助空間為1 //不適合排序個數較少的序列 public static int heap_sort(int arr) // 構建大根堆:o(n) for (int i = arr.length / 2; i >= 1; i--) // 重建:o(nlogn) for (int i = arr.length; i > 1; i--) for (int i = 1; i < tmp.length; i++) return arr; } private static void makemaxrootheap(int arr, int low, int high) if (tmp >= arr[j]) arr[low] = arr[j]; low = j; } arr[low] = tmp; }

穩定排序演算法;

思想:利用遞迴進行分割和合併,分割直到長度為1為止,並在合併前保證兩序列原本各自有序,合併後也有序;

實現**如下:

// 穩定排序; // 時間複雜度o(nlogn) // 空間複雜度:o(n+logn) public static int merge_sort(int arr) private static void msort(int sr, int tr, int s, int t) else } private static void merge(int tr2, int tr, int i, int m, int t) else } while (i <= m) while (k <= t) }

氣泡排序的公升級版;現在用的最多的排序方法;

思想:選取pivot,將pivot調整到乙個合理的位置,使得左邊全部小於他,右邊全部大於他;

注意:如果序列基本有序或序列個數較少,則可以採用簡單插入排序,因為快速排序對於這些情況效率不高;

實現**如下:

// 不穩定排序演算法 // 時間複雜度:最好:o(nlogn) 最壞:o(n^2) // 空間複雜度:o(logn) public static int quick_sort(int arr) private static void qsort(int arr, int low, int high) } private static int partition(int arr, int low, int high) swap(arr, low, high);//交換,此處可以優化 while (low < high && arr[low] <= pivotkey) swap(arr, low, high); } return low; }

(1)選取pivot:選取pivot的值對於快速排序至關重要,理想情況,pivot應該是序列的中間數;

而前面我們只是簡單的取第乙個數作為pivot,這點可以進行優化;

優化方法:抽多個數後取中位數作為pivot;

(2)對於小陣列使用插入排序:因為快速排序適合大陣列排序,如果是小陣列,則效果可能沒有簡單插入排序來得好;

如果想進行優化,則可以使用以下**:

public static int quick_sort(int arr) else return arr; }

此圖摘自的圖

總結:每個排序都有每個排序的優點,我們需要在適當的時候用適當的演算法;

比如在基本有序、陣列規模小時用直接插入排序;

比如在大陣列時用快速排序;

比如如果要想穩定性,則使用歸併排序;

摘錄維基百科:

資料結構之排序(期末複習)

知識框架 ps 文末有驚喜 排序是按關鍵字的非遞減或非遞增順序對一組記錄重新進行排列的操作。分類 1 內部排序 待排序記錄全部存放在計算機記憶體中進行排序的過程 2 外部排序 待排序記錄的數量很大,記憶體一次不能容納全部記錄,在排序過程中尚需對外存進行訪問的排序過程 1 插入類 直接插入排序 折半插...

資料結構之 棧 複習

棧 先進後出的線性表 棧也可以通過順序儲存和鏈式儲存的方式實現 一 順序儲存實現 陣列的尾端作為棧頂 實現 package org.xiazdong.list public class myarraystack public void push t e top if top t.length t t...

資料結構之 佇列 複習

佇列是先進先出的線性表 佇列由於是線性表,因此也有順序儲存和鏈式儲存兩種實現方式 一 順序儲存實現 由於佇列的特性是 從隊尾新增,從對頭刪除,因此如果讓陣列的尾部用作隊尾,陣列的頭部用作隊頭,則刪除元素時,時間複雜度為o n 因此我們需要用迴圈陣列實現,並且維護兩個屬性 front rear,fro...