[分析] 暴力法是简单的,合并后返回中位数。优化的方法是参考 http://blog.csdn.net/yutianzuijin/article/details/11499917介绍的解法,awesome! 思路的关键点是将原问题转为寻找两排序数组中第 k 小的数,中位数即为第 (n + m) / 2 小的数, n和m分别为输入中A 和 B 两个数组的长度。
findKth 思路:假设A 和 B长度均不小于 k/2, 比较A[k/2 - 1] 和 B[k/2 - 1]:
(1) A[k/2 - 1] < B[k/2 - 1]
则A的前 k/2个元素是AB合并后的前 k - 1 小的元素中,也即 A[k/2 - 1]不可能是要找的第k个数。反证法证明推断:假设A[k/2 - 1] 在AB合并后出现在第k个元素或以后位置,不妨设就是第k个元素,则 B[k/2 - 1]至少为第 k+1个元素。因为 小于 A[k/2 - 1]至多有 (k/2 - 1) * 2 = k -2 个元素,这与A[k/2 - 1]是合并后第 k个数矛盾,得证。
(2) A[k/2 - 1] > B[k/2 - 1]
分析同(1)。
(3) A[k/2 - 1] = B[k/2 - 1]
则A[k/2 - 1] 就是合并后第 k 个数。
findKth 每次缩小一半规模,时间复杂度是 logk, 因此找寻中位数时间复杂度为 log((m + n) / 2)。
public class Solution { public double findMedianSortedArrays(int[] nums1, int[] nums2) { int total = nums1.length + nums2.length; if ((total & 1) == 0) return (findKth(nums1, nums2, 0, 0, total / 2) + findKth(nums1, nums2, 0, 0, total / 2 + 1)) * 1.0 / 2; else return findKth(nums1, nums2, 0, 0, total / 2 + 1); } public int findKth(int[] nums1, int[] nums2, int off1, int off2, int k) { if (nums1 == null || nums1.length == off1) return nums2[off2 + k - 1]; if (nums2 == null || nums2.length == off2) return nums1[off1 + k - 1]; if (k == 1) return Math.min(nums1[off1], nums2[off2]); int m = nums1.length - off1; int n = nums2.length - off2; if (m > n) return findKth(nums2, nums1, off2, off1, k); int part1 = Math.min(k / 2, m); int part2 = k - part1; if (nums1[off1 + part1 - 1] < nums2[off2 + part2 - 1]) return findKth(nums1, nums2, off1 + part1, off2, k - part1); else if (nums1[off1 + part1 - 1] > nums2[off2 + part2 - 1]) return findKth(nums1, nums2, off1, off2 + part2, k - part2); else return nums1[off1 + part1 - 1]; } }