问题:设X[0:n-1]和Y[0:n-1]为两个数组,每个数组中含有n个已排好序的数。试设计一个O(logn)时间算法,找出X和Y的2n个数的中位数
思路:找出将大问题分割成较小规模的相同问题的切割点,并递归定义大问题与子问题之间的关系。
简单来说,就是比较两个区间的中位数,如果第一个区间的中位数比第二个大,那么就把第一个区间的范围缩小至它的前半段,把第二个区间缩小至它的后半段,然后重复上述过程
具体些,对于两个数组x,y,我们可以从他们中分别选取出一个中位数,并将两个数组的左右边界称之为xLeft,xRight,yLeft,yRight。对比两个中位数,如果X数组中的中位数大于Y数组中的中位数,且X数组中的元素个数为偶数个,则X数组被切割为X[xLeft,x+1],Y被切割为Y[y,yRight],如果X数组的元素个数不为偶数个的话,则直接将X切割为X[xLeft,x]。如果X数组的中位数小于Y数组的中位数,取值情况刚好相反。
递归关系:根据上面所述,对于原问题X[xLeft , xRight], Y[yLeft, yRight]。假设切割后的子问题为X[xLeft, x+1],Y[y,yRight]。则求解X[xLeft , xRight], Y[yLeft, yRight]问题的中位数,归结于求解子问题X[aLeft, x+1],Y[y,yRight]的中位数。
递归结束条件:当切割后得到的子问题的两个数组的长度都为2位时,整个递归结束。
Input:
第一行: n,为x和y数组的元素个数
第二行: x数组的n个数,用空格分隔
第三行: y数组的n个数,用空格分隔
Output:
中位数两个,用空格分隔
代码(C++):
<span style="font-family:Tahoma;">#include <iostream>
using namespace std;
int main(int argc, char** argv) {
// 获取数组长度
int n;
cin>>n;
// 分配两个数组(两个数组的左右端点的坐标和中间坐标)
int xleft = 0, yleft = 0, len = n, xright = n - 1, yright = n - 1, xmid, ymid;
int x[n], y[n];
// 获取两个数组的元素
for(int i = 0; i < n; i++){
int j;
cin>>j;
x[i] = j;
}
for(int i = 0; i < n; i++){
int j;
cin>>j;
y[i] = j;
}
// 如果数组长度为1,则其元素都为中位数
if(n == 1)
cout<<x[0]<<" "<<y[0];
else{
// 迭代循环
while(true){
// 如果两个数组都只剩下两个元素,则中位数一定在其中
if( (xright - xleft) == 1 && (yright - yleft) == 1){
// 输出两个值
cout<<((x[xleft]>=y[yleft])?x[xleft]:y[yleft])<<" "<<((x[xright]<=y[yright])?x[xright]:y[yright]);
break;
}
else{
// 求解各个数组的中值
xmid = (int)((xleft + xright)/2);
ymid = (int)((yleft + yright)/2);
// 如果x中值小于y中值
if(x[xmid] < y[ymid]){
// 如果y中现存的数列是偶数个,右边值加一
if((yleft + yright + 1) % 2 == 0) {
xleft = xmid;
yright = ymid + 1;
}
else {
xleft = xmid;
yright = ymid;
}
}
// 如果B中值小于x中值
else{
// 如果x中现存的数列是偶数个,右边值加一
if((xleft + xright + 1) % 2 == 0) {
xright = xmid + 1;
yleft = ymid;
}
else {
xright = xmid;
yleft = ymid;
}
}
}
}
}
return 0;
}</span>