Tuesday, October 8, 2013

4. Median of Two Sorted Arrays ——hard !!!!!

Given two sorted arrays nums1 and nums2 of size m and n respectively, return the median of the two sorted arrays.

The overall run time complexity should be O(log (m+n)).

 

Example 1:

Input: nums1 = [1,3], nums2 = [2]
Output: 2.00000
Explanation: merged array = [1,2,3] and median is 2.

Example 2:

Input: nums1 = [1,2], nums2 = [3,4]
Output: 2.50000
Explanation: merged array = [1,2,3,4] and median is (2 + 3) / 2 = 2.5.

 

Constraints:

  • nums1.length == m
  • nums2.length == n
  • 0 <= m <= 1000
  • 0 <= n <= 1000
  • 1 <= m + n <= 2000
  • -106 <= nums1[i], nums2[i] <= 106
A:
方法的核心是将原问题转变成一个寻找第k小数的问题(假设两个原序列升序排列),这样中位数实际上是第(m+n)/2小的数。所以只要解决了第k小数的问题,原问题也得以解决。
首先假设数组A和B的元素个数都大于k/2,我们比较A[k/2-1]和B[k/2-1]两个元素,这两个元素分别表示A的第k/2小的元素和B的 第k/2小的元素。这两个元素比较共有三种情况:>、<和=。如果A[k/2-1]<B[k/2-1],这表示A[0]到A[k /2-1]的元素都在A和B合并之后的前k小的元素中。换句话说,A[k/2-1]不可能大于两数组合并之后的第k小值,所以我们可以将其抛弃。
-------简单的说,就是或者丢弃较大中位数的右区间,或者丢弃较小中位数的左区间。
-----------呵呵,自己想出来的, 可是,还是不自信, 没有code完,就去看别人的提示了。

---------------哎~~~~~~~ 踩到地雷了,以为很简单,没想到是难度为5 的题目------------

public class Solution {
       public double findMedianSortedArrays(int A[], int B[]) {
        int m = A.length, n = B.length;
        assert (!(n == m && n == 0)); // if both array are empty, WRONG
        int[] C = null;// C point to the non-empty array, while the other is
                        // empty
        if (m == 0) {
            C = B;
        } else if (n == 0) {
            C = A;
        }
        if (C != null) { // if A is empty
            return (C.length % 2 == 1) ? C[C.length / 2]
                    : (C[C.length / 2] + C[C.length / 2 - 1]) / 2.0;
        }
        // NOW neither A or B is empty
        if ((m + n) % 2 == 1) {
            return findKth(A, 0, m - 1, B, 0, n - 1, (m + n) / 2 + 1);
        } else {
            int leftMedian = findKth(A, 0, m - 1, B, 0, n - 1, (m + n) / 2);
            int rightMedian = findKth(A, 0, m - 1, B, 0, n - 1, (m + n) / 2 + 1);
            return (leftMedian + rightMedian) / 2.0;
        }
    }

    private int findKth(int[] a, int lowA, int highA, int[] b, int lowB,
            int highB, int k) {
        // GOAL: each time, we either cut the half left of the smaller median
        // or the right part of the bigger median array
        if (k == 1) {
            return Math.min(a[lowA], b[lowB]);
        } else {
            // if one array is empty
            if (lowA > highA) { // A is empty, search in B
                return b[lowB + k - 1];
            }
            if (lowB > highB) {// B is empty, search in A
                return a[lowA + k - 1];
            }

            int midA = (lowA + highA) / 2;
            int midB = (lowB + highB) / 2;

            // two pointers pointing , according to the value of the median
            int[] medianSmall = a;
            int smallLow = lowA;
            int smallMid = midA;
            int smallHigh = highA;

            int[] medianBig = b;
            int bigLow = lowB;
            int bigMid = midB;
            int bigHigh = highB;

            // make sure that small point to small
            if (medianSmall[smallMid] > medianBig[bigMid]) {
                medianSmall = b;
                medianBig = a;

                smallLow = lowB;
                smallMid = midB;
                smallHigh = highB;

                bigLow = lowA;
                bigMid = midA;
                bigHigh = highA;
            }
            // check value of k with the size of their half
            int smallLeftLength = smallMid - smallLow + 1;
            // smallMid is counted, and != 0
            int bigLeftLength = bigMid - bigLow + 1; // bigMid is counted;
            if (k <= smallLeftLength + bigLeftLength) {
                // discard the right part of the medianBig array
                // NOTE that the length of the right part of the medianBig array
                // could be ZERO.
                if (bigMid < bigHigh) { // right part is not of length 0
                    return findKth(medianSmall, smallLow, smallHigh, medianBig,
                            bigLow, bigMid, k);
                } else { // right part is of length 0, i.e. bigLow == bigHigh
                            // now,we can (imaginely) insert this value into the
                            // range of medianSmall array
                    int biggerMedian = medianBig[bigMid];// first candicates
                    if (k == smallLeftLength + 1) {
                        return (smallMid == smallHigh) ? biggerMedian : Math
                                .min(biggerMedian, medianSmall[smallMid + 1]);
                    } else {
                        return medianSmall[smallLow + k - 1];
                    }
                }
            } else { // discard the left part of the medianSmall array (
                        // length>0)
                return findKth(medianSmall, smallMid + 1, smallHigh, medianBig,
                        bigLow, bigHigh, k - smallLeftLength);
            }
        }
    }
}


Mistakes:

TMD: median 的定义是: If there is an even number of observations, then there is no single middle value; the median is then usually defined to be the mean of the two middle values

1: 因为我们后来在处理一个数组结束之后,只寻找另一个数组的情况的时候。 用了一个唯一的值等于Interger.Max_VALUE。   所以, 当k == 1 时,我们要考虑Interger.MAX_VALUE 对我们的影响。   因此,不能简单得返回 Math.max(a[lowA],b[lowB])  , 要改成代码中所示。

2: 当我面在递归函数里, 处理 k == 1的时候,忘记了先检验,是否数组不为空。

3:  当递归的时候, 需要处理一个情况, 就是, pointerSmall 的左边  和pointerBig的右边,如果为空的话,就会导致out of stack exception.
因此,需要先处理那些情况。

4: 要记得检测, 总数是奇数还是偶数--------导致的计算方法是不一样的


5: 哎呀, TMD,疯了,  就因为,当时, 也不知道怎么想的。可能是半夜,脑子不清醒。
当k==1的时候,我们应该取两个数组里面,各自第一个里的值的较小的呀。
应该是Math.min    ------可是自己当时,写的是Math.max    花费了一次半值班的时间, 大约是4,5个小时的宝贵时间。    得不偿失啊。  Tiger

6: when  discarding the right part of the medianBig array, we should check first the length of the to-be-discarded part.
因此这种情况下, medianBig 数组的bigHigh == bigSmall,我们就把它的值单独列出来,并对比medianSmall即可。 (否则会出现死循环调用,导致out of stack memory ,)


Learned:
1:  当 一个int 值, 例如,
int a = 3;
a/2 的值,默认还是int类型。 因此,当我们需要返回一个带小数的double类型的话,需要除以2.0

---------------------------第二遍 ---------------------------
Mistakes:
1: kth这个参数, 第一遍是按照0 based考虑的。 那么, 在对比leftCount+rightCount 和 kth的大小的时候, 就不需要加1了。  ----------(这个问题很快发现, 然后就调整了)
2:由于在 if 语句中(20行左右),写 (lowB+highB)/2] 的时候,拷贝了上面,  然后发现多了个 ‘】’, 就随手把if (这里的】 也去掉了,  ) complier error,  就又随手加上了 ]  ,。  结果加到/2 后面去了 。  哎, 结果  傻看了40多分钟,才弄明白。    哎, 人生阿~~~~~~~
状态不好的时候, 就不要去coding, 犯的SB错误,都会搞死我的时间

############ 3 rd pass##################
class Solution {
public:
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
int n1 = nums1.size(), n2 = nums2.size();
if ((n1 + n2) % 2 == 0) {
return (getKth(nums1, 0, n1, nums2, 0, n2, (n1 + n2) / 2) +
getKth(nums1, 0, n1, nums2, 0, n2, (n1 + n2) / 2 - 1)) /
2.0;
} else {
return getKth(nums1, 0, n1, nums2, 0, n2, (n1 + n2) / 2);
}
}

private:
double getKth(vector<int>& nums1, int s1, int e1, vector<int>& nums2,
int s2, int e2, int k) {
// k starts from 0. s1/s2 is inclusive, e1/e2 are exclusive
if (s1 >= e1)
return nums2[s2 + k];
if (s2 >= e2)
return nums1[s1 + k];
// both are valid
int m1 = (s1 + e1) / 2, m2 = (s2 + e2) / 2;
if (nums1[m1] <= nums2[m2]) {
int cLeft = m1 - s1 + m2 - s2 + 1; // exclude nums2[m2]
if (cLeft >= k + 1) { // +1 since k starts from 0
return getKth(nums1, s1, e1, nums2, s2, m2, k);
} else {
return getKth(nums1, m1 + 1, e1, nums2, s2, e2,
k - (m1 - s1 + 1));
}
} else {
return getKth(nums2, s2, e2, nums1, s1, e1, k);
}
}
};

改进是, 倒数第二个if, 交换了nums1, nums2 两个数组,这样,少写一遍最容易出错的代码


Mistakes:
这里最容易犯的错误,  第一个if里,开始写成了 nLeft >= index
这里的核心,关键点是: m2 (which is the bigger value) is exclusive, and m1 is inclusive.

网上的解法:
不去管真正的median。 而是直接去找分别  + k/2的距离后的2个值做比较。 
class Solution {
public:
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
int n1 = nums1.size(), n2 = nums2.size();
int total = n1 + n2;
if (total % 2 == 1) {
return getKth(nums1, 0, nums2, 0, total / 2 + 1);
} else {
return (getKth(nums1, 0, nums2, 0, total / 2) +
getKth(nums1, 0, nums2, 0, total / 2 + 1)) /
2.0;
}
}

private:
int getKth(const vector<int>& A, int i, const vector<int>& B, int j,
int k) {
// Always make A the shorter one
if (i >= A.size())
return B[j + k - 1];
if (j >= B.size())
return A[i + k - 1];
if (k == 1)
return min(A[i], B[j]);

int midA = INT_MAX, midB = INT_MAX;
if (i + k / 2 - 1 < A.size())
midA = A[i + k / 2 - 1];
if (j + k / 2 - 1 < B.size())
midB = B[j + k / 2 - 1];

if (midA < midB) {
return getKth(A, i + k / 2, B, j, k - k / 2);
} else {
return getKth(A, i, B, j + k / 2, k - k / 2);
}
}
};




No comments:

Post a Comment