Leetcode 4 - Median of Two Sorted Arrays(二分)

题意

两个有序数组,求它们的中位数。

思路

其实我们可以将这个问题转化为:求两个有序数组的第k大。

算法1

非递归版。

首先,我们假设在第一个数组里面任意找一个元素 ai ,那么它在第一个数组里是 i+1 大,如果我们知道它在第二个数组内是j大,那么在两个数组合成以后,它就是 i+j+1 大了。那么我们如何判断它在第二个数组内是第几大呢?因为第二个数组是有序的,所以我们只需要二分查找一下它的位置就好了。我们会存在如下问题:

  1. 使用lower_bound还是upper_bound

    然后,我们在二分查找的时候,其实会面临一个问题就是,比如我们的数组是[1, 2], [1, 2]。求第3大的时候,假如第一个数组内我们选择 a1=2 ,那么如果用lower_bound查找, j=1 ,这时候 i+j+1=k=3 ,是成立的。

    然后我们还需要找到第2大,第一个数组内我们选择 a1=2 ,那么用lower_bound查找的时候,这时候j = 1。会使 i+j+1=32

    假如我们用upper_bound查找呢?

    k=3 a1=2 j=2 i+j+1=4k

    也就是,我们存在一些重复元素的情况下,其实插入中间的任意位置都可以。

    所以,我们的判断满足条件为:

    int j1 = lower_bound(b.begin(), b.end(), a[i]) - b;
    int j2 = upper_bound(b.begin(), b.end(), a[i]) - b;
    if (i + j1 + 1 <= k && i + j2 + 1 >= k) return a[i];
  2. 如果结果不在第一个数组中呢?

    我们只需要分别在第一个数组中和第二个数组中分别找一下就好。

  3. 如果m + n为偶数,我们需要找到两个数,怎么处理

    1. 调用两次找第k大。
    2. 我们记录一下找到的第k大的位置pos,那么第k - 1大,要么为nums1[pos - 1],要么为nums2[k - 1- pos]。即两个数取较大的一个即可。
算法2

递归版。

在我们找第k大的数时,假设我们另 i=k2,j=k2 。我们比较一下 ai bj 的值。

ai1<bj1 :说明第k大一定不在 [a0,ai1] 内。于是,我们下一次的递归为find(a + i, m - i, b, n, k - i)

bj1<ai1 :说明第k大一定不在 [b0,bj1] 内。于是,我们下一次的递归为find(a, m, b + j, n - j, k - j)

ai1=bj1 :说明第k大即为 ai1 bj1

边界条件:

  1. 若a已经为空了, 那么直接返回b[k - 1]即可。
  2. k == 1,那么,我们返回a[0]和b[0]中最小的一个即可。
  3. 我们确保m < n来满足第一个边界。
  4. i=k2 的时候,我们需要取 min{k2,m}

代码

//algorithm 1.1
class Solution {
public:
    double kthNum(vector<int>& a, vector<int>& b, int k, int flag) {
        int l = 0, r = k - 1, m;
        while (l <= r) {
            m = l + (r - l >> 1);
            if (m >= a.size()) {r = m - 1; continue;}
            int y1 = lower_bound(b.begin(), b.end(), a[m]) - b.begin();
            int y2 = upper_bound(b.begin(), b.end(), a[m]) - b.begin();
            if ((m + y1 + 1 <= k) && (m + y2 + 1 >= k)) return (double)a[m];
            if (m + y1 + 1 > k) r = m - 1;
            else l = m + 1;
        }
        //not in the first array
        return 1e15;
    }

    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
        int m = nums1.size(), n = nums2.size();
        int k1 = m + n >> 1;
        ++k1;
        double x1 = kthNum(nums1, nums2, k1, 1);
        if (x1 == 1e15) x1 = kthNum(nums2, nums1, k1, 1);
        if ((m + n) & 1) return x1;
        --k1;
        double x2 = kthNum(nums1, nums2, k1, 0);
        if (x2 == 1e15) x2 = kthNum(nums2, nums1, k1, 0);
        return (x1 + x2) / 2.0;
    }
};

//algorithm 1.2
class Solution {
public:
    int pos = 0;
    double kthNum(vector<int>& a, vector<int>& b, int k) {
        int l = 0, r = k - 1, m;
        while (l <= r) {
            m = l + (r - l >> 1);
            if (m >= a.size()) {r = m - 1; continue;}
            int y1 = lower_bound(b.begin(), b.end(), a[m]) - b.begin();
            int y2 = upper_bound(b.begin(), b.end(), a[m]) - b.begin();
            if ((m + y1 + 1 <= k) && (m + y2 + 1 >= k)) {pos = m; return (double)a[m];}
            if (m + y1 + 1 > k) r = m - 1;
            else l = m + 1;
        }
        //not in the first array
        return 1e15;
    }

    double another(vector<int>& nums1, vector<int>& nums2, int k1) {
        int y = --k1 - pos - 1; double x2;
        if (pos) {
            if (y >= 0) x2 = max(nums2[pos -1], nums1[y]);
            else x2 = nums2[pos - 1];
        } else {
            x2 = nums1[y];
        }
        return x2;
    }

    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
        int m = nums1.size(), n = nums2.size();
        int k1 = m + n >> 1;
        ++k1; bool flag = false;
        double x1 = kthNum(nums1, nums2, k1), x2;
        if (x1 == 1e15) {flag = true; x1 = kthNum(nums2, nums1, k1);}
        if ((m + n) & 1) return x1;
        if (flag) x2 = another(nums1, nums2, k1);
        else x2 = another(nums2, nums1, k1);
        return (x1 + x2) / 2.0;
    }
};

//algorithm 2
class Solution {
public:
    double findKth(int a[], int m, int b[], int n, int k) {
        if (m > n) return findKth(b, n, a, m, k);
        if (m == 0) return b[k - 1];
        if (k == 1) return min(a[0], b[0]);
        int x = min(m, k >> 1), y = k - x;
        if (a[x - 1] < b[y - 1]) return findKth(a + x, m - x, b, n, k - x);
        if (a[x - 1] > b[y - 1]) return findKth(a, m, b + y, n - y, k - y);
        return a[x - 1];
    }

    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
        int m = nums1.size(), n = nums2.size();
        int *a = new int[nums1.size()], *b = new int[nums2.size()];
        for (int i = 0; i < m; i++) a[i] = nums1[i];
        for (int i = 0; i < n; i++) b[i] = nums2[i];
        if ((m + n) & 1) return findKth(a, m, b, n, (m + n >> 1) + 1);
        return (findKth(a, m, b, n, (m + n >> 1) + 1) + findKth(a, m, b, n, m + n >> 1)) / 2.0;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值