LeetCode-4:Median of Two Sorted Arrays (兩個排序陣列的中位數)
阿新 • • 發佈:2019-01-05
題目:
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;
}
};