LeetCode OJ4 Median of Two Sorted Arrays 小结

来源:互联网 发布:美国原油库存eia数据 编辑:程序博客网 时间:2024/06/07 20:35

Median of Two Sorted Arrays

问题描述

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)).

解题思路

一开始想到的思路是: 将数组归并排序, 然后对排序成一个数组的序列, 进行二分查找, 时间复杂度 为 O(n+m), 显然和题目给出的要求 O(log(m+n)),还有一定的差距。
后来, 实现想不出方法之后, 无奈之下, 参考了大神门的源码,得到启发。
我们可以求数组的第K个元素!!!
我们可以取两个排序数组的中值, 进行比较。并计长的数组为a, 短的数组为b, 分别取他们的[k / 2] 处的值。 如果 a 中的值, 小于 b 中的值,表明 a 中的值之前的部分 不是我们 所求的第 K 个元素的范围, 因而由此可以缩小寻找范围!!!

AC code

class Solution {public:    // 此处的 k 以 1 开始计数    double findMed(int a[], int m, int b[], int n, int k)    {        if (m < n)            return findMed(b, n, a, m, k);        if (n == 0)            return a[k - 1];        if (k == 1)            return min(a[0], b[0]);        int pb = min(k/2, n);        int pa = k - pb;        if (a[pa - 1] < b[pb - 1])            return findMed(a + pa, m - pa, b, n, k - pa);        else if (a[pa - 1] > b[pb - 1])            return findMed(a, m, b + pb, n - pb, k - pb);        else            return a[pa - 1];    }    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {        int k = (nums1.size() + nums2.size());        if (k & 0x1 != 0)            return findMed(&nums1[0], nums1.size(), &nums2[0], nums2.size(), k / 2 + 1);        else            return findMed(&nums1[0], nums1.size(), &nums2[0], nums2.size(), k / 2) / 2 +                 findMed(&nums1[0], nums1.size(), &nums2[0], nums2.size(), k / 2 + 1) / 2;    }};

大神们的代码

demo 1

share my o(log(min(m,n)) solution with explanation

翻译下大神的思路, 将两个数组的元素分成数量相等的两部分,找出符合这种要求的分割点 i(二分法);求出 中值

Given a sorted array A with length m, we can split it into two part:{ A[0], A[1], ... , A[i - 1] } | { A[i], A[i + 1], ... , A[m - 1] }All elements in right part are greater than elements in left part.The left part has "i" elements, and right part has "m - i" elements.There are "m + 1" kinds of splits. (i = 0 ~ m)When i = 0, the left part has "0" elements, right part has "m" elements.When i = m, the left part has "m" elements, right part has "0" elements.For array B, we can split it with the same way:{ B[0], B[1], ... , B[j - 1] } | { B[j], B[j + 1], ... , B[n - 1] }The left part has "j" elements, and right part has "n - j" elements.Put A's left part and B's left part into one set. (Let's name this set "LeftPart")Put A's right part and B's right part into one set. (Let's name this set"RightPart")            LeftPart           |            RightPart { 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] }If we can ensure: 1) LeftPart's length == RightPart's length (or RightPart's length + 1) 2) All elements in RightPart are greater than elements in LeftPart.then we split all elements in {A, B} into two parts with eqaul length, and one part isalways greater than the other part. Then the median can be easily found.To ensure these two condition, we just need to ensure: (1) i + j == m - i + n - j (or: m - i + n - j + 1)     if n >= m, we just need to set:            i = 0 ~ m, j = (m + n + 1) / 2 - i (2) B[j - 1] <= A[i] and A[i - 1] <= B[j]     considering edge values, we need to ensure:           (j == 0 or i == m or B[j - 1] <= A[i]) and                (i == 0 or j == n or A[i - 1] <= B[j])So, all we need to do is: Search i from 0 to m, to find an object "i" to meet condition (1) and (2) above.And we can do this search by binary search. How?If B[j0 - 1] > A[i0], then the object "ix" can't be in [0, i0]. Why? Because if ix < i0, then jx = (m + n + 1) / 2 - ix > j0,  then B[jx - 1] >= B[j0 - 1] > A[i0] >= A[ix]. This violates the condition (2). So ix can't be less than i0.And if A[i0 - 1] > B[j0], then the object "ix" can't be in [i0, m].So we can do the binary search following steps described below:1. set imin, imax = 0, m, then start searching in [imin, imax]2. i = (imin + imax) / 2; j = (m + n + 1) / 2 - i3. if B[j - 1] > A[i]: continue searching in [i + 1, imax]   elif A[i - 1] > B[j]: continue searching in [imin, i - 1]   else: bingo! this is our object "i"When the object i is found, the median is:max(A[i - 1], B[j - 1]) (when m + n is odd)or (max(A[i - 1], B[j - 1]) + min(A[i], B[j])) / 2 (when m + n is even)Below is the accepted code:def median(A, B):    m, n = len(A), len(B)    if m > n:        A, B, m, n = B, A, n, m    imin, imax, half_len = 0, m, (m + n + 1) / 2    while imin <= imax:        i = (imin + imax) / 2        j = half_len - i        if j > 0 and i < m and B[j - 1] > A[i]:            imin = i + 1        elif i > 0 and j < n and A[i - 1] > B[j]:            imax = i - 1        else:            if i == 0:                num1 = B[j - 1]            elif j == 0:                num1 = A[i - 1]            else:                num1 = max(A[i - 1], B[j - 1])            if (m + n) % 2 == 1:                return num1            if i == m:                num2 = B[j]            elif j == n:                num2 = A[i]            else:                num2 = min(A[i], B[j])            return (num1 + num2) / 2.0

C++ version

class Solution {public:  double findMedianSortedArrays(int A[], int m, int B[], int n) {    if (m > n) return findMedianSortedArrays(B, n, A, m);    int minidx = 0, maxidx = m, i, j, num1, mid = (m + n + 1) >> 1,num2;    while (minidx <= maxidx)    {      i = (minidx + maxidx) >> 1;      j = mid - i;      if (i<m && j>0 && B[j-1] > A[i]) minidx = i + 1;      else if (i>0 && j<n && B[j] < A[i-1]) maxidx = i - 1;      else      {        if (i == 0) num1 = B[j-1];        else if (j == 0) num1 = A[i - 1];        else num1 = max(A[i-1],B[j-1]);        break;      }    }    if (((m + n) & 1)) return num1;    if (i == m) num2 = B[j];    else if (j == n) num2 = A[i];    else num2 = min(A[i],B[j]);    return (num1 + num2) / 2.;  }};

demo2

Very concise O(log(min(M,N))) iterative solution with detailed explanation

大神思路: 设计虚拟位置, L=mid12,R=mid2,mid<=[0,2Length],
由于要求左右两边保持平衡, 相等, 或者仅相差一个元素, 可以得到 1,2 两个数组之间的联系, 基于此对 较短的数组进行二分查找即可!!!
本质上, demo1, demo2 的思想是一致的!!!

double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {    int N1 = nums1.size();    int N2 = nums2.size();    if (N1 < N2) return findMedianSortedArrays(nums2, nums1);   // Make sure A2 is the shorter one.    if (N2 == 0) return ((double)nums1[(N1-1)/2] + (double)nums1[N1/2])/2;  // If A2 is empty    int lo = 0, hi = N2 * 2;    while (lo <= hi) {        int mid2 = (lo + hi) / 2;   // Try Cut 2         int mid1 = N1 + N2 - mid2;  // Calculate Cut 1 accordingly        double L1 = (mid1 == 0) ? INT_MIN : nums1[(mid1-1)/2];  // Get L1, R1, L2, R2 respectively        double L2 = (mid2 == 0) ? INT_MIN : nums2[(mid2-1)/2];        double R1 = (mid1 == N1 * 2) ? INT_MAX : nums1[(mid1)/2];        double R2 = (mid2 == N2 * 2) ? INT_MAX : nums2[(mid2)/2];        if (L1 > R2) lo = mid2 + 1;     // A1's lower half is too big; need to move C1 left (C2 right)        else if (L2 > R1) hi = mid2 - 1;    // A2's lower half too big; need to move C2 left.        else return (max(L1,L2) + min(R1, R2)) / 2; // Otherwise, that's the right cut.    }    return -1;} 

demo3

share my simple O(log(m+n)) solution for your reference
这个思路, 就是我们参考的那个思路,一次砍掉将近 1 / 4 的数据
T(n)=T(3n/4)+O(1)
通过主定理很容易看出, 时间复杂度为 T(n)=log(n)

ps: 这里的 K 从 1 开始计数

class Solution {public:    int getkth(int s[], int m, int l[], int n, int k){        // let m <= n        if (m > n)             return getkth(l, n, s, m, k);        if (m == 0)            return l[k - 1];        if (k == 1)            return min(s[0], l[0]);        int i = min(m, k / 2), j = min(n, k / 2);        if (s[i - 1] > l[j - 1])            return getkth(s, m, l + j, n - j, k - j);        else            return getkth(s + i, m - i, l, n, k - i);        return 0;    }    double findMedianSortedArrays(int A[], int m, int B[], int n) {        int l = (m + n + 1) >> 1;        int r = (m + n + 2) >> 1;        return (getkth(A, m ,B, n, l) + getkth(A, m, B, n, r)) / 2.0;    }};
0 0