排序演算法的java實現及複雜度總結1
阿新 • • 發佈:2020-12-02
各種排序演算法總結和複雜度總結:
-
氣泡排序
public static void bubbleSort(int[] arr){ //每次都要進行型別判斷防止非空操作和無效的比較操作 if (arr == null || arr.length < 2) { return; } for (int i = 0;i < arr.length - 1;i++){ for (int j = 0; j < arr.length - 1 -i ; j++) { if (arr[j] > arr[j+1]){ int t; t=arr[j]; arr[j]=arr[j+1]; arr[j+1]=t; } } } }
氣泡排序是每個把最大的值都放在最後面進行排序
時間複雜度O(N^2)
空間複雜度O(1)
-
對數器的概念(這個後面自己在進行總結各種對數器,看老師的給的自己總結下來)
- 想測試的方法A
- 絕對正確但是複雜度高的B
- 產生一個輸入的隨機樣本,短的就行
- 二方法輸出結果進行比較
-
選擇排序
public static void selectSort(int[] arr) { if (arr == null || arr.length < 2) { return; } 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; } } int t; t = arr[i]; arr[i] = arr[min]; arr[min] = t; } }
選擇排序是選出最小的值然後放在最前面
時間複雜度O(N^2)
空間複雜度O(1)
-
插入排序
public static void insertSort(int[] arr) { if (arr == null || arr.length < 2) { return; } for (int i = 1; i < arr.length - 1; i++) { for (int j = i+1; j > 0 && arr[j-1] > arr[j]; j--) { int t; t = arr[j]; arr[j] = arr[j-1]; arr[j-1] = t; } } }
插入排序就像打牌一樣,把新抓進來的數 從尾到頭進行插入
-
遞迴函式的時間複雜度的估算方法
利用到的是master公式
T(N)=a*T(N/b)+O(N^d)
N是原來的樣本量
N/b是子過程的樣本量
O(N^d)是除了樣本量剩下的子過程的大小
例如:
二邊遞迴找最大值
T(N) = 2*T(N/2) + o(1)
結果:
上面的結果為O(2^log(2,2))
-
歸併排序
public static void mergeSort(int[] arr) { if (arr == null || arr.length < 2) { return; } mergeSort(arr, 0, arr.length - 1); } private static void mergeSort(int[] arr, int l, int r) { //遞迴先找出出口 if (l == r) {//只剩下自己了一定是有序的 return; } int mid = l + ((r - l)>>2);//等同於這個東西(l + r) / 2因為l+r可能會超過正數的範圍進行溢位 mergeSort(arr, l, mid); mergeSort(arr, mid + 1, r); merge(arr, l, mid, r); } private static void merge(int[] arr, int l, int mid, int r) { int[] hlep = new int[r - l + 1];//暫存陣列 int i = 0;//轉移陣列 int p1 = l;//左指標標記左邊的陣列的開頭 int p2 = mid + 1;//右指標標記右邊的開頭 while (p1 <= mid && p2 <= r) {//進行比較歸併 hlep[i++] = arr[p1] < arr[p2] ? arr[p1++] : arr[p2++]; } while (p1 <= mid) {//剩下的加入 hlep[i++] = arr[p1++]; } while (p2 <= r) {//剩下的加入 hlep[i++] = arr[p2++]; } //填回原來的陣列中 for (i = 0; i < r - l + 1; i++) { arr[l + i] = hlep[i]; } }
把整個序列分成二部分,分成一個的時候就已經是有序序列了,然後在進行合併就可以了
時間複雜度這裡就運用到了前面的master公式
T(N)=2T(N/2)+O(N)
a =2 b = 2 d= 1
所以整個的複雜度為
O(N*logN)
空間複雜度O(N)//因為借用到了一個等長的額外變數進行儲存中間值,所以空間複雜度是O(N)
-
歸併排序解決實際問題-小和問題,逆序對問題
- 小和問題:
public static int mergeSort(int[] arr, int l, int r) { if (l == r) { return 0; } int mid = l + ((r - l) >> 2); return mergeSort(arr, l, mid) + mergeSort(arr, mid + 1, r) + merge(arr, l, mid, r); } private static int merge(int[] arr, int l, int mid, int r) { int[] help = new int[r - l + 1]; int result = 0; int i = 0;//標記help的長度 int p1 = l; int p2 = mid + 1; while (p1 <= mid && p2 <= r) { //左右二邊都是有序的,如果右邊的值大於當前的值,代表從這個數以後的值都大於這個值,而且右邊的本來就在左邊的右邊 result += arr[p1] < arr[p2] ? arr[p1] * (r - p2 + 1) : 0; help[i++] = arr[p1] < arr[p2] ? arr[p1++] : arr[p2++]; } while (p1 <= mid){ help[i++] = arr[p1++]; } while ( p2 <= r) { help[i++] = arr[p2++]; } for (int j = 0; j < help.length; j++) { arr[l+j] = help[j]; } return result; }
利用歸併排序去解決問題,遞迴時找到遞迴出口和把大問題化成同樣的小問題
-
逆序對問題
只更改while裡面的程式碼既可 while (p1 <= mid && p2 <= r) { //左右二邊都是有序的,如果右邊的值大於當前的值,代表從這個數以後的值都大於這個值,而且右邊的本來就在左邊的右邊 result += arr[p1] > arr[p2] ? (mid - p1 + 1) : 0; help[i++] = arr[p1] <= arr[p2] ? arr[p1++] : arr[p2++]; }
裡面的help陣列要改成小於等於 不然會丟失一些數,前面小和是要保證相等的時候,前面的不動,後面的動,不然也會丟失一些資料值的