这个问题其实蕴含了两道题目,一个是合并两个有序序列,一个是查找合并后序列中的中位数(第二个问题,不需要实际的合并操作)
合并有序序列比较简单,以下是代码供简单参考:
void merge_order(list<int>&des,const list<int>&a,const list<int>&b)
{
for(auto i=a.cbegin(),j=b.cbegin();i!=a.cend()||j!=b.cend();){
if(i==a.cend())
des.push_back(*j++);
else if(j==b.cend())
des.push_back(*i++);
else
*i<*j?des.push_back(*i++):des.push_back(*j++);
}
}
中位数,偶数个数是中间两个数的平均值,奇数个数是中间值。
这里有一个巧妙的处理方法:
可以观察到,L=(N-1)/2 R=N/2 当N%2==1时,L==R, 当N%2==0时,L+1==R
所以 中位数== (A[L]+A[R])/2
这个操作了解以后,代码将会简洁很多。
接下来说查找的思路:
1:暴力,即合并以后查找中位数即可
2:二分思想,A的中位数和B的中位数比较大小,如果A_mid>B_mid说明中位数出现在B的右半部分和A的坐半部分。如此递归下去,知道找到中位数,这里不做详细介绍。
3:也是二分思想,进阶为返回合并后序列中第k个都阔以。其实也是2分思想,但是代码易实现。且功能更多。
大致过程:
访问A中第start+k/2个元素(对应下标-1),访问B中第start+k/2个元素(对应下标-1)。
如果A中的元素大,则更新B的start到刚才访问的下一个元素,递归调用,反之亦然。
直到满足递归条件。
double find_median(const vector<int>&a,const vector<int>&b){
int len_a=a.size(),len_b=b.size();
//因为是第k个,k是从1开始的,对应L,R也要+1
int L=(len_a+len_b-1)/2+1;
int R=(len_a+len_b)/2+1;
return (getKth(a,0,b,0,L)+getKth(a,0,b,0,R))/2.0;
}
int getKth(const vector<int>&a,int start1,const vector<int>&b,int start2,int k)
{
/*递归结束条件*/
//如果a到头了,返回b中的第k个。
if(start1>=a.size())return b[start2+k-1];
//如果b到头,返回a中的第k个。
if(start2>=b.size())return a[start1+k-1];
//如果k==1 则返回小的。
if(k==1)return a[start1]<b[start2]?a[start1]:b[start2];
int end1=INT_MAX,end2=INT_MAX;//如果star1+k/2-1越界了,自然视为end2<end1,即在b中往后移动k/2;
if(start1+k/2-1<a.size())end1=a[start1+k/2-1];
if(start2+k/2-1<b.size())end2=b[start2+k/2-1];
if(end1<end2)
return getKth(a,start1+k/2,b,start2,k-k/2);
else
return getKth(a,start1,b,start2+k/2,k-k/2);
}