Leetcode hot100之“结合递归+二分“题目详解

1 总结

题目 215 ("数组中的第 K 个最大元素") 和题目 4 ("寻找两个正序数组的中位数") 之间的联系主要体现在它们都涉及到寻找一个有序集合中的第 k 个元素的问题。尽管这两个问题的具体应用场景和所处理的数据结构不同,它们共享相似的算法思想和技术。

题目 215 - 数组中的第 K 个最大元素

此题的解决方案涉及到快速选择算法,这是快速排序的一个变体。快速选择算法通过选择一个枢轴来划分数组,并基于枢轴的位置来决定继续在左边或右边搜索目标元素。该方法的目标是找到数组中第 k 个最大的元素。

题目 4 - 寻找两个正序数组的中位数

在这个问题中,目标是找到两个有序数组合并后的中位数。解决方案同样涉及到一种选择方法,即在两个数组中找到第 k 小的元素。这通常通过二分查找来实现,需要在两个数组中找到合适的分割线,以确保左右两侧元素的数量相等(或相差一个)。

共同点

  1. 有序集合中的元素选择:两个问题都涉及到在有序集合中选择特定顺序的元素(第 k 个最大或第 k 个最小)。

  2. 分治和递归思想:这两个问题都可以通过分治法和递归思想来解决。在问题 215 中,快速选择通过递归地划分数组来找到第 k 个最大元素;在问题 4 中,二分查找通过递归或迭代的方式在两个数组中找到正确的中位数位置。

  3. 时间复杂度优化:这两个问题的高效解法都不需要完整地排序整个数组或合并两个数组,而是通过局部排序或局部选择来达到目的,从而优化了时间复杂度。

结论

尽管题目 215 和题目 4 处理的具体问题不同,但它们在算法实现上有着类似的思想和技术。这表明了在算法设计中,不同的问题往往可以通过共享的核心算法思想来解决。

2 题目一:215. 数组中的第K个最大元素

2.1 答案

做这道题之前,建议先做一下快排,非常有帮助,912. 排序数组

2.1.1 方法一

java 复制代码
    public int findKthLargest(int[] nums, int k) {
        int n=nums.length;

        List<Integer>list=new ArrayList<>();
        for(int i=0;i<n;i++){
            list.add(nums[i]);
        }
        int ans=dfs(list,k);
        return ans;
    }

    int dfs(List<Integer>list,int p){

        Random rand=new Random();
        
        int base=list.get(rand.nextInt(list.size()));

        List<Integer>s=new ArrayList<>();
        List<Integer>m=new ArrayList<>();
        List<Integer>l=new ArrayList<>();

        for(int k=0;k<list.size();k++){
            if(list.get(k)<base){
                s.add(list.get(k));
            }else if(list.get(k)==base){
                m.add(list.get(k));
            }else{
                l.add(list.get(k));
            }
        }
        // 在数组中的第p大等于在数组中的第list.size()-p+1大
        if(s.size()>=list.size()-p+1){
            // 第二个参数决定在small数组中,这个数是第几大的数
            return dfs(s,s.size()-(list.size()-p+1)+1);
        }else if(s.size()+m.size()>=list.size()-p+1){
            return m.get(0);
        }else{
            return dfs(l,p);
        }
    }

2.1.2 方法二

java 复制代码
   public int findKthLargest(int[] nums, int k) {
        int n=nums.length;
        
        List<Integer>list=new ArrayList<>();
        for(int i=0;i<n;i++){
            list.add(nums[i]);
        }
        int ans=dfs(list,k);
        return ans;
    }

    // 使用不同的划分区间算法,将数组划分成三个区域:小于、等于和大于
    int dfs2(int[]nums,int l, int r, int k){
        Random rand=new Random();
        int pivot=nums[l];
        int p=l,q=r;
        for(int i=l;i<=r;){
            if(nums[i]>pivot){
                swap(nums,i,q--);
            }else if(nums[i]<pivot){
                swap(nums,i,p++);
            }else{
                i++;
            }
        }
        int len=r-l+1;
        if(p>=len-k+1){
            return dfs2(nums,l,p-1,p-(len-k+1)+1);
        }else if(q>=len-k+1){
            return dfs2(nums,q+1,r,k);
        }else{
            return nums[q];
        }
    }

    void swap(int[]nums, int i, int j){
        int t=nums[i];
        nums[i]=nums[j];
        nums[j]=t;
    }

3 问题二:4. 寻找两个正序数组的中位数

3.1 答案

java 复制代码
     public double findMedianSortedArrays2(int[] nums1, int[] nums2) {
        int length1 = nums1.length, length2 = nums2.length;
        int totalLength = length1 + length2;
        if (totalLength % 2 == 1) {
            int midIndex = totalLength / 2;
            double median = getKthElement(nums1, nums2, midIndex + 1);
            return median;
        } else {
            int midIndex1 = totalLength / 2 - 1, midIndex2 = totalLength / 2;
            double median = (getKthElement(nums1, nums2, midIndex1 + 1) + getKthElement(nums1, nums2, midIndex2 + 1)) / 2.0;
            return median;
        }
    }

    public int getKthElement(int[] nums1, int[] nums2, int k) {
        /* 主要思路:要找到第 k (k>1) 小的元素,那么就取 pivot1 = nums1[k/2-1] 和 pivot2 = nums2[k/2-1] 进行比较
         * 这里的 "/" 表示整除
         * nums1 中小于等于 pivot1 的元素有 nums1[0 .. k/2-2] 共计 k/2-1 个
         * nums2 中小于等于 pivot2 的元素有 nums2[0 .. k/2-2] 共计 k/2-1 个
         * 取 pivot = min(pivot1, pivot2),两个数组中小于等于 pivot 的元素共计不会超过 (k/2-1) + (k/2-1) <= k-2 个
         * 这样 pivot 本身最大也只能是第 k-1 小的元素
         * 如果 pivot = pivot1,那么 nums1[0 .. k/2-1] 都不可能是第 k 小的元素。把这些元素全部 "删除",剩下的作为新的 nums1 数组
         * 如果 pivot = pivot2,那么 nums2[0 .. k/2-1] 都不可能是第 k 小的元素。把这些元素全部 "删除",剩下的作为新的 nums2 数组
         * 由于我们 "删除" 了一些元素(这些元素都比第 k 小的元素要小),因此需要修改 k 的值,减去删除的数的个数
         */

        int length1 = nums1.length, length2 = nums2.length;
        int index1 = 0, index2 = 0;
        int kthElement = 0;

        while (true) {
            // 边界情况
            if (index1 == length1) {
                return nums2[index2 + k - 1];
            }
            if (index2 == length2) {
                return nums1[index1 + k - 1];
            }
            if (k == 1) {
                return Math.min(nums1[index1], nums2[index2]);
            }
            
            // 正常情况
            int half = k / 2;
            int newIndex1 = Math.min(index1 + half, length1) - 1;
            int newIndex2 = Math.min(index2 + half, length2) - 1;
            int pivot1 = nums1[newIndex1], pivot2 = nums2[newIndex2];
            if (pivot1 <= pivot2) {
                k -= (newIndex1 - index1 + 1);
                index1 = newIndex1 + 1;
            } else {
                k -= (newIndex2 - index2 + 1);
                index2 = newIndex2 + 1;
            }
        }
    }
相关推荐
Nil20811 小时前
leetcode 199二叉树的右视图
算法·leetcode·深度优先
M78佐菲12 小时前
c语言学习笔记:排序与查找方法整理
linux·c语言·笔记·学习·算法
AI备案指南-满满14 小时前
人工智能拟人化互动服务安全自评估报告的评估要点有哪些?
人工智能·算法·安全·机器人·大模型备案·算法备案
土司大王16 小时前
LeetCode hot100——两两交换链表中的节点
算法·leetcode·职场和发展
大熊背17 小时前
树莓派IspPipeline LSC模块原理详解
算法·lsc·isppipeline·mesh lsc
测试199817 小时前
Selenium 无法定位元素的几种解决方案
自动化测试·软件测试·python·selenium·测试工具·职场和发展·测试用例
zander25817 小时前
LeetCode 300. 最长递增子序列
算法·leetcode·深度优先
欧叶冲冲冲18 小时前
Python常见数据结构的CRUD(LeetCode高频版速查)
数据结构·python·leetcode
祖力5518 小时前
Linux应用软件编程:目录IO与framebuffer
linux·运维·算法·framebuffer·目录io