快排及partition和荷兰国旗问题

1 快排核心

1.1 partition 问题

  1. 问题描述:给定一个数组 arr 和一个数 num ,将小于等于 num 的数放在左边,大于 num 的数放在右边
  2. 解题思路:一个指针 p 指向小于等于区最后一个元素
    1. 若该元素小于等于 num,小于等于区扩展,并与小于等于区最后一个元素交换,然后 i++
    2. 若该元素大于 num,直接 i++
    3. 直到遍历完整个数组为止
  3. 代码实现
    void partition(int[] arr, int num) {
        int p = -1; // 小于等于区最后一个元素的指针p
        for (int i = 0; i < arr.length; i++) {
            if (arr[i] <= num) {
                swap(arr, i, ++p); // 扩展小于等于并交换
            }
        }
    }
    

1.2 荷兰国旗问题

  1. 问题描述:给定一个数组 arr 和一个数 num ,将小于 num 的数放在左边,大于 num 的数放在右边,等于 num 的数放在中间
  2. 解题思路:双指针,一个指针 L 指向小于区最后一个元素,一个指针 R 指向大于区第一个元素
    1. 若该元素小于 num,小于区扩展,并与小于区最后一个元素交换,i++
    2. 若该元素等于 num,直接 i++
    3. 若该元素大于 num,大于区扩展,并与大于区第一个元素交换
    4. 直到 i 指针碰到大于区第一个元素为止
  3. 代码实现
    void netherlandsFlag(int[] arr, int num) {
        int L = -1; // 指向小于区最后一个元素的指针
        int R = arr.length; // 指向大于区最后一个元素的指针
        int i = 0; 
        while (i < R) {
            if (arr[i] < num) {
                swap(arr, i++, ++L); // 扩展小于区并交换,i指针指向下一个元素
            } else if (arr[i] > num) {
                swap(arr, i, --R); // 扩展大于区并交换
            } else {
                i++;
            }
        }
    }
    

2 快速排序

2.1 快排 1.0

  1. 思路:玩 partition
    1. 每次将数组最后一个元素作为 num,0到倒数第二个元素进行 partition
    2. partition 完毕后,将最后一个元素与大于区第一个元素交换
    3. 然后在小于等于区和大于区分别玩 partition
    4. 每次能保证一个等于 num 的值得位置
  2. 代码实现
    int partition2(int[] arr, int L, int R) {
        int p = L - 1; // 小于等于区的指针
        for (int i = L; i < R; i++) {
            if (arr[i] < arr[R]) {
                swap(arr, i, ++p);
            }
        }
        swap(arr,++p,R); // 小于区等于区后面一个元素就是大于区第一个元素,然后大于区第一个元素与最后一个元素交换
        return p; // 返回partition后num的位置
    }
    
    void quickSort1(int[] arr, int L, int R) {
        if (L >= R) { // 左右指针重合跳出
            return;
        }
        int M = partition2(arr, L, R);
        quickSort1(arr, 0, M-1); // 在左边玩partition
        quickSort1(arr, M + 1, R); // 在右边玩partition
    }
    

2.2 快排 2.0

  1. 思路:玩荷兰国旗
    1. 每次将数组最后一个元素作为 num,0 到倒数第二个元素进行荷兰国旗
    2. 荷兰国旗完毕后,将最后一个元素与大于区第一个元素交换
    3. 然后在小于区和大于区分别玩荷兰国旗
    4. 每次能保证一组等于 num 的值得位置,所以比 1.0 快
  2. 代码实现
    int[] netherlandsFlag2(int[] arr, int L, int R) {
        int less = L-1;
        int more = R;
        int i = L;
        while (i < more) {
            if (arr[i] < arr[R]) {
                swap(arr, i++, ++less);
            } else if (arr[i] > arr[R]) {
                swap(arr, i, --more);
            } else {
                i++;
            }
        }
        swap(arr, more, R); // 将大于区第一与最后元素交换
        return new int[]{++less, more}; //第一个是等于区的开始,第二个是等于区的结束
    }
    
    void quickSort2(int[] arr, int L, int R) {
        if (L >= R) {
            return;
        }
        int[] flags = netherlandsFlag2(arr, L, R); 
        quickSort2(arr, 0, flags[0] - 1); //在小于区玩荷兰国旗
        quickSort2(arr, flags[1] + 1, R); //在大于区玩荷兰国旗
    }
    

2.3 随机快排

  1. 思路:在快排2.0之上,不指定元素作为num,玩荷兰国旗
  2. 为什么比快排2.0快?因为快排2.0和1.0都会出现全部数在左边或者右边的情况,理想情况是左右两边均等
  3. 代码实现
    void process3(int[] arr, int L, int R) {
        if (L >= R) {
            return;
        }
        swap(arr, L + (int) (Math.random() * (R - L + 1)), R); // 随机指定一个数作为 num
        int[] equalArea = netherlandsFlag(arr, L, R);
        process3(arr, L, equalArea[0] - 1);
        process3(arr, equalArea[1] + 1, R);
    }
    

3 总结

  1. 快排就是在玩 partition 或 荷兰国旗
  2. 因为荷兰国旗一次能确认多组相同的数的位置,所以比 partition 要快
  3. 随机快排极大减少最差情况的发生,所以效率更高
    1. 最差情况:一次荷兰国旗后,元素全在左边,或全在右边。时间复杂度:O(n2)
    2. 最好情况:一次荷兰国旗后,左右两边元素个数相当。时间复杂度:O(nlogn)
  4. 空间复杂度:O(logn)
  5. 稳定性:不稳定
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值