【算法练习】重点总结系列 快速排序和快速选择

没事就多看

相关leetcode算法题:

​​​​​​剑指 Offer 40. 最小的k个数

215. 数组中的第K个最大元素

参考链接:快速排序亲兄弟:快速选择算法 :: labuladong的算法小抄

方法一 二叉堆(小顶堆)   √

方法二 快速选择 

剑指 Offer 45. 把数组排成最小的数

1985. 找出数组中的第 K 大整数

快速排序的逻辑

       void quickSort(int[] arr, int l, int r) {
            // 子数组长度为 1 时终止递归
            if (l >= r) return;
            // 哨兵划分操作(以 arr[l] 作为基准数)
            int i = l, j = r;
            while (i < j) {
                while (i < j && arr[j] >= arr[l]) j--;
                while (i < j && arr[i] <= arr[l]) i++;
                swap(arr, i, j);
            }
            swap(arr, i, l);
            // 递归左(右)子数组执行哨兵划分
            quickSort(arr, l, i - 1);
            quickSort(arr, i + 1, r);
        }
      void swap(int[] arr, int i, int j) {
            int tmp = arr[i];
            arr[i] = arr[j];
            arr[j] = tmp;
        }

快速排序的逻辑是要对数组arr[l...r]排序,就需要找一个分界点p,使得

arr[l...p-1] 都小于等于arr[p] 

arr[p+1...r]都大于等于arr[p]

然后递归地在arr[l...p-1] 和 arr[p+1...r]中寻找

快速排序中partition的写法

参考:力扣https://leetcode-cn.com/problems/zui-xiao-de-kge-shu-lcof/solution/jian-zhi-offer-40-zui-xiao-de-k-ge-shu-j-9yze/

1、初始化“哨兵”索引位置,以arr[l]为基准数

2、循环交换,两哨兵相遇时跳出

   从右向左 查找 首个小于基准数的元素

   从左向右 查找 首个大于基准数的元素

   交换 arr[i] 和 arr[j]

3、交换基准数 arr[i] 和 arr[l]

while(i<j){
    while(i<j && arr[j]>=arr[l]) j--;
    while(i<j && arr[i]<=arr[l]) i++;
    swap(arr,i,j);
}
    swap(arr,i,l);

文中的写法这两个 while【指的是第一个嵌套的while】执行完, i j 同时指向一个 < arr[l] 的数,因此最后再执行 arr[l], arr[i] = arr[i], arr[l] 可以把哨兵交换到正确的位置。 而如果互换这两句,那么就是 i 先向右遍历,两个 while 执行完, i j 同时指向一个 > arr[l] 的数,那么就不对了。如果要交换写,那么同时也要把哨兵换成数组的末元素,让整个哨兵划分操作对称。

swap函数必须传入引用类型 也就是数组才可以实现交换,不可以写两个int直接交换

void swap(int[] arr, int i, int j) {
        int tmp = arr[i];
        arr[i] = arr[j];
        arr[j] = tmp;
}

原因:

引用类型才可以交换。因为原生数据类型是直接赋值,而引用类型直接赋的是地址,地址指向的是同一个对象,所以能改变。

快速排序的非递归写法

class Solution {
    public static void main(String[] args) {
        int[] nums=new int[]{9,1,8,2,7,3};
        Solution.stackQuickSort(nums,0, nums.length-1);
        for (int num:nums){
            System.out.println(num);
        }
    }

    public static void stackQuickSort(int[] nums,int left,int right){
        Stack<Integer> stack=new Stack<>();
        if (left<right){
            stack.push(right);
            stack.push(left);
        }
        while (!stack.isEmpty()){
            int l=stack.pop();
            int r=stack.pop();
            int index=quickSort(nums,l,r);
            if (l<index-1){
                stack.push(index-1);
                stack.push(l);
            }
            if (r>index+1){
                stack.push(r);
                stack.push(index+1);
            }
        }
    }

    public static int quickSort(int[] nums,int left,int right){
        int center=nums[left];
        int i=left;
        int j=right;
        while (i<j){
            while (i<j&&nums[j]>=center)j--;
            while (i<j&&nums[i]<=center)i++;
            int temp=nums[j];
            nums[j]=nums[i];
            nums[i]=temp;
        }
        nums[left]=nums[i];
        nums[i]=center;
        return i;
    }
}

快速选择的写法

partition 函数会将 nums[p] 排到正确的位置,使得 nums[lo..p-1] < nums[p] < nums[p+1..hi]

二分搜索+partition

那么我们可以把 p 和 k 进行比较,如果 p < k 说明第 k 大的元素在 nums[p+1..hi] 中,如果 p > k 说明第 k 大的元素在 nums[lo..p-1] 中

所以我们可以复用 partition 函数来实现这道题目,不过在这之前还是要做一下索引转化。

 public int findKthLargest(int[] nums, int k) {
        //随机打乱
        shuffle(nums);
        int lo = 0;
        int high = nums.length-1;
        int index = nums.length-k;
        while(lo<=high){
            int p = partition(nums,lo,high);  //确定了分界点索引p
            if(p<index){
                lo=p+1;
            }else if(p>index){
                high=p-1;
            }else{
                return nums[index];
            }
        }
        return -1;
    }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值