LeetCode #4 Median of Two Sorted Arrays

Keywords: C++

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

 

meaning of the title

 

Given two sorted arrays whose sizes are m and N respectively, finding the median of the two arrays requires that the overall running time complexity be O (log (m + n).

 

Example

 

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

The median is 2.0
nums1 = [1, 2]
nums2 = [3, 4]

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

 

thinking

 

As soon as I see that the given array is sorted and the time complexity requirement is O (log (m + n), I immediately associate with binary search and merge. I first use merge to do this problem, although the time complexity is O(n), but the optimized algorithm execution time is also good.

The idea of violent merger is very simple. Open an auxiliary space C, use index I1 and I2 to compare num1[i1] and num2[i2], and save the smallest value in C, and finally output the median in C in parity.

We can optimize the merging algorithm spatially. Since the median of C is half of the table length, the latter half of the table length can actually be saved. Moreover, since the solution of this problem only needs to return a double median, we do not really need to create an array, but use res1 res2. When the index of C reaches the middle position, we can store the last two elements of C and output them according to parity, which can reduce the space overhead.   

 

//Runtime: 52 ms
#include<algorithm>
using std::min;
class Solution {
public:
    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
        if(nums1.empty() && nums2.empty()) {
            return 0;
        }
        int l1 = nums1.size(), l2 = nums2.size();
        int l3 = l1 + l2;
        int i1 = 0, i2 = 0, i3 = 0;
        int indexM1 = (l3-1)/2, indexM2 = l3/2; //the former for odd,the latter for even
        double res1 = 0, res2 = 0;
        //mergeSort
        while (i1 < l1 && i2 < l2) { 
            if (i3 > indexM2) {
                break;
            }
            if (i3 == indexM1) {
                res1 = min(nums1[i1],nums2[i2]);
            }
            if (i3 == indexM2) {
                res2 = min(nums1[i1],nums2[i2]);
            }
            if (nums1[i1] < nums2[i2]) {
                i1++;
            }
            else {
                i2++;
            }
            i3++;
        }
        
        while (i1 < l1) { //nums1 has element
            if (i3 > indexM2) {
                break;
            }
            if (i3 == indexM1) {
                res1 = nums1[i1];
            }
            if (i3 == indexM2) {
                res2 = nums1[i1];
            }
            i1++;
            i3++;  
        }
        while (i2 < l2) { //nums2 has element
            if (i3 > indexM2) {
                break;
            }
            if (i3 == indexM1) {
                   res1 = nums2[i2];
            }
            if (i3 == indexM2) {
                res2 = nums2[i2];
            }
            i2++;
            i3++;
        }
        
        //for odd,return res1;for even,return (res1 + res2)/2.0;
        if (l3%2 == 0) {
            return (res1 + res2)/2.0;
        }
        else {
            return res1;
        }
    }
};

 

However, the time complexity of merging algorithm is O(n) in essence, which can not meet the requirements of this topic. To further optimize the exponential change of time complexity, only the strategy can be changed. Here, a pseudo-dichotomy search is used, and the time complexity is O(lg(n)^2.

First of all, the median is essentially the smallest number of k, where k = m + n) / 2. If it is even, then find the smallest number of k+1 and take the average.

Let's suppose that the number of elements in two arrays is larger than k/2. We take out A[k/2-1] and B[k/2-1], which represent the small K/2 elements of A and B respectively. If A[k/2-1] <= B[k/2-1], it means that A[0] to A[k/2-1] are before the smallest K in the merged sequence, that is to say, A[0...k/2-1] can't be larger than the smallest k, because A[0...k/2-1] can't be larger than the smallest K. This can be discarded.

Similarly, similar conclusions exist for A [k/2-1] <= B [k/2-1].

Discuss the time complexity of this algorithm. Because each processing is only the first k/2 elements of an array, for an array of length m, its time complexity is O(lg(m). For an array of length n, its time complexity is O(lg(n). So its time complexity is O(lg(m)* lg(n). Because it is not a dichotomy in the overall sense. Find, so you can't reach O(lgn).

 

//Runtime: 59 ms
class Solution {
public:
    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
        int m = nums1.size(), n = nums2.size();
        if ((m+n)%2 == 0) {
            return (findKth (nums1, nums2, (m+n)/2) + findKth (nums1, nums2, (m+n)/2+1)) / 2.0;       
        }else { 
            return findKth (nums1, nums2, (m+n)/2 + 1);
        }
    }
private:    
    int findKth(vector<int> a, vector<int> b, int k) {
        int m = a.size(), n = b.size();
        if (m > n) {
            return findKth(b, a, k);
        }
        if (m == 0) {
            return b[k-1];
        }
        if (k == 1) {
            return std::min(a[k-1], b[k-1]);
        }
        int i = std::min(k/2, m), j = std::min(k/2, n);
        if (a[i-1] < b[j-1]) {            
            return findKth (vector<int>(a.begin()+i, a.end()), b, k-i);
        }else{
            return findKth (a, vector<int>(b.begin()+j,b.end()), k-j);
        }
    }   
};

 

Some people have used other methods to achieve the overall sense of dichotomy search, the time complexity of O(lg(m+n)), very ingenious, because I did not implement, so here is only a link to the method, for your reference and discussion.

Posted by lional on Thu, 10 Jan 2019 14:24:11 -0800