题目概述
There are two sorted arrays nums1 and nums2 of size m and n respectively.
Find the median of the two sorted arrays. The overall run time complexity should be O(log (m+n)).
解题报告
这道题在处理题目的模型和实现上都稍微有点难度。
这里我们先讨论普通的二分查找,就是说我们要确定一个数字的具体排位的时候,比如说第N大的数字。可以维护一个优先队列,期望时间复杂度就是O(nlogm)。《编程珠玑》里面提到的一种更好的方法:借助快排的思想,可以将复杂度优化到O(n)。
题意解析
不过,上面是无序的情况,如果是有序的数字呢?显然,可以O(1)找到对应排位的数字,这个也就是我们要突破的重点。两个有序的序列,找到其中的中位数字。首先我们怎么理解可以帮助我们呢?中位数是分割一个序列为两个部分,这两个的部分的数字个数相等。中位数是说排在(n/2)或(n/2, n/2 + 1)的数字。
所以这道题目就是要我们找到一个中位数,使得两个数列合在一起可以被分成数目相等的两个部分。这样我们重新定义了问题,使得理解更加具体。
解决步骤
- a[0], a[1], a[2] … a[i - 1] median a[i + 1], a[i + 2] … a[n]
b[0], b[1], b[2] … b[j - 1] median b[j + 1], b[j + 2] … b[m] - a[i - 1] < b[j] (a)
b[j - 1] < a[i] (b) - a[i] == b[j] // 找到了median,中位数不一定同时存在于两个数组中
如何找到相应的i与j呢?切入口在于我们二分的对象是什么。
这里有个隐藏的条件:i + j = n - i + m - j(中位数两边的数字个数相等),也就是说i确定了,j也就可以确定了,这个结论很好,我们只需要枚举其中一个就能解决问题。通常我们需要枚举长度短的序列,这样边界就不会溢出。
重要结论1:i = (m + n + 1) / 2 - j。
这样我们遍历 i 亦或 j 中的一个,就能确定另外一个的值了。
我们来搜索 i :
- 如果 a[i] == b[j],那么我们就找到了最终的结果。
- 如果 a[i] > b[j] 说明我们要调整 i,使得a[i]减小,b[j]则相对增大。
- 对于2,反之亦然。
不过,这里要考虑一个情况,要找到的值未必都在两个数列中。比如:[1,2,3,4,5] 和 [6]。更特殊:[1,2], []。
这个也是问题的第二个关键点。
首先,假设较短的数列已经触及边界,同时不满足约束条件(a), (b),那么就意味着我们已经找到答案了。原因在于每次的划分都是平衡划分,一个数列触及了边界,答案也就一定在另外一个数列当中,而答案在另一个数列当中的时候就是一个普通的二分查找。显然较短的数列会更快碰到边界。
结论2:我们每次搜索需要检查较短数列的边界在合理范围之内。
搜索结束后,正确答案在哪里?
如果两个数列的长度总和为奇数,那么搜索位置较大的那个即为所求。否则要计算两个位置较大的和后继位置较小的数字的均值:
max(a[i-1], b[j-1]), odd
(max(a[i-1], b[j-1]) + min(a[i], b[j])) / 2, even
边界条件的处理,当 i == 0 或 i == length of list 的时候,这个时候搜索也结束了。对于 j 亦然。
程序清单
1 | public class Solution { |