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/