算法第二章上机实践报告
Posted ljl-gd
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了算法第二章上机实践报告相关的知识,希望对你有一定的参考价值。
1、 实践题目:7-3 两个有序序列的中位数(二分法解法)
2、 问题描述:对于两个等长的非降序序列S1、S2,如何利用二分法的思想求出S1与S2并集的中位数,要求算法的时间复杂度为T(logn)。
3、 算法描述:总体思想:采用分治与递归策略,二分法每次将问题规模减半(约减半),然后对问题进行递归处理,在进行递归的过程中对可能出现特殊的情况,就特殊安排处理。
(1) 解法一:
① 先排除两个两个等长序列的元素个数(奇和偶)的影响,首先对两个序列求中位数,求中位数的方法是如下,此方法为保证偶数序列的分割保持割后对称性,而对奇数序列不产生影响,如此可以保证递归的时候子问题也是两个等长的序列。
② 在两端标志还未出现相等或未出现其他特殊情况时,每次求得两个序列的中位数(如果序列是偶序列,则第二个序列的中位数定为A[(N-1) /2 + 1],即中位数的后一个数),将两个中位数进行比较,若两数相等的话,则可确认为此数为所求中位数。若不相等,则对两个数中较大的数的那一序列进行减掉比这个数大的(即这个数后面)的部分,保留其他;对另一个序列(较小数所在序列)而言,则减掉比它小的(即前面的)部分,保留其他,比较后,对剩下的等长序列进行递归调用。
③ 在进行递归过程中,若还未出现相等中位数,两序列的两端标志将会不断靠近,所以此时对于实例不同的序列,就会可能出现一些特殊情况,所以下面就讲解对于所有可能出现特殊情况的解决办法,第一种情况是当两序列都只剩最后一个时,就对两数进行比较,小的为所求中位数。
④ 第二种情况为两序列都最后只剩两个数而无法再进行递归下去(陷入死循环)时,第一个序列求中位数方法不变,第二个序列变为与第一序列相同的方法求得中位数,在这样的处理下再进行递归一次,所以之后两个共剩下三个数,就分两种情况,第一种为第一个序列剩一个,第二种为第二个序列剩一个,两种情况的解决方案都为:然后把剩一个的那组的数与剩两个那组的较大的数(也就是第二个数)进行比较,取小的那个数,那个数就是我们所求中位数。
⑤ 代码如下:
#include <iostream> using namespace std; int MidSearch(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; } if (a[ma] == b[mb]){ return a[ma]; } else if (a[ma] > b[mb]){ MidSearch(a, b, la, ma, mb, rb); } else { MidSearch(a, b, ma, ra, lb, mb); } } } int a[100005], b[100005]; int main(){ int n; cin >> n; for (int i = 0; i < n; i++) cin >> a[i]; for (int j = 0; j < n; j++) cin >> b[j]; cout << MidSearch(a, b, 0, n - 1, 0, n - 1) << endl; return 0; }
(2) 解法二:
① 如果中位数在数组a中,那么若a[m]<b[n-m-2],此时比a[m]小的数最多只有n-2个,即a[m]不可能为第n小数,偏小更新左界;若a[m]> b [n-m-1],此时比a[m]小的数至少有n个,a[m]不可能为第n小数,偏大更新右界;若a[m]介于b[n-m-2]与b [n-m-1]则a[m]恰好为第n小数。 中位数在数组b中的情况类似。
② 代码如下:
#include <iostream> using namespace std; int a[1005], b[1005]; int mid_num(int a[], int b[], int n){ int l = 0, r = n - 1; int m, tmp; while (l <= r){ m = (l + r) / 2; tmp = (a[m] < b[n - m - 2]? b[n - m - 2]: a[m]); if (tmp > b[n - m -1]) r = m - 1; else if (tmp > a[m + 1]) l = m + 1; else return tmp; } } int main(){ int n; cin >> n; for (int i = 0; i < n; i++){ cin >> a[i]; } for (int j = 0; j < n; j++){ cin >> b[j]; } cout << mid_num(a, b, n) << endl; return 0; }
4、 算法时间及空间复杂度分析(含分析过程)
(1) 时间复杂度:
① 法一:
n = m / 2, T(m) = T(m / 2) + O(1)
因为:T(n) = αT(n / b) + O(nd)
所以:d = 0, logba = d
得:T(m) = O(logm) = O(logn)
② 法二:
n = m / 2, T(m) = T(m / 2) + O(1)
因为:T(n) = αT(n / b) + O(nd)
所以:d = 0, logba = d
得:T(m) = O(logm) = O(logn)
(2) 空间复杂度:
① 法一:O(n)
② 法二:O(n)
5、 心得体会(对本次实践的收获及疑惑的总结)
(1) 三道实践题来说:上课认真听讲,下课基本可以完成。
(2) 组队实践而言:在计算实践复杂度的时候可以校队一下结果,在设计算法、函数的时候可以交流一下方向。
(3) 第三题的难度提升体会:课后练习尽量用到新学的知识完成,做到学以致用;一个看似简单的问题其实多动动脑筋就会变得十分有趣。
6、 参考链接:
https://blog.csdn.net/bensonrachel/article/details/78157103
https://blog.csdn.net/yangliuy/article/details/7194199
以上是关于算法第二章上机实践报告的主要内容,如果未能解决你的问题,请参考以下文章