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

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/lighthear/article/details/79465989

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快速排序的实际表现要快一些。


猜你喜欢

转载自blog.csdn.net/lighthear/article/details/79465989