分治-快排思想题集1

目录

1 颜色分类

分析:

代码展示:

 2 排序数组

分析:

代码展示:

3 数组中的第K个最大元素

​编辑

分析:

代码展示:

4 库存管理 III

分析:

代码展示:


颜色分类

分析:

这道题和之前双指针系列第一道题非常相似,只是这里需要多划分一个区域,核心思路是一样的。大家有兴趣可以看一下,

题目链接:. - 力扣(LeetCode)

题解:CSDN

这道题中我们需要三个指针,数组左边到left存储0,left + 1 到 right - 1 存储1; right到数组最右边存储2.此时还有一个指针就是用来遍历数组,如果i下标值是0,因为最左边到left都表示0,left + 1不为0,所以此时i遍历到0,就需要让left+1后再和 i 下标对应的值0换位置.换完位置后i++;同理right和i 的交换规则也是一样的,如果遇到2,那就需要让right--,然后和 i 下标所在值交换,注意交换玩后不能i++,因为right--1所对应的值是未经i遍历的,所以无法确定该值是多少。

代码展示:
class Solution {
    public void sortColors(int[] nums) {
        int left = -1;
        int right = nums.length;
        int  i = 0;
        while(i < right) {
            if(nums[i] == 0 ) {
                swap(nums, i++ , ++left);
            }else if (nums[i] == 1) {
                i++;
            }else {
                swap(nums, i , --right);
            }
        }
    }
    public static void swap(int[] arr , int i , int j) {
        int tmp = arr[i];
        arr[i] = arr[j];
        arr[j] = tmp;
    
    }
}
 2 排序数组

分析:

众所周知,快排在面对重复数字比较多的时候,时间复杂度接近n*n,但是当我们用三色划分的思想来做时,当一个数组重复数字较多时,时间接近n。这样大大的提升了效率。

这道题是上一道题的扩展,只不过上一道题只需要i遍历一次即可。这道题需要用到递归。简单分析一下,我们随机找一个数为基准key,依然用i遍历,当arr[i] == key ,i++,当arr[i]小于(大于)k,时,交换left(right)区域中的值,这样一遍下来后,key左边的均是小于key的,key右边的均是大于key的,一直重复下去,left 和 right 边界重合然后return。

代码展示:
public static void swap(int[] arr , int i , int j) {
        int tmp = arr[i];
        arr[i] = arr[j];
        arr[j] = tmp;
    }
    //输入:nums = [5,2,3,1]
    //输出:[1,2,3,5]
    //快速排序,三指针法
    public static int[] sortArray(int[] nums) {
        //调用三色划分的函数
        quickSort(nums,-1, nums.length);
        return nums;
    }

    public static void quickSort(int[] arr , int left , int right) {
        //如果左右边界重合那么就返回
        if(right - 1 <= left + 1) return;
        //因为left和right是边界值,因此我们不能移动left和right,不然递归的时候就无法确定边界情况,所以赋值给新的变量
        int i = left + 1;
        int l = left;
        int r = right;
        //生成随机数,来随机指定基准
        Random random = new Random();
        int par = random.nextInt(left + 1 , right  );
        int key = arr[par];
        //三色划分的代码
        while(i < r) {
            if(arr[i] < key) {
                swap(arr, i++ ,++l);
            }else if(arr[i] == key) {
                i++;
            }else {
                swap(arr,i,--r);
            }
        }
        //我们传入的是left++,right--和第一次传参传入(-1,nums.length)原理是一样的,数组第一个值是未处理的数,left的意义是,指向最后一个符合条件的数
        //我们不知道第一个数是否符合条件,因此left要指向数组0下标之前,这也是为什么交换的时候要先对left++,在和i下标对应值进行交换
        quickSort(arr, left , ++l );
        quickSort(arr, --r , right);
    }

所有细节及可能产生的疑问都写在代码注释里了,不懂的可以对照看一下。

数组中的第K个最大元素
分析:

我们的常规思路是直接利用排序,排完之后输出第k个最大值,但是我们用分治的思想就可以让算法的时间复杂度接近O(n);拿快排(三色划分实现)为例,当我们排完一次序后,数组一定划为3部分,我们可以拿k和右区域(大于左区域和中间区域的元素,中间元素值均相等)里的元素数量进行比较,如果该区域元素数量大于k,那么说明第K大的元素一定在这个区域中,那么我们只需要对该区域进行排序即可;当右边区域的元素数量小于k时,那说明第k大的元素不在该区域内,此时如果中间区域加上最大区域的元素个数大于k,那么说明第k大的值在中间区域,直接返回中间区域的任意一个值即可。

代码展示:
public static int findKthLargest(int[] nums, int k) {
        int r = quickSort(nums,-1, nums.length,k);
        System.out.println(r);
        return r;
    }
    public static int quickSort(int[] arr, int left , int right,int k) {
        //当只剩一个元素时;这个if判断可有可无,因为我们后面的条件判定一会返回正确的结果。
        if(right - 1 == left + 1) return arr[left + 1];
        int l = left;
        int r = right;
        int i = left + 1;
        //生成基准
        Random random = new Random();
        int key = arr[random.nextInt(left + 1 ,right)];
        //和之前的快排代码一样

        while(i < r) {
            if (arr[i] == key) {
                i++;
            }else if (arr[i] < key) {
                swap(arr,++l,i++);
            }else {
                swap(arr,--r,i);
            }
        }
        //如果右边区域的元素个数大于k,那么结果一定在右区域
        if(right - r >= k) {
            return quickSort(arr,--r , right , k);
        }else if(right - l - 1 >= k) {
            return key;
        }else {
            int a = k - (right - l);
            //注意当所求数,在左边的时候,由于中间和右边区域的数一定大于左边的数,因此,我们传参的时候就要用k减去右边和中间元素的个数
            return quickSort(arr,left,++l,k - (right - l));
        }
    }
    public static void swap(int[] arr , int i , int j) {
        int tmp = arr[i];
        arr[i] = arr[j];
        arr[j] = tmp;
    }
库存管理 III

分析:

这道题也可以用堆排序做,求的是最小的k个数,那我们可以创建一个小根堆(必须掌握)。

在这里我们还是用归并的思想做,这道题和上道题的区别就是第k个变成了前k个,因此我们只需要转换判断一下条件即可。

当最小区域的数大于k的时候,那我们继续对最小区域的数进行排序,直到中间区域加上最小区域的数大于等于k,那我们就可以直接返回(因为题目中不要求有序,所以符合条件就直接返回),如果左区域和中间区域的元素个数小于k,那我们就只对右区域的(k- a-b,a,b分别为左,中区域的元素个数)个数进行排序即可。

代码展示:
 
public static void swap(int[] arr , int i , int j) {
        int tmp = arr[i];
        arr[i] = arr[j];
        arr[j] = tmp;
    }
    public static int[] inventoryManagement(int[] stock, int cnt) {
        quickSort1(stock,-1,stock.length,cnt);
        int[] r = Arrays.copyOf(stock,cnt);
        System.out.println(Arrays.toString(r));
        return r;

    }

    public static void quickSort1(int[] arr, int left , int right , int k) {
        int l = left;
        int r = right;
        int i = l + 1;
        int[] ret = new int[k];
        Random random = new Random();
        int key = arr[random.nextInt(left + 1 , right)];
        while(i < r) {
            if(arr[i] == key) {
                i++;
            }else if (arr[i] > key) {
                swap(arr,i , --r);
            }else {
                swap(arr,i++,++l);
            }
        }
        if(l - left > k) {
            quickSort1(arr,left,++l,k);
        }else if(r - left - 1 >= k) {
            return;
        }else{
            quickSort1(arr,--r,right,k - (r - left));
        }

    }

  • 28
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值