1. 程式人生 > 實用技巧 >排序演算法的java實現及複雜度總結1

排序演算法的java實現及複雜度總結1

各種排序演算法總結和複雜度總結:

  1. 氣泡排序

     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)

  2. 對數器的概念(這個後面自己在進行總結各種對數器,看老師的給的自己總結下來)

    • 想測試的方法A
    • 絕對正確但是複雜度高的B
    • 產生一個輸入的隨機樣本,短的就行
    • 二方法輸出結果進行比較
  3. 選擇排序

     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)

  4. 插入排序

    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;
            }
        }
    }
    

    插入排序就像打牌一樣,把新抓進來的數 從尾到頭進行插入

  5. 遞迴函式的時間複雜度的估算方法

    利用到的是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))

  6. 歸併排序

    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)

  7. 歸併排序解決實際問題-小和問題,逆序對問題

    • 小和問題:
    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陣列要改成小於等於 不然會丟失一些數,前面小和是要保證相等的時候,前面的不動,後面的動,不然也會丟失一些資料值的