题目出处:https://leetcode.com/problems/median-of-two-sorted-arrays/
两个已经排好序的数组:nums1 和 nums2, 他们的长度分别是m和n. 求这两个数组的中位数。 要求时间复杂度是O(log(m+n)).
数组的中位数: 就是一排数据从小到大排列后,中间的那个数。 比如 [1, 2, 3, 4, 5], 那中位数就是3. 而 [1, 2, 3, 4, 5,6]的中位数就是 3, 4. 题目要求返回(3+4)/2 =3.5
分析:
1. 如果是一个数组,直接求解好求,直接长度length/2 可直接索引到中位数的位置, 这样可直接求解。
2. 不能合并数组,合并数组的时间复杂度为O(m+n). 与题目要求的O(log(m+n))符.
3. 不考虑复杂度时,最简单的方法是从左到右逐个移动,可很简单的实现快速找到中位数的位置,时间复杂度O((m+n)/2)., 后面会展示算法(算法1)
4. 时间复杂度O(log(m+n))的算法,让我想到二分查询算法, 细想下这问题与方法很像,都是查询,二分查询查询的某个值,而本算法查询的是中位数的位置。 所以决定模仿二分查询来解决这个问题。
算法1 顺序查询法
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
int length = nums1.size() + nums2.size();
if(length == 0) return 0;
//计算中间数开始的位置索引, 代表需要移动的步数据
int target = length/2;
if(length % 2 == 0){
target-=1;
}
int index1 = 0, index2 = 0;
int lastMove = 0;
while(index1 < nums1.size() && index2 < nums2.size()){
if(index1 + index2 > target)break;
if(nums1[index1] >= nums2[index2])
lastMove = nums2[index2++];
else
lastMove = nums1[index1++];
}
//中位数在某个列表中
if(index1 + index2 <= target){
int move = target-index1 - index2; //还要移动的步数
if(index1 < nums1.size()){
index1 += move;
lastMove = nums1[index1++];
}else{
index2 += move;
lastMove = nums2[index2++];
}
}
if(length % 2 == 1) return lastMove;
int second = 0;
if(index1 >= nums1.size()) second = nums2[index2];
else if(index2 >= nums2.size()) second = nums1[index1];
else second = nums1[index1] > nums2[index2] ? nums2[index2] : nums1[index1];
return (lastMove + second)/2.0;
}
算法2 二分查询法
算法简述:
1. 先计算需要移动的数:less
2. 两个数组同时移动 less/2个数(如果不够, 需要做相应调整)的值分别是data1, data2
3. data1 == data2, 则找到位置
4. data1 != data2, 则移动相应的步少, 同步减少 less的值。
循环结束标志:
less==0, 说明已经移动到中位数位置
有某个数组已经移动到最末,即begin==end. 则在单数组中移位less,就可得到中位数据位置
代码:
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2){
if(nums1.size() == 0 && nums2.size() == 0) return 0;
int length = nums1.size() + nums2.size();
int less = length>>1;
if(length%2 == 0){less -= 1;}
int begin1 = 0, end1 = nums1.size();
int begin2 = 0, end2 = nums2.size();
while(1){
//检查是否已经结束
if(less == 0 || begin1 == end1 || begin2 == end2){
if(begin1 == end1)
return (length % 2 != 0) ? nums2[begin2+less] : (nums2[begin2+less] + nums2[begin2+less+1])/2.0;
if(begin2 == end2)
return (length % 2 != 0) ? nums1[begin1+less] : (nums1[begin1+less] + nums1[begin1+less+1])/2.0;
//获取第一个数字
int first = 0;
if(nums1[begin1] > nums2[begin2]) first = nums2[begin2++];
else first = nums1[begin1++];
//
if(length % 2 != 0) return first;
int second = 0;
if(begin1 == end1) second = nums2[begin2];
else if(begin2 == end2) second = nums1[begin1];
else second = nums2[begin2] > nums1[begin1] ? nums1[begin1]: nums2[begin2];
return (first + second)/2.0;
}
int mid1 = begin1 + less/2;
int mid2 = begin2 + less/2;
if(mid1 >= end1){
mid1 = end1 - 1;
mid2 = begin2 + less - end1 + begin1;
}
else if(mid2 >= end2){
mid2 = end2 - 1;
mid1 = begin1 + less - end2 + begin2;
}
if(nums1[mid1] == nums2[mid2]){
less -= mid2 - begin2 + mid1 - begin1;
begin2 = mid2;
begin1 = mid1;
if(less > 0){less--; begin1++;}
}
else if(nums1[mid1] > nums2[mid2]){
if(mid2 > begin2){
less -= mid2 - begin2;
begin2 = mid2;
}
else{less-=1; begin2+=1;}
}
else{
if(mid1 > begin1){
less -= mid1 - begin1;
begin1 = mid1;
}
else{less-=1; begin1+=1;}
}
}
return 0;
}