4. Median of Two Sorted Arrays
題目簡析:在兩個有序數組裏面找出總的中位數,第一個思路當然是對整體排序成爲一個數組,在根據數字數目直接找到中位數,這樣顯然是毫無難度的,因此題目給出一個條件——O(log (m+n))的時間複雜度。看到O(log),第一個聯想起來的自然是二分法,因此如何結合二分法找出中位數,這就是我基本的解題靈感所在。
首先根據題目字母的意思,暫時我們擁有的條件就是“數組有序”這個條件,結合中位數的定義,我們可以數字有序這個特點,通過從兩端逼近中央,不斷縮小中位數所在的區間,當這個區間足夠小的時候,自然就找到中位數了。那麼問題就是——如何逼近?多大的區間纔是足夠小?
首先解決第一個問題——如何逼近?
假設有數組A、B,大小分別爲m、n(這裏暫且認爲m≥n),假如A、B都是有序的話,那麼會有以下的特點:
1. 這裏假如將AB整體排好序後,有一個新的有序數組C,那麼C顯然大小爲m+n,中位數爲mid, 這裏我們令k=(m+n)/2,根據中位數的定義,假如m+n是 奇數時,由於C是有序,那麼中位數mid兩側分別有k個數字。
2. 由於m≥n且均爲非負整數,所以m/2和n/2必定小於k
3. 分別從A、B兩側刪除n/2個數字(比如刪除A側最左端n/2個數和B最右端n/2個數;或者刪除A最右端n/2個數和B最左端n/2個數),中位數不會被刪除,因爲k>n/2,兩端可刪除的數字數目一定大於待刪除數目
4. 兩側對稱刪除同等量的數字後,特點1~3不變
根據1~4這4點,也就是說不斷重複3 的話,我們可以實現不斷從AB數組中刪除元素,從而縮短中位數所在區間。
第二個問題——多大的區間才足夠小?
這裏我們仍要看回上面的1~4點,從第3點,我們可以看到n是個很關鍵的數字,因爲它限制了刪除數字的上限,所以當我們將數組B逼近至無法再削減時,就達到我們需要的區間了。顯然,達到這個區間需要的時間複雜度,僅僅是O(logn),沒有超出題目的要求。
由於奇偶數的不同,中位數的計算方法不同,由於每次刪除的數字總量都是偶數,因此奇偶性不會變化,因此數組B需要保留的數字,是個要仔細探討的問題。結論是什麼呢?—— 結論是B要保留的數字是2個,爲什麼?
(1)假如m+n是奇數時,計算中位數我們只需要查找一個數字,此時中位數mid也許在A中也許在B中,所以B必須至少有1個數。
(2)假如m+n是偶數時,計算中位數我們需要查找兩個數字mid1和mid2,(mid1+mid2)/2計算中位數,mid1和mid2也許在A中也許在B中,甚至兩個數都在同一個數組裏,所以B必須保留2個數字,才能保證中位數不會被刪除掉這裏還有一個小細節,假如給定的數組分別是A: [1, 2, 3] 和 B: [1]時,怎麼辦呢?爲了解決這個問題,我們可以在分別在A、B的兩側添加一個數字,簡化這個問題。
比如 : 數組A [1, 2, 3] ——>[min, 1, 2, 3]
數組B [1] ——> [1,max]
這裏的min和max分別是所有數字中的最小值和最大值,使其分別能位於所有數字的最小端和最大端,這樣就不會影響中位數的計算(因爲最兩端的數字必定不參與計算,那麼添加數字前的計算情況和添加數字後的計算情況都是一樣的)。
當然,這裏還有幾種特殊情況,通過if語句直接篩掉,留下最主要的情況進行計算,這些特殊情況分別包括:
(1) 空數組 A:[ a …… b] B:[] 或者 A [] B [],這裏直接計算A的中位數或者返回0即可
(2) 單數字數組 A: [mid1] B: [mid2] ,直接返回(mid1+mid2)/2
(3) m<n,由於以上解析均建立在m≥n這個條件上,因此這裏通過調用函數自身,把數組A、B互換即可
比如:
if(m<n)
return findMedianSortedArrays(nums2,nums1);
現在,我們已經解決了如何逼近區間和區間大小的問題,剩下的就是計算中位數的問題。因爲在以上操作後,我們把B的數目控制在了2 這個大小,根據m≥n,A的數目也是≥2的,而且A的數目也許還非常大,不過這裏要提醒的是,我們每次縮減區間所刪除的數字數目都是偶數,而且是從兩端分別刪除,所以這裏有個很重要的特點——中位數兩端的數字數目也是相同的,並且不影響計算中位數需要的數字數目(1位或2位)。
這意味着,就算A剩餘數字數目很大,兩側的大部分數字是可以不考慮的,因爲他們既不參與計算,兩端數字數目也是相同,所以我們只需要取數組A最中間的幾個數字和數字B的2個數字進行比較,即可完成中位數的查找和計算,這裏需要列舉一下幾種計算情況
1. m=n=2時
排列情況有
情況 |
|
|
|
中位數 |
1 |
a1 |
(a2 b1) or (b1 a2) |
b2 |
(a2 + b1)/2 |
2 |
b1 |
(b2 a1) or (a1 b2) |
a2 |
(a1 + b2)/2 |
3 |
a1 |
b1 b2 |
a2 |
(b1 + b2)/2 |
4 |
b1 |
a1 a2 |
b2 |
(a1 + a2)/2 |
2.m爲偶數且m≥4時
排列情況有
序號 |
|
|
|
中位數 |
1 |
a1 a2 |
a3 a4 |
b1 b2 |
(a3+a4)/2 |
2 |
b1 b2 |
a1 a2 |
a3 a4 |
(a1+a2)/2 |
3 |
a1 a2 |
b1 b2 |
a3 a4 |
(b1+b2)/2 |
4 |
(a1 b1)or(b1 a1) |
a2 a3 |
(a4 b2)or(b2 a4) |
(a1+a3)/2 |
5 |
(a1 b1)or(b1 a1) |
(a2 b2)or(a2 b2) |
a3 a4 |
(a2+b2)/2 |
6 |
a1 a2 |
(b1 a3)or(a3 b1) |
(a4 b2)or(b2 a4) |
(a3+b1)/2 |
3.m爲奇數且m≥3時
排列情況有
情況 |
|
|
|
中位數 |
1 |
b1 b2 |
a1 |
a2 a3 |
a1 |
2 |
a1 a2 |
a3 |
b1 b2 |
a3 |
3 |
(b1 a1)or(a1 b1) |
a2 |
(a3 b2)or(b2 a3) |
a2 |
4 |
a1 a2 |
b1 |
(b2 a3)or(a3 b2) |
b1 |
5 |
(a1 b1)or(b1 a1) |
b2 |
a2 a3 |
b2 |
到這裏整體的算法思路都已經描述完畢,剩下的是全部代碼,僅供參考
class Solution {
public:
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
int m=nums1.size();
int n=nums2.size();
int mid_flag=(m+n)%2;
if(m==n){
if(m==1)
return (nums1[0]+nums2[0])/2.0;
if(m==0)
return 0/1.0;
else if(nums1[0]<nums2[0])
return findMedianSortedArrays(nums2,nums1);
}
if(m<n)
return findMedianSortedArrays(nums2,nums1);
else if(m>1&&n==1){
nums2.push_back(max(nums2[0],nums1[m-1]));
const int insert_ele=min(nums1[0],nums2[0]);
nums1.insert(nums1.begin(),insert_ele);
m++;
n++;
}
if(n==0){
if(m%2==0)
return (nums1[m/2-1]+nums1[m/2])/2.0;
else
return nums1[m/2]/1.0;
}
int start1=0;
int start2=0;
int end1=m-1;
int end2=n-1;
int p1,p2;
while(start2!=end2-1){
p1=(end1-start1)/2;
p2=(end2-start2)/2;
if(nums1[start1+p1]>nums2[start2+p2]){
start2+=p2;
end1-=p2;
}
else if(nums1[start1+p1]<nums2[start2+p2]){
start1+=p2;
end2-=p2;
}
else if(nums1[start1+p1]==nums2[start2+p2]){
if(m==n && mid_flag==1)
return (nums1[start1+p1]+nums2[start2+p2])/2.0;
if((end2-start2)>p2*2){
start2+=p2;
end2-=p2;
}
else{
start1++;
end2--;
}
}
}
int mid=(end1-start1)/2+start1;
if(mid_flag==1){
if(nums2[start2] >= nums1[mid+1])
return nums1[mid+1]/1.0;
else if(nums2[end2] <= nums1[mid-1])
return nums1[mid-1]/1.0;
else if(nums2[start2] <= nums1[mid] && nums2[end2] >= nums1[mid])
return nums1[mid]/1.0;
else if(nums2[start2]>=nums1[mid]&&nums2[start2]<=nums1[mid+1])
return nums2[start2];
else if(nums2[end2]>=nums1[mid-1]&&nums2[end2]<=nums1[mid])
return nums2[end2];
}
else if(mid_flag==0){
if((end1-start1)==1){
if(nums1[mid]>=nums2[start2] && nums1[mid+1]>=nums2[end2])
return (nums1[mid]+nums2[end2])/2.0;
else if(nums1[mid]<=nums2[start2] && nums1[mid+1]<=nums2[end2])
return (nums1[mid+1]+nums2[start2])/2.0;
else if(nums1[mid]>=nums2[start2] && nums1[mid+1]<=nums2[end2])
return (nums1[mid]+nums1[mid+1])/2.0;
else
return (nums2[start2]+nums2[end2])/2.0;
}
if(nums1[mid-1]>=nums2[end2])
return (nums1[mid-1]+nums1[mid])/2.0;
else if(nums1[mid+2]<=nums2[start2])
return (nums1[mid+1]+nums1[mid+2])/2.0;
else if(nums1[mid]>=nums2[start2] && nums1[mid+1]<=nums2[end2])
return (nums1[mid]+nums1[mid+1])/2.0;
else if(nums1[mid]<=nums2[start2] && nums1[mid+1]>=nums2[end2])
return (nums2[start2]+nums2[end2])/2.0;
else if(nums1[mid]>=nums2[start2] && nums1[mid+1]>=nums2[end2])
return (nums1[mid]+nums2[end2])/2.0;
else if(nums1[mid]<=nums2[start2] && nums1[mid+1]<=nums2[end2])
return (nums1[mid+1]+nums2[start2])/2.0;
}
return 0;
}
};
Submission Result: Accepted
Runtime: 0 ms
題目鏈接:https://leetcode.com/problems/median-of-two-sorted-arrays/description/