算法第二章上机实践报告

一、实践题目

7-3 两个有序序列的中位数 (20 分)

已知有两个等长的非降序序列S1, S2, 设计函数求S1与S2并集的中位数。有序序列A0​​,A1​​,,AN1​​的中位数指A(N1)/2​​的值,即第(N+1)/2⌋个数(A0​​为第1个数)。

输入格式:

输入分三行。第一行给出序列的公共长度N(0<N≤100000),随后每行输入一个序列的信息,即N个非降序排列的整数。数字用空格间隔。

输出格式:

在一行中输出两个输入序列的并集序列的中位数。

输入样例1:

  5
  1 3 5 7 9
  2 3 4 5 6

输出样例1:

  4

输入样例2:

  6
  -100 -10 1 1 1 1
  -50 0 2 3 4 5

输出样例2:

  1

二、问题理解

给出两个非降序序列,题目要求求出两个序列并集的中位数。并集是指给定两个集合A,B,把他们所有的元素合并在一起组成的集合。

三、算法描述

然而这里的并集是不去重的。
很容易的,最先想到的就是先把两个序列合并,然后直接利用下标 (a[n-1]+a[n])/2 求得,这种方法的最优时间复杂度是利用归并排序的O(N)达到。
但是,还有更快的方法,那就是直接利用序列式有序的这个前提,利用两个序列的中位数查找。
首先,很容易知道的。如果 a[mid]>b[mid] ,那么所求的中位数一定位于 b 序列的右边与 a 序列的左边;反之所求中位数一定在 a 序列的右边与 b 序列的左边。
利用这个想法,不断分割序列,理论上,最后会剩下两个数,这时 把两个数相加除以二即为中位数。
但是还有一个问题是,我们的想法是有漏洞的。
即我们的 mid = (left+right)/2; 
就拿样例2来说,根据我们的算法与求 mid 的式子:
 
-100 -10 1 1 1 1
-50 0 2 3 4 5

 

 

 

- - 1 1 1 1
-50 0 2 - - -
 
 
 
 
过程中就有可能使两个序列的长度不再相等,从而导致最终的结果出现错误。解决的方法就是在求 mid 的时候加一再除以二,从而保证两条序列长度一直相等。
 

四、算法时间及空间复杂度分析

每次分别求中位数的时间复杂度为O(1),划分区间的时间复杂度O(1),根据计算得时间复杂度O(logN)

此算法只需要常数个额外变量,空间复杂度为O(1)

五、代码实现

#include <iostream>
#include <algorithm>
using namespace std;
int n;
int a[1000005], b[1000005];

int solve(int a[], int b[], int aleft, int aright, int bleft, int bright){
    int ans;
    if((aleft+1 == aright || aleft == aright) && (bleft+1 == bright || bleft == bright)){
        int num[4] = {a[aleft], a[aright], b[bleft], b[bright]};
        sort(num, num+4);
        int mid = (num[1] + num[2]) / 2;
        //cout << ans << endl;
        //cout <<num[0]<<"---"<<num[1]<<"---"<<num[2]<<"---"<<num[3]<<endl;
        return mid;
    }
    //cout << "asfasdasda" << endl;
    int amid = (aleft + aright+1) >> 1;
    int bmid = (bleft + bright) >> 1;
    if(a[amid] > b[bmid])
        ans = solve(a, b, aleft, amid, bmid, bright);
    else
        ans = solve(a, b, amid, aright, bleft, bmid);
    return ans;
}

int main(){
    cin >> n;
    for(int i = 0; i < n ; i++){
        cin >> a[i];
    }
    for(int i = 0; i < n ; i++){
        cin >> b[i];
    }
    int ans = solve(a, b, 0, n-1, 0, n-1);
    cout << ans << endl;
    return 0;
}

六、心得体会 

多跟别人交流,很多时候思维黑洞会限制了发挥。

多动手模拟。

posted @ 2018-10-18 21:15  请你吃糖呀  阅读(146)  评论(0编辑  收藏  举报