做题笔记——Median of Two Sorted Arrays

作者将LeetCode上求两个已排序数组中位数的英文官方解答整理成中文。题目要求输入两个已排序数组,输出中位数且时间复杂度为O(log(m+n))。思路是将两数组各分两部分再合成两部分,用二分法在[0, m]区间找满足条件的i,还考虑了极端情况并给出代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目描述

今天做leetcode上的题,遇到了这到题目,自己想了一上午没想到符合时间复杂度要求的解法,所以看了官方解答,但是解答是英文的,看着比较难受,所以整理成中文的,便于以后自己查看和理解。

输入:已经排好序的两个数组nums1和nums2
输出:找出两个数组的中位数,要求时间复杂度为O(log(m+n))。可以假设两个数组都不为空,即不用考虑特殊情况。
示例1

nums1 = [1, 3]
nums2 = [2]

the median is 2.0

示例2

nums1 = [1, 2]
nums2 = [3, 4]
the median is (2+3)/2 = 2.5
思路:

为了方便,用A代替nums1,B代替nums2。
假设A有m个数,B有n个数(要保证m<=n)。将A,B各分成两部分,然后将四个部分再合成两个部分,如下效果:

	    left_part            |            right_part
A[0], A[1], ..., A[i-1]   |   A[i], A[i+1], ... ,A[m-1] 
B[0], B[1], ..., B[j-1]   |   B[j], B[j+1], ..., B[n-1]

现在就需要保证满足下面两个条件:

  1. len(left_part) <= len(right_part)
  2. max(left_part) <= min(right_part)

那么就可以得到中位数:

median = (max(left_part) + min(right_part)) / 2

因为A,B都是拍了序的数组,因此,要保证前面的两个条件,我们就需要保证:

3. i+j = m-i+n-j(或者m-i+n-j+1),如果n >= m,那么只需要设:i = 0~m, j = (m+n+1) / 2 - i
4. B[j-1] <= A[i]以及 A[i-1] <= B[j]

为什么要保证 n>= m呢?因为这样才能保证j一直非负,而不会出错。

因此,现在要做的就是:
在[0, m]的区间内找到一个i,能够满足条件4。

有两种思路去搜寻i,一种是挨个遍历,另一种就是官方解答所用的二分法。
步骤1:取iMin=0, iMax=m,那么初始i = (0+m) / 2,j=(m+n+1)/2 - i。
开启循环
步骤2:对两个数组所分区间的分段处两边的值进行比较
2.1 当满足条件4时,可以终止循环,输出中位数
2.2
a) 当i位于iMin和iMax之间,并且B[j-1] > A[i]时,说明i偏小,所系需要增加i,因此iMin = i+1;
b) 当i位于iMin和iMax之间,并且B[j] < A[i-1]时,说明i偏大,所以需要减小i,因此iMax = i-1;
2.3 当满足条件的i找到后,求中位数需要先判断m+n是否为奇数:若为奇数,中位数就是max(A[i-1], B[j-1]);若为偶数,中位数就是(max(A[i-1], B[j-1]) + min(A[i], B[j])) / 2。

另外,有几种比较极端的情况需要考虑到:
(1) i = 0,此时 j = n, 并且A[i-1]就不会存在,也就是说A数组中最小的数都比B数组中值最大的数要大,所以中位数就是B[j-1] (m+n为奇数时)或者(A[i]+B[j-1]) / 2 (m+n为偶数);
(2) j = 0,此时i = m,并且B[j-1]不存在,即B数组最小的数比A数组最大的数还要大,所以中位数为A[i-1] (m+n为奇数时)或者(A[i-1]+B[j]) / 2 (m+n为偶数)。

代码如下:

class Solution {
    public double findMedianSortedArrays(int[] A, int[] B) {
        int m = A.length;
        int n = B.length;
        // 保证 m <= n
        if (m > n) { 
            int[] temp = A; A = B; B = temp;
            int tmp = m; m = n; n = tmp;
        }
        int iMin = 0, iMax = m, halfLen = (m + n + 1) / 2;
        while (iMin <= iMax) {
            int i = (iMin + iMax) / 2;
            int j = halfLen - i;
            if (i < iMax && B[j-1] > A[i]){
            	// i偏小
                iMin = i + 1; 
            }
            else if (i > iMin && A[i-1] > B[j]) {
                // i偏大
                iMax = i - 1;
            }
            else { 
                // 此时i符合条件
                int maxLeft = 0;
                if (i == 0) { maxLeft = B[j-1]; }
                else if (j == 0) { maxLeft = A[i-1]; }
                else { maxLeft = Math.max(A[i-1], B[j-1]); }
                if ( (m + n) % 2 == 1 ) { return maxLeft; }

                int minRight = 0;
                if (i == m) { minRight = B[j]; }
                else if (j == n) { minRight = A[i]; }
                else { minRight = Math.min(B[j], A[i]); }

                return (maxLeft + minRight) / 2.0;
            }
        }
        return 0.0;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值