给定两个大小分别为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。请你找出并返回这两个正序数组的 中位数 。
算法的时间复杂度应该为 O(log (m+n)) 。
示例 1:
输入: nums1 = [1,3], nums2 = [2]
输出: 2.00000
解释: 合并数组 = [1,2,3] ,中位数 2
示例 2:
输入: nums1 = [1,2], nums2 = [3,4]
输出: 2.50000
解释: 合并数组 = ```
[1,2,3,4] ,中位数 (2 + 3) / 2 = 2.5
解法一
简单粗暴,先将两个数组合并,两个有序数组的合并也是归并排序中的一部分。然后根据奇数,还是偶数,返回中位数。
解法二
看到 log,很明显,我们只有用到二分的方法才能达到。我们不妨用另一种思路,题目是求中位数,其实就是求第 k 小数的一种特殊情况,而求第 k 小数有一种算法。
假设我们要找第 k 小数,我们可以每次循环排除掉 k/2 个数。看下边一个例子。
假设我们要找第 7 小的数字。
我们比较两个数组的第 k/2 个数字,如果 k 是奇数,向下取整。也就是比较第 33 个数字,上边数组中的 44 和下边数组中的 33,如果哪个小,就表明该数组的前 k/2 个数字都不是第 k 小数字,所以可以排除。也就是 11,22,33 这三个数字不可能是第 77 小的数字,我们可以把它排除掉。将 13491349 和 4567891045678910 两个数组作为新的数组进行比较。
更一般的情况 A[1] ,A[2] ,A[3],A[k/2] ... ,B[1],B[2],B[3],B[k/2] ... ,如果 A[k/2]<B[k/2] ,那么A[1],A[2],A[3],A[k/2]都不可能是第 k 小的数字。
证明:A 数组中比 A[k/2] 小的数有 k/2-1 个,B 数组中,B[k/2] 比 A[k/2] 大,假设 B[k/2] 前边的数字都比 A[k/2] 小,也只有 k/2-1 个,所以比 A[k/2] 小的数字最多有 k/2-1+k/2-1=k-2个,所以 A[k/2] 最多是第 k-1 小的数。而比 A[k/2] 小的数更不可能是第 k 小的数了,所以可以把它们排除。
所以我们采用递归的思路,为了防止数组长度小于 k/2,所以每次比较 min(k/2,len(数组) 对应的数字,把小的那个对应的数组的数字排除,将两个新数组进入递归,并且 k 要减去排除的数字的个数。递归出口就是当 k=1 或者其中一个数字长度是 0 了。
func findMedianSortedArrays(nums1 []int, nums2 []int) float64 {
totalLength := len(nums1) + len(nums2)
var ret float64
// 奇数
if totalLength&1 == 1 {
ret = float64(getKthMin(nums1, 0, len(nums1)-1, nums2, 0, len(nums2)-1, totalLength/2+1))
} else {
left := getKthMin(nums1, 0, len(nums1)-1, nums2, 0, len(nums2)-1, totalLength/2)
right := getKthMin(nums1, 0, len(nums1)-1, nums2, 0, len(nums2)-1, totalLength/2+1)
ret = float64(left+right) / 2.0
}
return ret
}
func getKthMin(nums1 []int, start1, end1 int, nums2 []int, start2, end2 int, k int) int {
len1 := end1 - start1 + 1
len2 := end2 - start2 + 1
if len1 > len2 {
return getKthMin(nums2, start2, end2, nums1, start1, end1, k)
}
if len1 == 0 {
return nums2[start2+k-1]
}
if k == 1 {
return min(nums1[start1], nums2[start2])
}
half1 := start1 + min(len1, k/2) - 1
half2 := start2 + min(len2, k/2) - 1
if nums1[half1] < nums2[half2] {
return getKthMin(nums1, half1+1, end1, nums2, start2, end2, k-min(len1, k/2))
} else {
return getKthMin(nums1, start1, end1, nums2, half2+1, end2, k-min(len2, k/2))
}
}
func min(a, b int) int {
if a < b {
return a
}
return b
}