1. 程式人生 > >Java常用的八種排序演算法與程式碼實現(二):歸併排序法、快速排序法

Java常用的八種排序演算法與程式碼實現(二):歸併排序法、快速排序法

注:這裡給出的程式碼方案都是通過遞迴完成的

---
歸併排序(Merge Sort):
  分而治之,遞迴實現
  如果需要排序一個數組,我們先把陣列從中間分成前後兩部分,然後對前後兩部分進行分別排序,再將排好序的數組合並在一起,這樣整個陣列就有序了
  歸併排序是穩定的排序演算法,時間複雜度為O(nlogn),空間複雜度是O(n)
  圖解:
  在這裡插入圖片描述
  程式碼:

/**
   * 歸併排序
   *
   * @param arr 待排陣列
   * @param low 歸併起始位置
   * @param high 歸併截至位置
   */
  public static void mergeSort(int[] arr, int low, int high) {
    int mid = (low + high) / 2;
    if (low < high) {
      // 分開
      mergeSort(arr, low, mid);
      mergeSort(arr, mid + 1, high);
      // 合併
      mergeArray(arr, low, mid, high);
    }
  }

  private static void mergeArray(int[] arr, int low, int mid, int high) {
    int[] temp = new int[high - low + 1];
    // 陣列被分成兩部分,進行合併
    // 兩個陣列中的元素進行比較,小的進入temp中
    int i = low;
    int k = 0;
    int j = mid + 1;
    while (i <= mid && j <= high) {
      if (arr[i] <= arr[j]) {
        temp[k++] = arr[i++];
      } else {
        temp[k++] = arr[j++];
      }
    }
    while (i <= mid) {
      temp[k++] = arr[i++];
    }
    while (j <= high) {
      temp[k++] = arr[j++];
    }
    // 將temp陣列中的資料,重新灌入被排序陣列,此時陣列順序已經排好
    for (int l = 0; l < temp.length; l++) {
      arr[low + l] = temp[l];
    }
  }

注:
  空間複雜度又稱漸進空間複雜度,指的是隨資料量的增加,額外需要使用的空間開銷。前一次歸併後的陣列空間,可以被接下來的操作使用。所以該排序的空間複雜度是O(n)

---
快速排序(QuockSort):
  如果需要排序陣列中下標從p到r之間的一組資料,我們會選擇從p到r之間的任意一個數據作為區分點,我們遍歷p到r之間的資料,將小於區分點的資料放左邊,大於區分點的資料放右邊,然後將區分點放到中間
  快速排序是一種原地排序,不穩定的排序,時間複雜度為O(nlogn),空間複雜度是O(n)
  圖解:
  在這裡插入圖片描述在這裡插入圖片描述  程式碼:

/**
   * 快速排序
   *
   * @param arr 待排陣列
   * @param low 排序起始位置
   * @param high 排序終止位置
   */
  public static void quockSort(int[] arr, int low, int high) {
    if (low > high) {
      return;
    }
    int i = low;
    int j = high;
    int pivot = arr[low];
    while (i < j) {
      // 如果選取兩端的數作為pivot,那麼迴圈要從基數的對面開始,選取中間的基數則不需要考慮這個問題
      while (i < j && arr[j] > pivot) {
        j--;
      }
      while (i < j && arr[i] <= pivot) {
        i++;
      }
      if (i < j) {
        int c = arr[i];
        arr[i] = arr[j];
        arr[j] = c;
      }
    }
    int p = arr[i];
    arr[i] = arr[low];
    arr[low] = p;
    quockSort(arr, low, i - 1);
    quockSort(arr, i + 1, high);
  }

---
  測試方法及生成隨機陣列方法:

/**
   * 生成一個長度5-10的隨機陣列
   *
   * @return 隨機陣列
   */
  private static int[] initArray() {
    int len = 5 + new Random().nextInt(6);
    int[] arr = new int[len];
    for (int i = 0; i < arr.length; i++) {
      arr[i] = new Random().nextInt(100);
    }
    return arr;
  }

public static void main(String[] args) {
    int[] arr2 = initArray();
    System.out.println("排序之前的陣列是:" + Arrays.toString(arr2));
    quockSort(arr2,0,arr2.length-1);
    System.out.println("排序之後的陣列是:" + Arrays.toString(arr2));
  }

---
  關於 如果選取兩端的數作為pivot,那麼迴圈要從基數的對面開始 的參考部落格:
https://blog.csdn.net/w282529350/article/details/50982650

---
測試結果,親測有效:

歸併:
  排序之前的陣列是:[71, 79, 11, 20, 68, 78, 95]
  排序之後的陣列是:[11, 20, 68, 71, 78, 79, 95]
快速:
  排序之前的陣列是:[44, 25, 17, 44, 71, 25, 66, 96, 36]
  排序之後的陣列是:[17, 25, 25, 36, 44, 44, 66, 71, 96]