10.经典快排和快排加速

随机快速排序的细节和复杂度分析

可以用荷兰国旗问题来改进快速排序,时间复杂度0(N*logN),额外空间复杂度O(logN)

经典快排

在这里插入图片描述

从数列中随机挑出一个元素作为基准,比如将一个数组的最后一个元素x作为基准,将小于等于x的元素移动到数组前部分,将大于x的元素移动到数组后部分,分治的思想继续将上一次的小于等于x和大于x的两块再次重复以上排序步骤,最终部分有序从而整体有序。函数minuteArray将数组[l,r]区间上的元素以num为基准分成大于和小于num的前后两部分,返回值x是小于区域的最后一个元素下标;quickSort函数是经典快排的核心函数。

package yzy.algorithm;

public class classicFastRow {
    //给定一个数组arr,和一个数num,请把小于等于num的数放在数 组的左边,大于num的数放在数组的右边
    public static int minuteArray(int[] arr, int l, int r, int num){
        int x = l-1;  //小于等于区域[0,x]
        int i=l;
        while(i<arr.length){
            if(arr[i]<=num) {
                int tmp = arr[i];
                arr[i] = arr[x + 1];
                arr[x + 1] = tmp;
                x += 1;
            }
            i++;
        }
        return x;
    }
    //经典快排
    public static void quickSort(int[] arr){
        if(arr == null || arr.length < 2)
            return ;
        quickSort(arr, 0, arr.length-1);
    }
    public static void quickSort(int[] arr, int L, int R){
        if(L < R) {
            swap(arr, L+(int)(Math.random()*(R-L+1)), R);
            int x = minuteArray(arr, L, R, arr[R]);
            quickSort(arr, L, x);
            quickSort(arr, x + 1, R);
        }
    }
    //对数器: 生成一个长度范围在[1, size]的任意长度并且任意位置是随机值的数组
    //随机值的范围在:[0, value]
    public static int[] generateRandomArray2(int size, int value) {
        //Math.random()  返回[0, 1)范围中的随机小数
        //(size+1)*Math.random()  [0, size+1)-->[0, size]
        int[] arr = new int[(int)((size+1)* Math.random())+1];  //(size+1)* Math.random()是[0, size]范围中的随机小数
        for(int i=0; i<arr.length; ++i) {
            arr[i] = (int) ((value+1)* Math.random());
        }
        return arr;
    }
    public static void showArr(int[] arr){
        for(int i=0; i<arr.length; ++i){
            System.out.print(arr[i]+" ");
        }
        System.out.println();
    }
    public static void main(String[] args) {
        int[] arr = generateRandomArray2(10, 10);
        arr = new int[]{3, 5, 8, 10, 7, 2 };

        System.out.print("对数器产生的数组:");
        showArr(arr);
        quickSort(arr);
        System.out.print("结果数组:");
        showArr(arr);
    }
}

以上代码中需要随机挑选一个元素作为基准,不然很大概率会栈溢出,即使改成随机快排样本量大一点任然会栈溢出,因此以上代码只是粗糙的模拟快排

使用荷兰国旗加速的快排

荷兰国旗的核心函数是partition,将数组[L,R]区间上的数组元素以基准num分成小于等于大于三部分,返回值以数组形式返回等于num的闭区间下标。用荷兰国旗改进的快排quickSort2递归的方式只对每层递归小于和大于两部分继续进行分治,减少了等于num区间的样本量,从而做到对快排加速

package yzy.algorithm;

public class classicFastRow {
    //改进的荷兰国旗
    public static void swap(int[] arr, int i, int j){
        int tmp = arr[i];
        arr[i] = arr[j];
        arr[j] = tmp;
    }
    //数组下标范围:闭区间
    public static int[] partition(int[] arr, int L, int R){
        int less = L - 1;
        int more = R + 1;
        int num = arr[R];
        while(L < more){
            if(arr[L] < num)
                swap(arr, ++less, L++);
            else if(arr[L] > num)
                swap(arr, --more, L);
            else
                L++;
        }
        return new int[] {less+1, more-1};
    }
    //荷兰国旗改进的快排
    public static void quickSort2(int[] arr){
        if(arr == null || arr.length < 2)
            return ;
        quickSort2(arr, 0, arr.length-1);
    }
    public static void quickSort2(int[] arr, int L, int R) {
        if(L < R){
            swap(arr, L+(int)(Math.random()*(R-L+1)), R);
            int[] p = partition(arr, L, R);
            quickSort2(arr, L, p[0]-1);
            quickSort2(arr, p[1]+1, R);
        }
    }
    //对数器: 生成一个长度范围在[1, size]的任意长度并且任意位置是随机值的数组
    //随机值的范围在:[0, value]
    public static int[] generateRandomArray2(int size, int value) {
        //Math.random()  返回[0, 1)范围中的随机小数
        //(size+1)*Math.random()  [0, size+1)-->[0, size]
        int[] arr = new int[(int)((size+1)* Math.random())+1];  //(size+1)* Math.random()是[0, size]范围中的随机小数
        for(int i=0; i<arr.length; ++i) {
            arr[i] = (int) ((value+1)* Math.random());
        }
        return arr;
    }
    public static void showArr(int[] arr){
        for(int i=0; i<arr.length; ++i){
            System.out.print(arr[i]+" ");
        }
        System.out.println();
    }
    public static void main(String[] args) {
        int[] arr = generateRandomArray2(10, 10);
        arr = new int[]{3, 5, 8, 10, 7, 2 };

        System.out.print("对数器产生的数组:");
        showArr(arr);
        quickSort2(arr);
        System.out.print("结果数组:");
        showArr(arr);
    }
}

对意以上荷兰国旗加速的快排继续进行改进,以上加速快排的荷兰国旗的分块函数partition是以最后一个元素作为基准的,基准在整个分块中也参与了分块,改进之后只对最后一个基准前面的元素进行分块,然后将基准与大于块的第一个元素交换,满足小于等于大于三个分块规则

    //对partition进行改进 数组下标范围:闭区间
    public static int[] partition2(int[] arr, int L, int R){
        int less = L - 1;
        int more = R;
        while(L < more){
            if(arr[L] < arr[R])
                swap(arr, ++less, L++);
            else if(arr[L] > arr[R])
                swap(arr, --more, L);
            else
                L++;
        }
        swap(arr, more, R);
        return new int[] {less+1, more-1};
    }

上述经典快排的代码当数据量稍微大一点,就容易栈溢出比如这样的数据:2 8 3 9 0 2 4 6 7 0 1 ,发现好玩的是五个元素的数组是不会栈溢出的

经典快排存在的问题:每次partition与数据状况有关系。比如小于或大于的块特别大

最好的时间复杂度才达到O(NlogN),随机快排的复杂度是一个概率问题,基准的选择可能落到任何一个点上。经典快排的时间复杂度结果是长期期望的值:O(NlogN)

快排使用广泛得益于与其它同样时间复杂度的排序算法下,快排的常数项最小所以快排被大量使用,注意递归都是可以改成非递归形式的,因此用于工程中的快排必然是非递归形式
随机快排的额外空间复杂度

最好情况空间复杂度:O(logN)

最差情况空间复杂度:O(N)

长期期望:O(logN)

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值