算法-随机快排及荷兰国旗优化

算法介绍 :

随机快速排序和传统的快速排序的逻辑本质是一致的,都是找到一个值作为划分的中间位置,左边数值均小于该数值,右边数值均大于该数值,但是与传统的快排又不一致的是,我们的这个位置是通过随机获得的,对于随机行为的时间复杂度不能简单的按照最坏的情况解读

1. 随机快排解析

下面是我们随机快排的代码实现

public class Sort {
    public static void quickSort(int[] nums) {
        if (nums == null || nums.length <= 1) {
            return;
        }

        quickSort(nums, 0, nums.length - 1);
    }

    //下面是经典版本随机快排的函数主体
    private static void quickSort(int[] nums, int left, int right) {
        if (left >= right) {
            return;
        }
        //在left -- right范围上随机出来一个数字
        int randn = nums[left + (int) (Math.random() * (right - left + 1))];
        int boundary = partiton(nums, left, right, randn);
        quickSort(nums, left, boundary - 1);
        quickSort(nums, boundary + 1, right);
    }

    //经典快排的partition过程
    private static int partiton(int[] nums, int left, int right, int randn) {
        //逐个遍历的下标
        int i = left;
        //用来区别 <= randn的边界位置(已经越界了)
        int j = left;
        //记录找到的x的位置(用于交换)
        int randIndex = left;
        while (i <= right) {
            if (nums[i] <= randn) {
                swap(nums, i, j);
                if (nums[j] == randn) {
                    randIndex = j;
                }
                i++;
                j++;
            } else {
                i++;
            }
        }
        swap(nums, j - 1, randIndex);
        return j - 1;
    }

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

下面主要解析一下上述代码的逻辑是什么
首先我们通过
(int)(Math.random()*(right - left + 1)) + left
这段代码可以随机产生一个left – right的下标值
然后我们就拿到该位置的随机数作为划分
之后通过partiton过程来进行快排的逻辑,其中定义了两个指针,一个遍历数组下标,一个进行边界位置的扩大,当遍历到随机数的时候我们就记录下来该随机数的下标,然后通过左右区间调用递归过程完成排序, 但是这个快排的逻辑在leetcode上是跑不过全部的,因为你一轮只能排出来一个数据

2. 荷兰国旗问题

在这里插入图片描述
荷兰国旗问题可以为我们的接下来的排序优化打下基础, 上面的排序说了我们创造了一个左边界线, 那为什么不创建一个有边界线呢 ? 荷兰国旗就是这样解决的,但是要注意的是,在移动右边界线的时候,遍历的下标值不可以移动,在移动左边界线却可以, 因为左边已经遍历过一轮了, 可以确定交换过来的元素属性,但是右边是不确定的, 代码实现如下

class Solution {
    public void sortColors(int[] nums) {
        int first = 0;
        int last = nums.length - 1;
        int i = 0;
        while(i <= last){
            if(nums[i] == 0){
                swap(nums,i++,first++);
            }else if(nums[i] == 1){
                i++;
            }else{
                swap(nums,i,last--);
            }
        }
    }
    public void swap(int[] nums,int i,int j){
        int temp = nums[i];
        nums[i] = nums[j];
        nums[j] = temp;
    }
}

3. 随机快排优化

有了上面的荷兰国旗的基础,我们的快排优化也是这样做的,逻辑也是定出来两个边界,然后逐个进行移动…
代码实现如下


    /**
     * 下面是根据荷兰国旗问题的改进版本(分为三个区间, 一次搞定一批)
     * 下面这个两个属性是左右边界的越界位置
     */
public class Sort{
    public int first = 0;
    public int last = 0;

    public void quickSortImprove(int[] nums) {
        if (nums == null || nums.length <= 1) {
            return;
        }
        quickSortImprove(nums, 0, nums.length - 1);
    }

    public void quickSortImprove(int[] nums,int left,int right){
        if (left >= right) {
            return;
        }

        int randn = nums[left + (int) (Math.random() * (right - left + 1))];
        partitonImprove(nums,left,right,randn);
        int fir = first;
        int la = last;
        quickSortImprove(nums,left,fir - 1);
        quickSortImprove(nums,la + 1, right);
    }
    private void partitonImprove(int[] nums,int left,int right,int randn){
        first = left;
        last = right;
        int i = left;
        while(i <= last){
            if(nums[i] < randn){
                swap(nums,i++,first++);
            }else if(nums[i] == randn){
                i++;
            }else{
                swap(nums,i,last--);
            }
        }
    }
    private static void swap(int[] nums, int i, int j) {
        int temp = nums[i];
        nums[i] = nums[j];
        nums[j] = temp;
    }
}

在这里插入图片描述

4. 总结随机快排

随机快速排序算法

  • 随机快速排序的经典版本就是下面这样,随机出一个位置作为分界点,然后左右递归…
  • 我们下面的快排代码里面的 j 是作为左/右边界的越界位置存在 --> 随着下标指针的遍历(边界在不断地扩大)
  • Math.random方法的常数时间复杂度属于随机行为的时间复杂度
  • 荷兰国旗问题其实是随机快速排序的一种优化方式
  • 因为原本我们随即快排一次只能搞定一个数字, 优化之后我们一次可以搞定一批 x == randNum 的数据
  • 随机快速排序的时间复杂度与空间复杂度的估计
  • 关于随机行为的时间复杂度的估计不可以采用最坏的情况估计(因为最坏的情况就是无穷大),应该采用期望的方式估计
  • 最好情况(正好是中点位置) --> 时间复杂度 O(N*LogN)(Master公式) 空间复杂度O(LogN)(堆栈中树的高度)
  • 最坏情况(边界位置) --> 时间复杂度 O(N^2) 空间复杂度O(N)(堆栈中树的高度)
  • 经过大批数学家的证明(因为最终的时间复杂度应该算的是期望值(包含随机行为作为核心步骤))
  • –> 结论就是 时间复杂度 O(N*LogN) 空间复杂度O(LogN) --> 详见算法导论(有详细证明)
  • 所以 : 随机快排要比普通的快排更加优秀
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值