算法第二章上机实践报告
一.实践题目
求两个有序序列的中位数---已知有两个等长的非降序序列S1, S2, 设计函数求S1与S2并集的中位数。有序序列,的中位数指A(N−1)/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
二.问题描述
两个等长的非降序序列,求两个序列并集的中位数,要求时间复杂度为O(log n)
三.算法描述
由于题目要求的时间复杂度为O(log n),较容易想到应该运用二分法。
基本思想:先找到每个序列的中位数,比较两个中位数的大小,若相等,则该数为两序列求并集后的中位数。若不相等,将中位数较大的序列的左半部分和中位数较小序列的右半部分看作新的两个序列(应注意奇数和偶数的情况不同),重复上述步骤,直至每个序列剩余一个数,比较两个数的大小,较小的即为两序列求并集后的中位数。
N为奇数:
|
1 |
3 |
5 |
7 |
9 |
|
2 |
3 |
4 |
5 |
6 |
在此例子中,中位数5大于4,可知4左边的2, 3和5右边的7, 9不可能是中位数,故取 1, 3 ,5 和 4, 5, 6为两个新的序列。
| 1 | 3 | 5 |
| 4 | 5 | 6 |
| 3 | 5 |
| 4 | 5 |
4 和 5 比较,中位数为4
N为偶数:
| 1 | 4 | 5 | 7 |
| 2 | 3 | 4 | 5 |
中位数分别为4, 3。4 大于3,取1, 4 和4, 5(取的个数与N奇数时不同,注意mid + 1 )。
代码:
1 // 求两个有序数组并集的中位数,时间复杂度为O(log N) 2 3 #include <iostream> 4 using namespace std; 5 int n; 6 void median(int s1[], int s2[], int left1, int right1, int left2, int right2) { 7 8 if (right1 < left1) 9 return; 10 else if (right1 > left1) { 11 int mid1 = (left1 + right1) / 2; 12 int mid2 = (left2 + right2) / 2; 13 if (s1[mid1] == s2[mid2]) { 14 cout << s1[mid1]; 15 return; 16 } 17 if ((right1 - left1 + 1) % 2 == 0) { //个数为偶数 18 if (s1[mid1] > s2[mid2]) 19 return median(s1, s2, left1, mid1, mid2 + 1, right2); 20 else if (s1[mid1] < s2[mid2]) 21 return median(s1, s2, mid1 + 1, right1, left2, mid2); 22 } 23 else { //个数为奇数 24 if (s1[mid1] > s2[mid2]) 25 return median(s1, s2, left1, mid1, mid2, right2); 26 else 27 return median(s1, s2, mid1, right1, left2, mid2); 28 29 } 30 } 31 32 if (s1[left1] < s2[left2]) //最终剩两个数进行比较,输出较小的 33 cout << s1[left1]; 34 else 35 cout << s2[left2]; 36 } 37 int main() { 38 int s1[100000], s2[100000]; 39 cin >> n; 40 for (int i = 0; i < n; i++) { 41 cin >> s1[i]; 42 } 43 for (int i = 0; i < n; i++) { 44 cin >> s2[i]; 45 } 46 median(s1, s2, 0, n - 1, 0, n - 1);47 return 0; 48 }
四.算法时间及空间复杂度分析
每比较一次中位数,序列的大小约减为一半,消耗时间为O(log n),其他如比较等所需时间都为常数时间,故时间复杂度为O(log n)。
算法执行时所需的辅助空间与 问题规模 n 无关,故空间复杂度为O(1)。
五.心得体会
该题需要考虑的点较多,像N为奇数或偶数时情况的不同,运用分治法时分割子问题的规模和方法,递归终止的条件,时间复杂度等,都是值得好好思考的问题。最开始疑惑的是找到每个序列的中位数之后应该怎么分治才能得到最终结果,然后通过自己写了几个例子之后慢慢找到规律。过程中问题还是挺多的,不过最大的问题是没看清题目,没有注意到该题目N为偶数时中位数不是求中间两个数的平均值,而是取较小的那个数,直到我提交了代码才发现我看错了,所以最大的体会就是以后要好好审题。
浙公网安备 33010602011771号