第二章算法上机实践

实践题目:
已知有两个等长的非降序序列S1, S2, 设计函数求S1与S2并集的中位数。有序序列A0, A1…AN-1的中位数指A(N-1)/2的值,即第[(N+1)/2]个数(A0为第1个数)。
输入格式说明:输入分3行。第1行给出序列的公共长度N(0<N<=100000),随后每行输入一个序列的信息,即N个非降序排列的整数。数字用空格间隔。
输出格式说明:在一行中输出两个输入序列的并集序列的中位数。

算法描述:由于题目要求时间复杂度为log(n),故不可能采取两者合并在排序的方法。 因为快排的时间复杂度也就O(nlog(n)),不满足题意。但是O(log(n))的复杂度的提示已经足够明显,必须采用二分法。通过比较两个序列之间的中位数,大的序列取小于等于中位数的子序列,小的序列取大于等于中位数的子序列。这其实就是一个二分逼近的思想。通过不断收缩头部和尾部,最后得出结果。
如此,既然不需要合并两个序列和重新排序,自然时间复杂度就立刻下降了。由于是二分逼近的思想,时间复杂度也是理想的O(log(n)),当然算法的实现在提交时,没有考虑周全,出现了部分结果正确,这在后文心得体会详细讲述

核心部分:
int MidSerch(int a[], int b[], int la, int ra, int lb, int rb) {
int ma = (la + ra) / 2;
int mb = (lb + rb+1) / 2;
if (la == ra&&lb != rb) {
if (a[la] > b[rb])return b[rb];
else return a[la];
}
if (lb == rb&&la != ra) {
if (b[lb] > a[ra])return a[ra];
else return b[lb];
}

if (la == ra&&lb == rb) {
if (a[la] > b[lb])return b[lb];
else return a[la];
}
else
{ if (la == ra - 1 && lb == rb - 1)
{
ma = (la + ra) / 2;//第一个序列求中位数方法不变
mb = (lb + rb) / 2;//第二个序列变为与第一序列相同的方法求得中位数
//但由于元素个数只有两个,所以括号内可写为:mb = mb - 1;
}
if (a[ma]==b[mb])
{
return a[ma];
}
else if (a[ma] > b[mb]) {
MidSerch(a, b, la, ma ,mb, rb);
}
else{
MidSerch(a, b, ma, ra, lb, mb);
}

}
}

算法时间空间复杂度分析:时间复杂度O(log(n)),空间复杂度:由于该算法并没有动态申请空间,故空间复杂度为O(1)

心得体会:在递归调用的过程中,不同的例子会导致一些特殊情况。由于当时是依据样例输出写出来的算法,没有考虑特殊情况。导致有两个测试点报错。如果两个序列都剩下一个数的时候,还未出现相等的中位数,那么应该取小的那个数作为中位数。但是最重要的是一开始并没有想到用二分的思想,因为没有掌握二分逼近的思想。当时只是想着将两个序列分别求中位数好像没什么用,但是忽略了分割的步骤,就一心想着合并两个数组,在用c++里的sort函数,但是这不可能达到题目要求的时间复杂度。这次上机实验很好的暴露了学习二分法过程中的思维漏洞,前两题的话还可以按照最基础的二分算法提交成功,但是到了第三题,同样是二分法的应用,但确因为题目变了些花样,就没有想出用二分法解决。这说明了平时做的题目还是太少了,对算法优劣的直觉不够敏感,平时训练的时候必须强迫自己多考虑一下自己提交的算法是否是最优的,有没有更好的解决方法。

posted @ 2019-09-19 17:31  FITZ陈思宇  阅读(252)  评论(0编辑  收藏  举报