464. Sort Integers II (Quick Sort & Merge Sort)

Description

Given an integer array, sort it in ascending order. Use quick sort, merge sort, heap sort or any O(nlogn) algorithm.

Example

Given [3, 2, 1, 4, 5], return [1, 2, 3, 4, 5].

Solution

1. 快速排序 Quick Sort

平均時間複雜度O(nlogn),最壞時間複雜度O(n^2),空間複雜度O(1),不穩定排序(排序之後原來相同元素的順序也會改變)

做法:隨便選擇一個數字作爲中心點P(一般取中點),小於等於P放左邊,大於等於P放右邊,然後遞歸左半部分和右半部分進行排序。

思想:先整體有序再局部有序,先看起來宏觀上有序再調整微觀有序。

關鍵點:

  1. pivot 取中間點int pivot = A[(start + end) / 2]
  2. left <= right,保證左右指針能交錯,防止StackOverFlow
  3. A[left] < pivot 而不是 A[left] <= pivot,否則如果所有元素都相等則會直到末尾,不能均分數組,複雜度會退化到O(n^2)

記憶邊界條件:

  1. left <= right;
  2. A[left] < pivot; A[right] > pivot;
public class Solution {
    /**
     * @param A: an integer array
     * @return: nothing
     */
    public void sortIntegers2(int[] A) {
        // write your code here
        if (A == null || A.length == 0) {
            return;
        }
        quickSort(A, 0 ,A.length - 1);
    }
    private void quickSort(int[] A, int start, int end) {
        if (start >= end) {
            return;
        }
        int left = start, right = end;
        //key point 1: pivot is the value, not the index
        int pivot = A[(start + end) / 2];
        
        // key point 2: every time you compare left & right, it should be 
        // left <= right not left < right
        //保證兩個指針錯開,防止因重複排序,沒有降低問題規模而造成的內存越界
        while (left <= right) {
            while (left <= right && A[left] < pivot) {
                //找到一個大於等於中心點的
                left++;
            }
            while (left <= right && A[right] > pivot) {
                //找到一個小於等於中心點的
                right--;
            }
            if (left <= right) {
                //左右交換,之後指針往後移
                int temp = A[left];
                A[left] = A[right];
                A[right] = temp;
                left++;
                right--;
            }
        }
        //先整體有序之後再遞歸quickSort使得局部也有序
        //while結束之後right會到left的左面,所以是從start到right排序,再從left到end排序
        quickSort(A, start, right);
        quickSort(A, left, end);
    }
}

2. 歸併排序 Merge Sort

時間複雜度穩定在O(nlogn),空間複雜度O(n),穩定排序(排序之後原來相同元素的順序不會改變)。

做法:上來先取半,左邊排序,再右邊排序,然後合併到一起。

思想:先局部有序再整體有序

public class Solution {
    /**
     * @param A: an integer array
     * @return: nothing
     */
    public void sortIntegers2(int[] A) {
        // write your code here
        if (A == null || A.length == 0) {
            return;
        }
        int[] temp = new int[A.length];
        mergeSort(A, 0 , A.length - 1, temp);
    }
    private void mergeSort(int[] A, int start, int end, int[] temp) {
        if (start >= end) {
            return;
        }
        //左右分別歸併排序
        mergeSort(A, start, (start + end) / 2, temp);
        mergeSort(A, (start + end) / 2 + 1, end, temp);
        //合併左右結果
        merge(A, start, end, temp);
    }
    private void merge(int[] A, int start, int end, int[] temp) {
        int middle = (start + end) / 2;
        int leftIndex = start;
        int rightIndex = middle + 1;
        int index = start;
        while (leftIndex <= middle && rightIndex <= end) {
            //左邊的索引小於中間,右邊的不超過end
            if (A[leftIndex] < A[rightIndex]) {
                //左邊小就存左邊的數到temp中
                temp[index++] = A[leftIndex++];
            } else {
                //右邊小就存右邊的數到temp中
                temp[index++] = A[rightIndex++];
            }
        }
        //循環結束之後可能有一個數組還沒結束,把剩下的存到temp中
        while (leftIndex <= middle) {
            temp[index++] = A[leftIndex++];
        }
        while (rightIndex <= end) {
            temp[index++] = A[rightIndex++];
        }
        //將temp中排好的數存回A數組中
        for (int i = start; i <= end; i++) {
            A[i] = temp[i];
        }
    }
}

總結:

二者都是將O(n)的問題分成兩個 O(n/2)的問題,分的過程和合的過程在這一層耗費O(n)的時間複雜度,即T(n) = 2 * T(n/2) + O(n)。

區別在於完成的先後順序不同:

  1. 快速排序先分割成左右兩半(小於中間點放左邊,大於的放右邊),完成了O(n)這件事情,再去遞歸完成2個T(n/2)的事情。
  2. 歸併排序是先遞歸到左右兩邊進行排序,分別完成左右兩邊的排序之後(完成2個T(n/2)的事情),再將結果進行合併(完成O(n)這件事情)。

Marge Sort歸併排序的劣勢在於需要額外使用O(n)的空間(合併兩個數組的時候),開闢和回收額外空間很浪費時間。對於穩定性的要求並不是時時刻刻都有,而且即使有穩定性的要求也可以通過修改compartor返回滿足條件的值來實現。所以Quick Sort快速排序的實際表現要快一些。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章