LeetCode-004 Median of Two Sorted Arrays

本文介绍了一种高效算法,用于在两个已排序数组中找到中位数。通过巧妙地利用二分搜索,该算法实现了O(log(min(m,n)))的时间复杂度。

Description

There are two sorted arrays nums1 and nums2 of size m and n respectively.

Find the median of the two sorted arrays. The overall run time complexity should be O(log (m+n)).

Example

Example 1:

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

The median is 2.0

Example 2:

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

The median is (2 + 3)/2 = 2.5

Analyse

俗话说,描述越简单的题目越难,这里也属于一题。题意很简单,找到两个有序数列中的中位数,要是题目的描述到这就结束的话,只需一个归并,题目就做完了。时间复杂度为O(m+n)O(m+n)O(m+n),但是问题就在这里,题目要求时间复杂度为O(log(m+n))O(log(m+n))O(log(m+n)),这也是这道题被划分为Hard的原因。看到log,我们很容易 想到二分法,问题是怎么把二分法用到这个问题上来,然后我们还发现了题目还有一个条件没用上,就是“有序”,也许把这两个条件加起来,就能获得答案了呢。
算法如下:
我们设那两个数列一个为A,一个为B。
我们在A的任意位置i将A划分为两部分

left_partright_part
A[1] A[2] … A[i]A[i+1] A[i+2] … A[n]

同理,我们在B的任意位置j将B划分为两部分

left_partright_part
B[1] B[2] … B[j]B[j+1] B[j+2] … B[m]

然后我们把A和B的左半部分放在一块,A和B的右半部分放在一块。

left_partright_part
A[1] A[2] … A[i]A[i+1] A[i+2] … A[n]
B[1] B[2] … B[j]B[j+1] B[j+2] … B[m]

若我们能使得left_part和right_part的长度一样且max(left_part)<=min(right_part),那么右边的数都大于左边的数且数量一样,中位数就等于(max(left_part)+min(right_part))/2。
对于前面两个条件,我们可以转换为代码。
(1)i+j == m-i+n-j(或者i+j == m-i+n-j+1) 即:当n>=m时,i=0~m,j=(m+n+1)/2-i
(2)B[j-1]<=A[i] && A[i-1]<=B[j]。
(1)处的n>=m是用来保证j一直大于0,这里可以通过比较两个数列来保证。
现在重点需要考虑的是(2)的边界问题,当i=0/j=0/i=m/j=n时,上面的公式会出问题的,不过我们先不考虑这一点。
i的取值可以通过二分查找来获得。
让imin=0,imax=m,i=(imin+imax)/2,j=(m+n+1)/2-i。
a.当B[j-1]<=A[i] && A[i-1]<=B[j]时,找到了答案,退出二分查找
b.当B[j-1]>A[i]时,表示A[i]太小了,所以需要变大,所以i需要往大的找,此时,j会变小,B[j-1]也会变小,即:imin=i+1。
c.当A[i-1]>B[j]时,表示A[i-1]太大了,所以需要变小,所以i需要往小的找,此时,j会表大,B[j]也会变大,即:imax=i-1。
当二分查找退出时,我们找到了答案。中位数为:
max(A[i-1], B[j-1]) (当m+n为奇数时),(max(A[i-1], B[j-1]) + min(A[i], B[j]))/2 (当m+n为偶数时)。
我们再回来考虑边界的问题,我们可以发现,若i,j取到边界时,中位数就找到了,因为中位数存在于其中某个数列里面,我们只需要把边界挑出来就好了。
a.(j == 0 || i == m || B[j-1] <= A[i]) && (i == 0 || j = n || A[i-1] <= B[j])即找到答案。
然后强化后两个条件。
b.j > 0 && i < m && B[j - 1] > A[i],我们可以证明j > 0 和 i < m时等价的,所以只需要:j > 0 && B[j - 1] > A[i]即可,imin=i+1。
c.i > 0 && j < n && A[i - 1] > B[j],同上,i > 0 和 j < n也是等价的,所以只要j < n && A[i - 1] > B[j]即可,imax=i-1。
算法结束。时间复杂度为O(log(min(m,n)))O(log(min(m,n)))O(log(min(m,n)))

Code

class Solution {
public:
    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
        vector<int> temp;
        if (nums1.size() > nums2.size()) {
            temp = nums1; nums1 = nums2; nums2 = temp;
        }
        int m = nums1.size(), n = nums2.size();
        int min = 0, max = m, mid = (m + n + 1) / 2, max_left, min_right;
        while (min <= max) {
            int i, j;
            i = (min + max) / 2;
            j = mid - i;
            if (i < m && nums2[j - 1] > nums1[i]) {
                min = i + 1;
            } else {
                if (i > 0 && nums1[i - 1] > nums2[j]) {
                    max = i - 1;
                } else {
                    if (i == 0) {
                        max_left = nums2[j - 1];
                    } else {
                        if (j == 0) {
                            max_left = nums1[i - 1];
                        } else {
                            if (nums1[i - 1] > nums2[j - 1]) max_left = nums1[i - 1];
                            else max_left = nums2[j - 1];
                        }
                    }
                    if ((m + n) % 2 == 1) return max_left;
                    if (i == m) {
                        min_right = nums2[j];
                    } else {
                        if (j == n) {
                            min_right = nums1[i];
                        } else {
                            if (nums1[i] < nums2[j]) min_right = nums1[i];
                            else min_right = nums2[j];
                        }
                    }
                    return double(max_left + min_right) / 2;
                }
            }
        }
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值