题目:
给定两个大小分别为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。请你找出并返回这两个正序数组的 中位数 。
算法的时间复杂度应该为 O(log (m+n)) 。
算法:
方法一:排序
朴素思想,时间复杂度nlog(n+m)
func findMedianSortedArrays(nums1 []int, nums2 []int) float64 {
nums1 = append(nums1, nums2...)
n := len(nums1)
sort.Ints(nums1)
if n % 2 == 0 {
return float64(nums1[n/2] + nums1[n/2 - 1]) / float64(2)
}
return float64(nums1[n/2])
}
方法二:遍历 方法一没有利用到两个数组有序的特性,我们设置双指针,找到中位数和中位数前的那个数
func findMedianSortedArrays(nums1 []int, nums2 []int) float64 {
m, n := len(nums1), len(nums2)
left, right := 0, 0
aStart, bStart := 0, 0
// i走了这么多步之后刚好到中位数的位置
for i := 0; i <= (m + n) / 2; i ++ {
left = right
// 什么时候移动搞清楚
if aStart < m && (n <= bStart || nums1[aStart] < nums2[bStart]) {
right = nums1[aStart]
aStart ++
} else {
right = nums2[bStart]
bStart ++
}
}
if (m + n) % 2 == 0 {
return (float64(left + right)) / float64(2)
}
return float64(right)
}
方法三:二分
数组长度为len,则我们需要找第(len + 1) / 2小和第(len + 2) / 2小的数(也可以根据len的奇偶性,找第(len + 1) / 2小和第(len + 2) / 2小,或者第第(len + 1) / 2小小的数),找到之后计算结果即可。
问题转换为构造函数findKth,找到第k小的数。我们每次可以比较nums1[i + k / 2]和[j + k / 2]的大小,排除时间,然后缩小k,修改i,j继续查找。
小技巧:
1.第k小的数从1开始算的话,两个中位数是第(len + 1) / 2和(len + 2) / 2小的数。
2.getKth交换参数位置,让nums1的长度始终小于等于nums2
func findMedianSortedArrays(nums1 []int, nums2 []int) float64 {
m, n := len(nums1), len(nums2)
// 两个中位数是第(m + n + 1) / 2和(m + n + 2) / 2小的数
return float64(getKth(nums1, 0, nums2, 0, (m + n + 1) / 2) + getKth(nums1, 0, nums2, 0, (m + n + 2) / 2)) / float64(2)
}
// k从1开始计数,找到第k小的数,i,j为nums1,nums2开始找最小数的位置
func getKth(nums1 []int, i int, nums2 []int, j int, k int) int {
// 如果nums1的长度大于nums2, 则交换位置进行findKth
if len(nums1) - i > len(nums2) - j {
return getKth(nums2, j, nums1, i, k)
}
// 如果nums1空了,直接返回nums2[j + k - 1]
if i == len(nums1) {
return nums2[j + k - 1]
}
// 如果k == 1了,比较nums1[0]和nums2[0]
if k == 1 {
return min(nums1[i], nums2[j])
}
// nums1,nums2第k小的数的位置,注意越界
si := i + min(len(nums1), k / 2) - 1
sj := j + min(len(nums2), k / 2) - 1
if nums1[si] <= nums2[sj] {
return getKth(nums1, si + 1, nums2, j, k - (si - i + 1))
}
return getKth(nums1, i, nums2, sj + 1, k - (sj - j + 1))
}
func min(a, b int) int {
if a < b {
return a
}
return b
}