两个有序数组的中位数求解

🤚我的博客

  • 欢迎光临我的博客:https://blog.csdn.net/qq_52434217?type=blog

🥛前言

来了来了他终于带着算法题的解析来了!困扰依旧的算法终于被语言文字输出来了!

两个有序数组的中位数求解

题目

给定两个大小分别为 mn 的正序(从小到大)数组 nums1nums2。请你找出并返回这两个正序数组的 中位数

要求

算法的时间复杂度应该为 O(log (m+n))

分析

初步推理

本题是寻找两个数组拼在一起的中位数,假设中位数的在这两个数组的位置为第k个,换言之就是找第k小的数。考虑假设两个数组一共3个数,则中位数是第2个,假设两个数组一共4个数,则中位数是第2个和第3个数求平均。寻找规律后发现需要分情况讨论,如果两个数组一共m+n个数,当n+m为奇数时,那么中位数就是第(n+m)/2+1小的数;当n+m为偶数时,那么中位数就是第(n+m)/2小的数和(n+m)/2+1小的数;本题要求时间复杂度为 O(log (m+n)),自然需要采用二分法。那么如何使用二分法呢?

进一步推理

首先这两个数组是顺序数组,我们分别比较两个数组中第i个值。如果nums1[i-1]>nums2[i-1],就能推出在nums2中有i-1个数比nums1[i-1]小。这里假设nums1={1,2,4}, nums2={1,2,3,4,5,6,7,8,9,10},则k=7。由于采用二分法,这里我们取i=k/2。当我们比较i=k/2=3时的值发现nums1[2]>nums2[2],这时可以推出nums1[2]最多为第6小的数,但是我们排除了前3小的数。于是在剩下的数字中按照同样的思路去查找第7-3=4小的数,就可以再排除3个,直到排除前k-1个。这里有一个细节就是如果取等于的时候,我们可以只挪动其中一个数组的下标。如果nums1的长度过小怎么办呢,比如k/2=4,而nums1长度为3。这个时候就只需要在nums2中找到值就行了。

思路与代码的转换

核心思路

在前面我们已经知道如何找到找到第k小的数,那么如何表示剩下的数字呢。其实我们可以直接将整个数组传进去,然后指定startend,这样我们就可以知道一个数组的全部值了。如何得到startend值呢?我们首先传入的肯定是整个数组,那么start0endm-1或者n-1。以nums1为例,在nums1中找第k/2个数,就是找下标为0+k/2-1的数。如果nums1[i]>nums2[i],那么再递归时就传入nums1从start到end的数和nums2i+1个数到end的数。同时我们已经排除了nums2中的i个数,所以在递归时,要传入k-(i-start+1)以用来找到第k-(i-start+1)个数。但是需要考虑一个下标越界的问题,所以i值应该为start+Math.min(m,k/2)-1,这样一来两个数组就需要两个变量指向下标,分别为ij
转化成代码为

int i = start1 + Math.min(len1, k / 2) - 1;
int j = start2 + Math.min(len2, k / 2) - 1;
if (nums1[i] > nums2[j]) {
	return getKth(nums1, start1, end1, nums2, j + 1, end2, k - (j - start2 + 1));
} else {
	return getKth(nums1, i + 1, end1, nums2, start2, end2, k - (i - start1 + 1));
}

什么时候完成递归返回结果呢?这里可以用上面的例子,分两种情况,一种是nums1取完,另一种是排除完前k-1个数。当nums1被取完仍没有排除掉前k-1个数,这里假设已经排除了x个,那么还有k-x个,则可以取剩余的nums2中第k-x个。前面已经提到nums2的剩余数组是由startend决定的,又由于我们递归传入的是滴k=k-x个值,所以返回nums2[start+k-1]就可以。如果已经排除了前k-1个值,那么只需要返回第k=1小的数,即nums1[start]nums2[start]最小值即可。

if (len1 == 0) return nums2[start2 + k - 1];
if (k == 1) return Math.min(nums1[start1], nums2[start2]);

细节

如何知道一定是nums1长度不够呢,这里我们可以先计算两个数组的长度,如果nums1长于nums2就交换。

int len1 = end1 - start1 + 1;
int len2 = end2 - start2 + 1;
if (len1 > len2) return getKth(nums2, start2, end2, nums1, start1, end1, k);

最后解决就奇偶长度的问题。我们直接将利用(n+m+1)/2(n+m+2)/2即可,即利用下面的代码解决即可。

int left = (n + m + 1) / 2;
int right = (n + m + 2) / 2;
(getKth(nums1, 0, n - 1, nums2, 0, m - 1, left) + getKth(nums1, 0, n - 1, nums2, 0, m - 1, right)) * 0.5;

这里我们发现当n+m为奇数时,(n+m+1)/2==(n+m+2)/2,当n+m为偶数时,(n+m+2)/2==(n+m+1)/2+1

完整解题代码

class Solution {
    public double findMedianSortedArrays(int[] nums1, int[] nums2) {
        int n = nums1.length;
        int m = nums2.length;
        int k1 = (n + m + 1) / 2;
        int k2 = (n + m + 2) / 2;
        return (getKth(nums1, 0, n - 1, nums2, 0, m - 1, k1) + getKth(nums1, 0, n - 1, nums2, 0, m - 1, k2)) * 0.5;
    }
    private int getKth(int[] nums1, int start1, int end1, int[] nums2, int start2, int end2, int k) {
        int len1 = end1 - start1 + 1;
        int len2 = end2 - start2 + 1;
        if (len1 > len2) return getKth(nums2, start2, end2, nums1, start1, end1, k);
        if (len1 == 0) return nums2[start2 + k - 1];
        if (k == 1) return Math.min(nums1[start1], nums2[start2]);
        int p1 = start1 + Math.min(len1, k / 2) - 1;
        int p1 = start2 + Math.min(len2, k / 2) - 1;
        if (nums1[p1] > nums2[p2]) {
            return getKth(nums1, start1, end1, nums2, p2 + 1, end2, k - (p2 - start2 + 1));
        } else {
            return getKth(nums1, p1 + 1, end1, nums2, start2, end2, k - (p1 - start1 + 1));
        }
    }
}

END

总结

1、当复杂度要求为log时,需要采用二分法递归
2、当遇到奇偶情况时,可以采用(n+m+1)/2(n+m+2)/2这个关系解决
3、在遇到此类数组截断的情况时,可以传入起始点和终止点而不用遍历。

参考资料

[1] 链接:https://leetcode.cn/problems/median-of-two-sorted-arrays/solutions/8999/xiang-xi-tong-su-de-si-lu-fen-xi-duo-jie-fa-by-w-2/  作者:windliang 来源:力扣(LeetCode)

公众号

更多内容请关注小夜的公众号,将持续更新java spring全家桶,vue全家桶,python数据分析与爬虫以及js与爬虫逆向。
欢迎关注小夜的公众号


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值