LeetCode-004 Median of Two Sorted Arrays

Posted by theorok on Sat, 19 Feb 2022 05:09:39 +0100

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:

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

Solution 1

To find the median in an ordered array, you only need to find the element in the middle coordinate position, so you can divide the topic into two steps:

1. Combine two ordered arrays into an ordered array

2. Find the median from the merged ordered array

code implementation

/**
 * Find the median in two ordered arrays
 *
 * @param nums1 Ordered array 1
 * @param nums2 Ordered array 2
 * @return median
 */
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
    int[] l = mergeArray(nums1, nums2);
    if (l.length % 2 == 0) {
        return (l[result.length / 2] + l[l.length / 2 - 1]) / 2.0;
    } else {
        return l[l.length / 2];
    }
}

/**
 * Merge two ordered arrays into one ordered array
 *
 * @param nums1 Ordered array 1
 * @param nums2 Ordered array 2
 * @return Merged ordered array
 */
private int[] mergeArray(int[] nums1, int[] nums2) {
    int len1 = nums1.length;
    int len2 = nums2.length;

    int i = 0;
    int j = 0;
    int k = 0;
    int[] result = new int[len1 + len2];
    while (i < len1 && j < len2) {
        if (nums1[i] < nums2[j]) {
            result[k] = nums1[i];
            i++;
        } else {
            result[k] = nums2[j];
            j++;
        }
        k++;
    }
    while (i < len1) {
        result[k++] = nums1[i++];
    }
    while (j < len2) {
        result[k++] = nums2[j++];
    }
    return result;
}

You need to traverse two arrays and copy them to another array. The time complexity and space complexity are: O ( m + n ) O(m + n) O(m+n).

Solution 2

Assuming that the length of the merged ordered array is l (L= m + n), the median is to find the K-th number:

  • If (m + n) is an even number, K = L/2 and K = L/2 + 1, the median is the mean of the two numbers
  • If (m + n) is odd, K = L/2

The problem becomes: find the smallest number K in two ordered arrays. The problem is divided into two steps:

1. Traverse array A and array B, compare the element values of the two arrays, move the subscript of the smaller element backward, and record the number of elements k that have been traversed

2. When the number of traversals is equal to K, the median is obtained

code implementation

/**
 * Find the median in two ordered arrays
 *
 * @param nums1 Ordered array 1
 * @param nums2 Ordered array 2
 * @return median
 */
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
    int l = nums1.length + nums2.length;
    if (l % 2 == 0) {
        return getMedianFromOddNums(nums1, nums2, l);
    } else {
        return getMedianFromEvenNums(nums1, nums2, l);
    }
}

/**
 * Find the K-th number in two ordered arrays
 *
 * @param nums1 Ordered array 1
 * @param nums2 Ordered array 2
 * @param k     Number K, no subscript
 * @return Return the K-th number
 */
private double getKthElement(int[] nums1, int[] nums2, int k) {
    int length1 = nums1.length, length2 = nums2.length;
    int index1 = 0, index2 = 0;
    // Number of elements that have been traversed
    int l = 0;
    // Target element value
    int targetNum = 0;
    while (index1 < nums1.length && index2 < nums2.length) {
        if (nums1[index1] < nums2[index2]) {
            index1++;
            l++;
            if (l == k) {
                targetNum = nums1[index1 - 1];
            }
        } else {
            index2++;
            l++;
            if (l == k) {
                targetNum = nums2[index2 - 1];
            }
        }
    }
    while (index1 < nums1.length) {
        index1++;
        l++;
        if (l == k) {
            targetNum = nums1[index1 - 1];
        }
    }
    while (index2 < nums2.length) {
        index2++;
        l++;
        if (l == k) {
            targetNum = nums2[index2 - 1];
        }
    }
    return targetNum;
}

Only one traversal is performed without using additional space. The space complexity is: O ( 1 ) O(1) O(1), time complexity: O ( m + n ) O(m+n) O(m+n).

Solution 3

How to reduce the time complexity and meet the requirements of the problem l o g ( m + n ) log(m+n) log(m+n)?

The first two methods are essentially the same. They both look for the median in the merged ordered array. There is no sorting algorithm to find the median in the l o g ( m + n ) log(m+n) log(m+n) combines the two arrays into an ordered array within the time complexity, so the idea of "solution 1" and "solution 2" is no longer feasible.

The common time complexity is l o g ( m + n ) log(m+n) log(m+n) algorithms mainly include binary tree and dichotomy. The former needs to establish a tree first, and the complexity is O ( n l o g n ) O(n log n) O(nlogn), then only use dichotomy.

How do you use dichotomy here? Where is the breakthrough?

In "solution 1" and "solution 2", only one subscript is moved in each comparison, and only one element can be excluded. If multiple elements can be excluded in each comparison, or even the number of elements can be excluded proportionally, the time complexity can be reduced.

The following is the idea of the official explanation:

We know we need to rule it out K − 1 K-1 K − 1 number K K K number, if the number of elements excluded each time is K 2 \frac{K}{2} 2K​, K 4 \frac{K}{4} 4K​, K 8 \frac{K}{8} 8K... Can achieve the required time complexity of the topic l o g ( m + n ) log(m+n) log(m+n).

Because the data A and B are ordered, it is difficult to compare A [ K 2 − 1 ] A[\frac{K}{2}-1] A[2K − 1] and B [ K 2 − 1 ] B[\frac{K}{2}-1] Size of B[2K − 1], if A [ K 2 − 1 ] ≤ B [ K 2 − 1 ] A[\frac{K}{2}-1] \le B[\frac{K}{2}-1] A[2K − 1] ≤ B[2K − 1] has the following relationship:

A [ 0 , . . . , K 2 − 2 ] ≤ A [ K 2 − 1 ] ≤ B [ K 2 − 1 ] A[0,...,\frac{K}{2}-2] \le A[\frac{K}{2}-1] \le B[\frac{K}{2}-1] A[0,...,2K​−2]≤A[2K​−1]≤B[2K​−1]

If B [ 0 , . . . , K 2 − 2 ] B[0,...,\frac{K}{2}-2] B[0,...,2K − 2] ratio is also less than A [ K 2 − 1 ] A[\frac{K}{2}-1] A[2K − 1], then the ratio A [ K 2 − 1 ] A[\frac{K}{2}-1] A[2K − 1] the smallest number is only K − 2 K-2 K − 2, A [ K 2 − 1 ] A[\frac{K}{2}-1] A[2K − 1] is not the first K K The number of K can be excluded A [ 0 , . . . , K 2 − 1 ] A[0,...,\frac{K}{2}-1] A[0,...,2K − 1] total K 2 \frac{K}{2} 2K # elements.

As you can see, the comparison A [ K 2 − 1 ] A[\frac{K}{2}-1] A[2K − 1] and B [ K 2 − 1 ] B[\frac{K}{2}-1] After B[2K − 1], it can be excluded K 2 \frac{K}{2} 2K is impossible K K For the number of K elements, the search range is reduced by half. We only need to continue the binary search on the excluded new array and reduce it according to the number of excluded arrays K K The value of K.

There are three special cases:

  • If A [ K 2 − 1 ] A[\frac{K}{2}-1] A[2K − 1] and B [ K 2 − 1 ] B[\frac{K}{2}-1] If B[2K − 1] is out of bounds, you can select the last element in the corresponding array and reduce it according to the actual value K K The value of K instead of subtracting it directly K 2 \frac{K}{2} 2K​
  • If an array is empty, it means that all elements in the array are traversed and excluded. We can directly return the second element in another array K K K elements
  • If K = = 1 K==1 K==1, returns the minimum value of the current elements of the two arrays

code implementation

/**
 * Find the median in two ordered arrays
 *
 * @param nums1 Ordered array 1
 * @param nums2 Ordered array 2
 * @return median
 */
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
    int l = nums1.length + nums2.length;
    if (l % 2 == 1) {
        int k = l / 2; // k is the subscript, and k+1 is the number to find
        return getKthElement(nums1, nums2, k + 1);
    } else {
        int k1 = l / 2, k2 = l / 2 - 1;
        return (getKthElement(nums1, nums2, k1 + 1) + getKthElement(nums1, nums2, k2 + 1)) / 2.0;
    }
}

/**
 * Find the K-th number in two ordered arrays
 *
 * @param nums1 Ordered array 1
 * @param nums2 Ordered array 2
 * @param k     The K-th number is not a subscript
 * @return Return the K-th number
 */
public int getKthElement(int[] nums1, int[] nums2, int k) {
    int lenth1 = nums1.length, length2 = nums2.length;
    // The current starting subscript of array 1 and array 2
    int index1 = 0, index2 = 0;

    while (true) {
        // If the initial index of an array is equal to the length of the array, it means that the array has been traversed. Look for the k-th number from another array
        if (index1 == lenth1) {
            return nums2[index2 + k - 1];
        }
        if (index2 == length2) {
            return nums1[index1 + k - 1];
        }
        // If the value of k is 1, it means that only the first number is found and the smaller value of the two arrays is returned
        if (k == 1) {
            return Math.min(nums1[index1], nums2[index2]);
        }
        // Generally, take half of the k as the offset and compare the values of the subscripts corresponding to the two arrays
        int half = k / 2;
        int newIndex1 = Math.min(index1 + half, lenth1) - 1;
        int newIndex2 = Math.min(index2 + half, length2) - 1;
        if (nums1[newIndex1] <= nums2[newIndex2]) {
            // K subtracts the number of elements between newIndex1 and index1 to get the next target K
            k -= (newIndex1 - index1 + 1);
            // Update start subscript to next number
            index1 = newIndex1 + 1;
        } else {
            k -= (newIndex2 - index2 + 1);
            index2 = newIndex2 + 1;
        }
    }
}

Each cycle can reduce the search range by half, so the time complexity is: O ( l o g ( n + n ) ) O(log(n+n)) O(log(n+n)).

Topics: Algorithm data structure leetcode