【每天进步一点点】快速排序(Quick sort)

208 阅读1分钟

快速排序

前置知识:荷兰国旗问题1荷兰国旗问题2

有了荷兰国旗问题这个前置知识,快排就变得很简单了

此文章所使用的快排为:随机快排

快速排序是基于 “分治法” 原理实现。

快排实际上就是不断递归,将数组不断的分为左右两个子过程(中间等于 target 的部分则不用继续排序,相当于是一个小的加速过程),直至将数组切割至长度为 1 或 2 再进行 partition,就完成了一次最小规模的排序,也称为 base case。

代码部分

public class 快速排序 {
    public static void main(String[] args) {
        int[] arr = {5,2,4,8,9,6,2,5,3,2,1,4,7,8,5,3,6};
        quickSort(arr,0,arr.length-1);
        for (int i = 0; i < arr.length; i++) {
            System.out.print(arr[i] + "  ");
        }
    }

    public static void quickSort(int[] arr,int L,int R) {
        if (null == arr && arr.length <= 1) {
            return;
        }
        if (L <= R) { 
            /**
             * 完全随机值,避免了人为干扰,复杂度遵循概率统计规律,为 N * logN(以 2 为底)
             */
            swap(arr,L + (int) (Math.random() * (R - L + 1)), R); //给定范围的随机数
            int[] p = partition(arr,L,R);
            quickSort(arr, L, p[0] - 1); // 小于区域
            quickSort(arr,p[1] + 1,R); // 大于区域
        }
    }

    /**
     * @param R 随机挑选出来的基准数,作为荷兰国旗问题的 target值
     * @return int[] 数组包含两个数:划分值相等的左边界和右边界下标
     */
    public static int[] partition(int[] arr,int L,int R) {
        int less = L-1;
        int more = R+1;
        //int i = 0; // 不能用这个来代替数组下标了,要用 L 来代替,因为不一定都是从0 开始的了
        while (L < more) { // L 表示当前数
            if (arr[L] < arr[R]) { // 当前数 < arr[R](target值)
                swap(arr,++less,L++);
            } else if (arr[L] == arr[R]) { // 当前数 = target值
                L++;
            } else { // 当前数 > target值
                swap(arr,--more,L);
            }
        }
        return new int[] {less+1, more-1}; // target相等区域的左边界、右边界下标
    }

    public static void swap(int[] arr,int i,int j) {
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }
}

时间复杂度

n * log n

空间复杂度

n * log n

排序稳定性

因为 partition 过程中的不断交换的缘故,导致可能会将相同数字的相对次序打乱

快排算法的优缺点

优点:

① 随机快排避免了人为构造的最差情况 O(N²),将时间复杂度转化为具有概率统计规律的 O(n * ㏒ n)

② 排序效率很高

缺点:

① 不能保证排序的稳定性

② 小数据量时性能不是很优秀