1. 程式人生 > >LeetCode-4:Median of Two Sorted Arrays (兩個排序陣列的中位數)

LeetCode-4:Median of Two Sorted Arrays (兩個排序陣列的中位數)

題目:

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

例子:

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

問題解析:

給定兩個排序的陣列,給出兩個陣列的中位數。

連結:

思路標籤

雙指標割和二分法兩個排序陣列的第k個元素

解答:

  • 問題為尋找中位數,這類問題屬於尋找兩個排序陣列中的第k個元素。
  • 下面給出雙指標合併和割的第k個元素。

1. 雙指標合併陣列求中位數

  • 利用兩個指標分別指向第一和第二個陣列;
  • 利用兩個陣列的大小,使用計數直接找到對應的中位數。
class Solution {
public:
    double findMedianSortedArrays(vector<int>& nums1, vector
<int>
& nums2) { int m = nums1.size(); int n = nums2.size(); int length = m + n; int midl = 0, midr = 0; if(length % 2 == 0){ midl = length/2; midr = length/2+1; } else{ midl = length/2+1; midr = length/2
+1; } int k = 0; int i = 0, j = 0; int mid[2] = {0,0}; while(k < midr){ int a = (i < m)? nums1[i] : INT_MAX; int b = (j < n)? nums2[j] : INT_MAX; if(a <= b){ k++; i++; }else{ k++; j++; } if(k == midl) mid[0] = min(a, b); if(k == midr) mid[1] = min(a, b); } return (mid[0] + mid[1])/2.0; } };

2. 利用割的思想,以及二分法來尋找中位數。

  • 利用割的思想尋找兩個陣列中的第k個元素:我們設:
    • Ci為第i個數組的割;
    • Li為第i個數組割後的左元素;
    • Ri為第i個數組割後的右元素。
  • 對於兩個陣列:
    • 首先Li<=Ri是一定的(因為陣列有序);
    • 如果我們令L1<=R2 && L2<=R1,那麼左半邊 全小於右半邊;
    • 如果左邊的元素個數相加剛好等於k,那麼第k個元素就是Max(L1,L2);
    • 如果 L1>R2,說明陣列1的左邊元素太大,我們把C1減小,把C2增大。
    • L2>R1同理,把C1增大,C2減小。
  • 例子:
    • 假設k=3
    • 對於 [1 4 7 9] [2 3 5]
    • 設C1 = 2,那麼C2 = k-C1 = 1
    • [1 4/7 9] [2/3 5]
    • 這時候,L1(4)>R2(3),說明C1要減小,C2要增大,C1 = 1,C2=k-C1 = 2
    • [1/4 7 9] [2 3/5]
    • 這時候,滿足了L1<=R2 && L2<=R1,第3個元素就是Max(1,3) = 3。
  • 中位數的特殊處理:(考慮奇偶的問題)
    • 虛擬加入‘#’(這個trick在manacher演算法中也有應用),讓陣列長度恆為奇數(2n+1恆為奇數)。
    • 加入前:[1 4 7 9],長度4;加入後:[# 1 # 4 # 7 # 9 #],長度9;
    • 加入前:[2 3 5],長度3;加入後:[# 2 # 3 # 5 #],長度7。
    • 加完之後,每個位置可以通過/2得到原來元素的位置。
    • 無論是奇數還是偶數的情況都有:
    • Li = (Ci-1)/2, Ri = Ci/2
  • 把2個數組看做一個虛擬的陣列A,目前有2m+2n+2個元素,找中位數,割在m+n+1處,所以我們只需找到m+n+1位置的元素和m+n+2位置的元素就行了。
  • 左邊:A[m+n+1] = Max(L1+L2)
  • 右邊:A[m+n+2] = Min(R1+R2)
Mid = (A[m+n+1]+A[m+n+2])/2 = (Max(L1+L2) + Min(R1+R2) )/2
  • 更快地尋找割的位置的方法:二分法
    • 只要C1或C2確定,另外一個也就確定了。這裡,為了效率,我們肯定是選長度較短的做二分,假設為C1。
    • L1>R2,把C1減小,C2增大。—> C1向左二分
    • L2>R1,把C1增大,C2減小。—> C1向右二分
  • 如果C1或C2已經到頭怎麼辦?這種情況出現在:如果有個陣列完全小於或大於中值。可能有4種情況:
    • C1 = 0 :陣列1整體都比中值大,L1賦小值取L2,中值在2中;
    • C1 = 2*m :陣列1整體都比中值小,R1賦大值取R2,中值在2中;
    • C2 = 0 :陣列2整體都比中值大,L2賦小值取L1,中值在1中;
    • C2 = 2*n:陣列2整體逗比中值小,R2賦大值取R1,中值在1中。
class Solution {
public:
    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
        int m = nums1.size();
        int n = nums2.size();
        if(m > n)
            return findMedianSortedArrays(nums2, nums1);

        int L1, R1, L2, R2, C1, C2, low = 0, high = 2*m;
        while(low <= high){
            C1 = (low + high)/2;
            C2 = m+n - C1;

            L1 = (C1 == 0)? INT_MIN : nums1[(C1-1)/2];
            R1 = (C1 == 2*m)? INT_MAX : nums1[C1/2];
            L2 = (C2 == 0)? INT_MIN : nums2[(C2-1)/2];
            R2 = (C2 == 2*n)? INT_MAX : nums2[C2/2];

            if(L1 > R2)
                high = C1-1;
            else if(L2 > R1)
                low = C1+1;
            else
                break;
        }
        return (max(L1, L2) + min(R1, R2))/2.0;
    }
};