力扣困难题 4. 寻找两个正序数组的中位数

106 阅读2分钟

题目链接

该题是在两个顺序数组中找到中点

输入: nums1 = [1,3], nums2 = [2]
输出: 2.00000
解释: 合并数组 = [1,2,3] ,中位数 2

方案一

合并然后排序,然后输出中点即可

func findMedianSortedArrays(nums1 []int, nums2 []int) float64 {
	nums1 = append(nums1, nums2...)
	sort.Ints(nums1)
	if len(nums1)%2 != 0 {
		return float64(nums1[len(nums1)/2])
	} else {
		return (float64(nums1[len(nums1)/2]) + float64(nums1[len(nums1)/2-1])) / 2
	}
}

时间复杂度是o(n)空间复杂度o(n) 这个题是需要o(logn)的复杂度的

方案二

采用二分,这里的二分和一般的二分不太相同,因为这两个数组都是有序的我们可以在两个数组中取k/2的节点观察大小,小的一方可以将其前面的0 ~ k/2排除掉,这样达到二分的目的,如果两个数组大小相同每一次都可以筛掉1/4的大小

image.png 如图中示例数组b中的k/2节点的地方比数组a中的大因此数组a中的前两个可以排除掉

image.png 之后又排除掉三

image.png 之后数组a就走到头了因此直接返回数组b中对应的值即可因此中位数是10

在编写代码的时候可以定义一个find函数封装一下,默认数组a长度小于数组b这样方便操作,同时数组a的长度可能小于k/2这个时候数组a最多筛掉len(a)所以就筛len(a)即可

func findMedianSortedArrays(nums1 []int, nums2 []int) float64 {
   // 获取总长度
   total := len(nums1) + len(nums2)
   if total%2 == 0 {
      // 获取中间的两个值
      left, right := find(nums1, 0, nums2, 0, total/2), find(nums1, 0, nums2, 0, total/2+1)
      return (float64(left) + float64(right)) / 2
   } else {
      // 获取中间值
      return float64(find(nums1, 0, nums2, 0, total/2+1))
   }
}

func find(nums1 []int, i int, nums2 []int, j int, k int) int {
   // 如果num1剩余的大小更多我们就交换下
   if len(nums1)-i > len(nums2)-j {
      return find(nums2, j, nums1, i, k)
   }
   // 如果num1用完了直接输出num2中对应的数即可
   if i == len(nums1) {
      return nums2[j+k-1]
   }
   // 如果k==1表示在num1和num2中输出最小的即可
   if k == 1 {
      return min(nums1[i], nums2[j])
   }
   // 获取中间值并比较,将小的一方删除掉部分
   si, sj := min(len(nums1), i+k/2), j+k-k/2
   if nums1[si-1] > nums2[sj-1] {
      return find(nums1, i, nums2, sj, k-(sj-j))
   } else {
      return find(nums1, si, nums2, j, k-(si-i))
   }
}
func min(a, b int) int {
   if a < b {
      return a
   } else {
      return b
   }
}