LeetCode Median of Two Sorted Arrays(004)解法總結

NewCoder1024發表於2020-03-11

描述

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

You may assume nums1 and nums2 cannot be both empty.

Example 1:

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

The median is 2.0複製程式碼

Example 2:

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

The median is (2 + 3)/2 = 2.5複製程式碼

思路

題目明確指出時間複雜度應該在O(log (m+n))。猜測可能用到樹型資料結構進行優化。自己沒思路。

先試一下不限制時間複雜度的:合併兩個陣列,再取合併完成的陣列(有序)的中位數。

class Solution {
    public double findMedianSortedArrays(int[] nums1, int[] nums2) {
        int totalLen = nums1.length + nums2.length;
        int[] total = new int[totalLen];
        int i1 = 0, i2 = 0, count = 0;
        while(i1<nums1.length && i2<nums2.length){
            if(nums1[i1]<nums2[i2]){
                total[count] = nums1[i1];
                i1++;
            }else{
                total[count] = nums2[i2];
                i2++;
            }
            count++;
        }
        while(i1<nums1.length){
            total[count] = nums1[i1];
            i1++;count++;
        }
        while(i2<nums2.length){
            total[count] = nums2[i2];
            i2++;count++;
        }
        if(totalLen%2 == 0){
            return (double)(total[totalLen/2 -1] + total[totalLen/2])/2;
        }else{
            return (double)total[(totalLen-1)/2];
        }
    }
}複製程式碼

時間複雜度應該是O(m+n),大於題目要求。不過居然也AC了。時間空間消耗看起來也都不錯。

Runtime: 2 ms, faster than 99.83% of Java online submissions for Median of Two Sorted Arrays.
Memory Usage: 42 MB, less than 98.61% of Java online submissions for Median of Two Sorted Arrays.

變換思路

看了評論區的解,沒有用到樹型資料結構,使用到了二分法。非常巧妙。

In order to solve this question, we need to first understand what a median is. A median is the middle value of a dataset.
Since we have 2 seperately sorted array in this question, to find the middle value is somewhat complicated. However, keep in mind that we do not care about the actual value of the numbers, what we want is the middle point from the combination of 2 arrays. In other words, we are looking for the middle index of the 2 arrays. Thus approach like binary search could be employed.
Based on the fact that the 2 arrays are sorted seperatedly, we could try to get the submedian of the 2 arrays in each round. Than compare them. And the basic idea is that the left half of the array with a smaller submedian can never contains the common median.

該思想的方法是,在原陣列保證有序的情況下,通過不斷的調整兩個整型陣列的兩個分隔指標(一個陣列一個)。使得第一個陣列指標左邊的元素全部小於第二個陣列指標右邊的元素,使第二個陣列指標左邊的元素全部小於第一個陣列指標右邊的元素。

由於陣列本身是有序的,如此一來只要使得兩個指標左邊的數字數量和指標右邊的數字數量相等,就可以得到所有元素的中位數(一個或者是兩個的平均值,即左邊兩個陣列的最大數字和右邊兩個陣列的最小值)。

進行上述調和過程的時候對移動步長使用二分法控制指標移動的位置。

public class Solution {
    public double findMedianSortedArrays(int[] nums1, int[] nums2) {
        // Deal with invalid corner case. 
        if (nums1 == null || nums2 == null || nums1.length == 0 
            || nums2.length == 0) return 0.0;
        
        int m = nums1.length, n = nums2.length;
        int l = (m + n + 1) / 2; //left half of the combined median
        int r = (m + n + 2) / 2; //right half of the combined median
        
        // If the nums1.length + nums2.length is odd, 
        // the 2 function will return the same number
        // Else if nums1.length + nums2.length is even, 
        // the 2 function will return the left number and right number that make up a median
        return (getKth(nums1, 0, nums2, 0, l) + getKth(nums1, 0, nums2, 0, r)) / 2.0;
    }
    
    private double getKth(int[] nums1, int start1, int[] nums2, int start2, int k) {
        // This function finds the Kth element in nums1 + nums2
        
        // If nums1 is exhausted, return kth number in nums2
        if (start1 > nums1.length - 1) return nums2[start2 + k - 1];
        
        // If nums2 is exhausted, return kth number in nums1
        if (start2 > nums2.length - 1) return nums1[start1 + k - 1];
        
        // If k == 1, return the first number
        // Since nums1 and nums2 is sorted, 
        // the smaller one among the start point of nums1 and nums2 is the first one
        if (k == 1) return Math.min(nums1[start1], nums2[start2]);
        
        int mid1 = Integer.MAX_VALUE;
        int mid2 = Integer.MAX_VALUE;
        if (start1 + k / 2 - 1 < nums1.length) mid1 = nums1[start1 + k / 2 - 1];
        if (start2 + k / 2 - 1 < nums2.length) mid2 = nums2[start2 + k / 2 - 1];
        
        // Throw away half of the array from nums1 or nums2. And cut k in half
        if (mid1 < mid2) {
            // nums1.right + nums2
            return getKth(nums1, start1 + k / 2, nums2, start2, k - k / 2); 
        } else {
            // nums1 + nums2.right
            return getKth(nums1, start1, nums2, start2 + k / 2, k - k / 2); 
        }
    }
}複製程式碼
Runtime: 2 ms, faster than 99.83% of Java online submissions for Median of Two Sorted Arrays.
Memory Usage: 42 MB, less than 98.61% of Java online submissions for Median of Two Sorted Arrays.

時間空間消耗差不多,但這種方法的時間複雜度是符合題意的。


相關文章