实践题目 7-3 两个有序序列的中位数
问题描述
已知有两个等长的非降序序列S1, S2, 设计函数求S1与S2并集的中位数。有序序列,的中位数指A(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的中位数,(中位数位置 =(Left + Right)/2),
如果A数组中位数小于B数组中位数递归查找A中间到右边的中位数和数组B左边到中间的中位数,
如果A数组中位数大于B数组中位数,则递归查找A数组左边到中间的中位数和B数组中间到左边的中位数,
边界条件是当数组A和数组B元素个数都为2时无法再递归查找中位数时,直接判断数组A和数组B4个元素的中位数,
判断方法为
若A第一个元素小于B第一个元素,那么A第一个元素不可能是中位数(因为A第二个元素B第一第二个元素都比A第一个元素大,A第一个元素就一定是最小的),
接着判断A第二元素和B第一个元素,
若A第二个元素比B第一个元素小,那么A第二元素就为中位数(说明A第二元素大于A第一元素同时必定小于B第一个和第二个元素,该例子中4个数中第二大的为中位数),
若A第二元素比B第一个元素大,那么B第一个元素就为中位数(说明B第一个元素小于B第二个元素和A第二个元素,同时B第一个元素又大于A第一个元素,则B第一个元素为中位数)。
若A第一元素大于B第一个元素,接着判断推理同上类似
边界判断代码实现:
if (aRight - aLeft == 1 || bRight - bLeft == 1) { if (a[aLeft] < b[bLeft]) return a[aRight] < b[bLeft] ? a[aRight] : b[bLeft]; else return b[bRight] < a[aLeft] ? b[bRight] : a[aLeft]; }
在递归查找中位数时调试出现问题,若数组AB元素为奇数时,那么数组AB中间切分时不会出现个数不一样的情况,但若AB数组元素个数为偶数时如为4时,切分数组时会出现个数分别为2和3的情况,
为了避免这种情况发生,就要舍掉一个元素或者多加一个元素,若A数组中位数小于B数组中位数,二分一半时A会分到 N/2+1元素(N为偶数),而B数组分到N/2个元素(N为偶数),分B数组时多加一个元素就可以让两个数组个数一致,
让B数组分到BRight到Bmid+1就可以了;若A数组中位数大于B数组中位数,则让A数组从ALeft分到Amid+1即可。
递归代码实现:
if (a[medianA] == b[medianB]) return a[medianA]; else if (a[medianA] < b[medianB]) { if ((aRight - aLeft + 1) % 2 == 1) return biSearchMedian(a, medianA, aRight, b, bLeft, medianB); else return biSearchMedian(a, medianA, aRight, // medianA + 1? - 1 ? b, bLeft, medianB + 1); } else { if ((bRight - bLeft + 1) % 2 == 1) return biSearchMedian(a, aLeft, medianA, b, medianB, bRight); else return biSearchMedian(a, aLeft, medianA + 1, b, medianB, bRight); } }
完整函数实现:
int biSearchMedian(int *a, int aLeft, int aRight, int *b, int bLeft, int bRight) { int medianA = (aRight + aLeft) / 2; // (aRight - aLeft + 2) / 2 (x) 第floor((N+1)/2)个数! (aRight - aLeft + 1) / 2(x) 如果这样算下标,第一个例子第二次在函数时madianA = medianB = 1 int medianB = (bRight + bLeft) / 2; if (aRight - aLeft == 1 || bRight - bLeft == 1) { if (a[aLeft] < b[bLeft]) return a[aRight] < b[bLeft] ? a[aRight] : b[bLeft]; else return b[bRight] < a[aLeft] ? b[bRight] : a[aLeft]; } else { if (a[medianA] == b[medianB]) return a[medianA]; else if (a[medianA] < b[medianB]) { if ((aRight - aLeft + 1) % 2 == 1) return biSearchMedian(a, medianA, aRight, b, bLeft, medianB); else return biSearchMedian(a, medianA, aRight, // medianA + 1? - 1 ? b, bLeft, medianB + 1); } else { if ((bRight - bLeft + 1) % 2 == 1) return biSearchMedian(a, aLeft, medianA, b, medianB, bRight); else return biSearchMedian(a, aLeft, medianA + 1, b, medianB, bRight); } } }
算法时间及空间复杂度分析
时间复杂度为Ologn,因为用了二分搜索的方法,范围从一开始n逐步缩减到n/2, n/4... 所以是Olog2n的时间复杂度
空间复杂度为O1,因为没有用到辅助的空间,所以空间复杂度为O1
心得体会
一开始用的是归并的思想,因为已经归好了直接并就行了,但是这样的时间复杂度是On,空间复杂度为On,因为题目要求时间复杂度为OlogN,就只能采取二分搜索了,二分搜索是一种比较快的算法了。