快速排序

快速排序效率高,空间复杂度底,在工作中一般是用的最多的排序算法,所以需要掌握。

在看快速排序之前,先看一下另一个比快排简单点的问题,就是荷兰国旗问题

给定一个数组arr,和一个数num,请把小于num的数放在数组的左边,等于num的数放在数组的中间,大于num的数放在数组的右边。
要求额外空间复杂度O(1),时间复杂度O(N)

解决思路:

在这里插入图片描述

准备准备三个指针less、more、cur

  • less指针,从数组的第一个元素的前一个位置开始,第一个元素的位置为0,所以less从-1开始
  • more指针,从数组最后一个元素的后一个位置开始,最后一个元素的位置是数组长度减一位置,所以more的值就是数组的长度
  • cur指针,当前比较的元素的位置,从第一个元素开始比较,也就是从0开始
  • 如果当前元素小于num,less的后一个位置的元素跟cur位置的元素交换。交换完成之后,当前位置cur加一,比较下一个元素
  • 如果当前元素大于num,more的前一个元素跟cur位置的元素交换。交换完成之后,此时交换回来的元素我们不知道它大于num还是小于num,所以cur位置不变,继续比较
  • 如果当前元素的值等于num,cur直接加一,比较下一个。

代码如下

  public static int[] partition(int[] arr, int num) {
        if(arr == null || arr.length < 2){
            return arr;
        }
        //less从数组第一个元素的前一个元素开始
        int less = -1;
        //more从数组的最后一个元素的后一个元素开始
        int more = arr.length;
        //cur 当前比较的位置
        int cur = 0;
        while (cur < more) {
            if (arr[cur] < num) {
                //less的后一个位置和cur位置元素交换
                //交换之后,cur加一
                swap(arr, ++less, cur++);
            } else if (arr[cur] > num) {
                //cur和more位置的前一个交换
                swap(arr, --more, cur);
            }else {
                cur++;
            }
        }
        //返回等于num数据的位置
        return new int[] { less + 1, more - 1 };
    }

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

快速排序跟荷兰国旗算法的思想差不多,不同的是荷兰国旗算法是给了一个num值用来比较,快速排序中,我们自己从数组中随便找一个数来作为比较值。数组分成两部分之后,这两部分按照同样的方式递归在分自己,最后完成排序

解决思路

在这里插入图片描述

  • 首先选一个参照点,一般选择数组的最后一个值当做参照点
  • 之后的交换思路就跟荷兰国旗思路一样了
  • 需要注意,如果需要排序的数据本来就是一个有序的数组,那么如果一直选择最后的点作为参考点,那么每次分区得到的两个区间是不相等的,每次都要进行n次分区操作,每次分区大约扫描n/2个元素,时间复杂度会退化为O(N²)
  • 解决方式,每次不在取最后一个,从数组中随机取一个数,然后跟最后一个元素交换,在按照前的方式循环。改成随机事件,就从概率上降低了时间复杂度

代码如下

代码中直接使用left这个变量代替cur,可以省一个变量

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 left,int right){
        if(left<right){
            //在经典快排中,一般取最后一个位置的点作为参考点,但是加入数据本来就是有序的时候,快排的时间复杂度会退化到O(N²)
            //所以,可以在数组中随机找一个数来作为参考点,使用这种方法来降低时间复杂度
            //在left到right的范围中随机取一个数,把它放到最后一个位置
            swap(arr,left + (int)(Math.random()*(right-left+1)),right);
            //选一个参考点,把数组分区,比如选数组的最后一个点
            //把比它小的放到它的左边,比它大的放到它右边,相等的不动
            //返回的p数组中包含两个元素,这两个元素就是跟参考点相等的数的左边界和右边界
            int []p = partition(arr,left,right);
            //递归分区
            quickSort(arr,left,p[0]-1);
            quickSort(arr,p[1]+1,right);
        }
    }

    private static int[] partition(int[] arr, int left, int right) {
        //左边指针 最开始在数组最左边的前一个位置 也就是 -1 位置
        int less = left-1;
        //右边指针,最开始在数组最右边的位置(所以最后位置的那个数不参与循环)
        int more = right;
        // left变量 作为当前指针的位置
        while (left<more){
            //以数组最右边的数值为参考点
            //如果当前指针的位置的数小于参考点的数
            if(arr[left]<arr[right]){
                //左边指针的下一个位置的数和当前指针位置的数交换,然后当前位置加一
                //比如第一次的时候其实是数组第一个元素和自己交换
                swap(arr,++less,left++);
            }
            //如果当前指针的位置的数大于参考点的数
            else if(arr[left]>arr[right]){
                //当前指针位置的元素和数组最后一个元素交换
                swap(arr,left,--more);
            }else {
                //如果相等,当前位置的指针加一
                left++;
            }
        }
        //因为数组最后位置的数是最为参考点来的,没有参与循环,最后需要把它交换到正确的位置
        swap(arr,more,right);
        return new int[]{less+1,more};
    }

    public static void swap(int[] arr,int left,int right){
        int temp = arr[left];
        arr[left] = arr[right];
        arr[right] = temp;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值