leetcode 4. 寻找两个有序数组的中位数 困难 递归+二分

题目:
在这里插入图片描述

分析:两个有序的数组,可以考虑归并排序,然后取中位数,但归并排序的时间复杂是NlogN,虽然是能a出来,但和题目要求不符合,题目要求要O(log(m + n)),出现log,自然想到用二分搜索,现在来分析问题。有时候分析问题建议先抛开要编程,如果可以放在实际生活中那就放在实际生活中这个问题你会怎么解决。
题目中的问题的解决思路如下:先明确中位数是什么,通俗地来说中位数会将一堆已排序的数分成两半,左右两部分大小相等。现在是从两堆已排序的数中找一个中位数,可以看做看作把两堆数逐步放成一个堆,再在里面找中位数,那么按照中位数会把一堆数分成大小相等的两个部分这一性质,可以想到这样一种做法,逐步找到左半部分,那么自然接下来的那个数就是中位数了。
怎么逐步找到左半部分?可以这么想,中位数也就是这一堆数中第x小的数,那么就要逐步找到前面的x-1个数。前面的x-1个数怎么找?可以循环每次将x/2的数找到,分别在两个数组中找前x/2的数(因为都是排好序的,所以第x小也就是前x个数),并且比较两堆数中x/2小的数的大小,把较小的那堆数的x/2个数全部删掉(因为你的第x/2小比我的第x/2小,所以你的那部分必定在中位数前),如果两个数相等,随便删掉哪一个堆的那部分都行
删除并不是真的删除,而是移动一个指针,指向该数组的起始位置,如果“删除了”,那么移动该指针就行,除此外,要记得更新x值,因为本次已经找到x/2个数,所以剩下只需找x-x/2个数。
我们使用递归来做,有几个特殊情况,1.有一个数组已经删光了;2.只需找最后的一个数。这两个特殊情况可以用作终止递归

举例:
在这里插入图片描述

代码:

class Solution {
    public double findMedianSortedArrays(int[] nums1, int[] nums2) {
        int len1 = nums1.length;
        int len2 = nums2.length;
        if(nums1 == null || len1 == 0){
            //总数是偶数
            if(len2%2 == 0 ){
                return (nums2[len2/2 - 1] + nums2[len2/2])/2.0;
            }
            return nums2[len2/2]/1.0;
        }
        if(nums2 == null || len2 == 0){
            if(len1%2 == 0){
                return (nums1[len1/2 - 1] + nums1[len1/2])/2.0;
            }
            return nums1[len1/2]/1.0;
        }
        //总数是偶数
        if((len1 + len2)%2 == 0){
            return (findMedian(nums1, 0, len1-1, nums2, 0, len2-1, (len1 + len2)/2) + findMedian(nums1, 0, len1-1, nums2, 0, len2-1, (len1 + len2)/2+1))/2.0;
        }
        //总数是奇数
        return findMedian(nums1, 0, len1-1, nums2, 0, len2-1, (len1 + len2 + 1)/2)/1.0;
    }

    public int findMedian(int[] nums1, int start1, int end1, int[] nums2, int start2, int end2, int median){
        int nums1_len = end1 - start1 + 1;
        int nums2_len = end2 - start2 + 1;
        //按个人习惯总是保持nums1的长度是较短的,下一个判断判断nums1的len即可
        if(nums1_len > nums2_len){
            return findMedian(nums2, start2, end2, nums1, start1, end1, median);
        }
        //nums1已经删光了,在nums2里找即可
        if(nums1_len == 0){
            return nums2[start2+median-1];
        }
        //只剩最后一个数了,取最小的那个即中位数
        if(median == 1){
            return Math.min(nums1[start1], nums2[start2]);
        }
        //注意数组剩余长度和median/2的大小关系,取较小者,并且注意与索引的关系,要-1
        int nums1_index = start1 + Math.min(nums1_len, median/2) - 1;
        int nums2_index = start2 + Math.min(nums2_len, median/2) - 1;
        //num1该位置比nums2的小,把nums1 start1到该位置包含该位置的数全“删掉”,nums2保留
        if(nums1[nums1_index] < nums2[nums2_index]){
            return findMedian(nums1, nums1_index+1, end1, nums2, start2, end2, median-(nums1_index-start1+1));
        }else{
            return findMedian(nums1, start1, end1, nums2, nums2_index+1, end2, median-(nums2_index-start2+1));
        }
    }
}

在这里插入图片描述
在这里插入图片描述
总是递归地找k/2个数,所以时间复杂度是logk级别,而k=两数组长度和,所以=log(m+n)

发布了134 篇原创文章 · 获赞 17 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/weixin_40208575/article/details/104805381
今日推荐