尋找兩個有序數組的中位數


題目描述:
給定兩個大小為 m 和 n 的有序數組 nums1 和 nums2。
請你找出這兩個有序數組的中位數,並且要求算法的時間復雜度為 O(log(m + n))。
你可以假設 nums1 和 nums2 不會同時為空。
鏈接https://leetcode-cn.com/problems/median-of-two-sorted-arrays/solution/

示例 1:

nums1 = [1, 3]
nums2 = [2]
則中位數是 2.0

示例 2:

nums1 = [1, 2]
nums2 = [3, 4]
則中位數是 (2 + 3)/2 = 2.5

思路:(官方題解)
由題目可知,給定兩個有序數組,假設分別為數組A,數組B。
在任一位置將數組A划分成兩個部分

      left_A       |      right_A
A[0], A[1], ..., A[i-1] | A[i], A[i+1], ..., A[m-1]

同理在任一位置將數組B划分成兩個部分:

     left_B        |       right_B
B[0], B[1], ..., B[j-1] | B[j], B[j+1], ..., B[n-1]

將left_A和left_B放入一個集合,並將right_A和right_B放入另一個集合。再把這兩個新的集合分別命名為left_part和right_part:

    left_part      |     right_part
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]

如果我們可以確認:

1. $len(left_part) = len(right_part)$
2. $max(left_part)\le min(right_part)$

那么,我們已經將{A, B}中的所有元素划分為相同長度的兩個部分,且其中一部分的元素總是大於另一部分的元素。那么:
$median = \frac{max(left_part)+min(right_part)}{2}$
確保這兩個條件,我們只需要保證:

1. $i+j = m-i+n-j$(或:($m-i+n-j+1$))如果$n \ge m$,只需要使$i = 0 \sim m,j=\frac{m+n+1}{2}-i$
2. $B[j-1] \le A [i] 以及 A[i-1] \le B[j]$

接着,按照以下步驟進行二叉樹搜索:

1.設$imin=0, imax=m$,然后開始在$\left|{imin, imax}\right|$中進行搜索。
2.令$i = \frac{imin+imax}{2}, j =\frac{m+n+1}{2} - i$
3.現在我們有$len(left_part)=len(right_part)$.我們會遇到三種情況:

  • $B[j - 1]| \le A[i]$且$A[i-1]\le B[j]$
    這意味着我們找到了目標對象$i$,所以可以停止搜索。
  • $B[j-1] > A[i]$:
    這意味着$A[i]$太小,我們必須調整$i$以使$B[j-1] \le A[i]$
    由$i$和$j$的關系可知,$i$增大的時候,$j$就會減小。所以此處必須增大$i$,將搜索范圍調整為$[i + 1, imax]$。因此$imin=i+1$,並轉向步驟2。
  • $A[i-1]>B[j]$。這說明$A[i-1]$太大,我們必須減小$i$以使$A[i - 1]\le B[j]$。也就是說,我們必須將搜索范圍調整為$[imin, i-1]$。
    因此,着$imax=i-1$,並轉向步驟2.

當找到目標對象i時,中位數為:

$max(A{i-1],B[j-1]})$,當$m+n$為奇數時

$\frac{max(A[i-1],B[j-1])+min(A[i],B[j])}{2}$,當$m+n$為偶數時

考慮臨界值。$i=0, i=m, j=0,j=n$。此時,$A[i-1], B[j-1], A[i], B[j]$可能不存在。
確保$max(left_part)\le min(right_part)$。如果$i$和$j$不是臨界值,必須同時檢查$B[j-1]\le A[i]$以及$A[i-1] \le B[j]$是否成立。但是如果$A[i-1], B[j-1], A[i], B[j]$中部分不存在,我們只需要檢查兩個條件中的一個(或不需要檢查)

在$[0, m]$中搜索目標對象$i$,以使:($j=0$ or $i=m$ or $B[j-1]\le A[i]$) 或是 ($i=0$ or $j=n$ or $A[i-1]\le B[j]$),其中$j=\frac{m+n+1}{2}-i$

循環搜索中,會遇到三種情況:

1.($j=0$ or $i=m$ or $B[j-1]\le A[i]$) 或是 ($i=0$ or $j=n$ or $A[i-1]\le B[j]$)這說明$i$是完美的,可以停止搜索。
2.$j>0$ and $i < m$ and $B[j-1] >A[i]$這意味着$i$太小,必須增大它。
3.$i>0$ and $j < n$ and $A[i-1]>B[j]$這意味着$i$太大,我們必須減小它。

JAVA版

class Solution {
    public double findMedianSortedArrays(int[] A, int[] B) {
        int m = A.length;
        int n = B.length;
        if (m > n) { // to ensure m<=n
            int[] temp = A; A = B; B = temp;
            int tmp = m; m = n; n = tmp;
        }
        int iMin = 0, iMax = m, halfLen = (m + n + 1) / 2;
        while (iMin <= iMax) {
            int i = (iMin + iMax) / 2;
            int j = halfLen - i;
            if (i < iMax && B[j-1] > A[i]){
                iMin = i + 1; // i is too small
            }
            else if (i > iMin && A[i-1] > B[j]) {
                iMax = i - 1; // i is too big
            }
            else { // i is perfect
                int maxLeft = 0;
                if (i == 0) { maxLeft = B[j-1]; }
                else if (j == 0) { maxLeft = A[i-1]; }
                else { maxLeft = Math.max(A[i-1], B[j-1]); }
                if ( (m + n) % 2 == 1 ) { return maxLeft; }

                int minRight = 0;
                if (i == m) { minRight = B[j]; }
                else if (j == n) { minRight = A[i]; }
                else { minRight = Math.min(B[j], A[i]); }

                return (maxLeft + minRight) / 2.0;
            }
        }
        return 0.0;
    }
}

Python版

def median(A, B):
    m, n = len(A), len(B)
    if m > n:
        A, B, m, n = B, A, n, m
    if n == 0:
        raise ValueError

    imin, imax, half_len = 0, m, (m + n + 1) / 2
    while imin <= imax:
        i = (imin + imax) / 2
        j = half_len - i
        if i < m and B[j-1] > A[i]:
            # i is too small, must increase it
            imin = i + 1
        elif i > 0 and A[i-1] > B[j]:
            # i is too big, must decrease it
            imax = i - 1
        else:
            # i is perfect

            if i == 0: max_of_left = B[j-1]
            elif j == 0: max_of_left = A[i-1]
            else: max_of_left = max(A[i-1], B[j-1])

            if (m + n) % 2 == 1:
                return max_of_left

            if i == m: min_of_right = B[j]
            elif j == n: min_of_right = A[i]
            else: min_of_right = min(A[i], B[j])

            return (max_of_left + min_of_right) / 2.0


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM