寻找两个正序数组的中位数
题目描述:
给定两个大小分别为 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
提示:
- nums1.length == m,nums2.length == n
- 0 <= m <= 1000,0 <= n <= 1000,1 <= m + n <= 2000
- -106 <= nums1[i], nums2[i] <= 106
解法
- 这道题很多地方都有看到,但一直没有冷静下来解决掉,主要还是中位数需要考虑奇偶的问题,感觉处理起来很麻烦;另外,把复杂度降到O(log(m + n)),肯定是要二分,所以始终给我一种感觉,就是需要分别找nums1和nums2的中点,然后进行一些判断,抛去一半长度的搜索区间。这种思路导致我不能成功做出来;做出不来就会看题解,一下就会了,又担心直接写记不住,没效果,准备隔几天再写;然后几天后又回到起始的思路…
- 言归正传,如果把寻找中位数转为寻找第 k 小,那么这道题其实就不难了。我们仍然使用前面的记法,记nums1的长度为m,nums2的长度为n,如果m + n为奇数,那么找到第(m + n + 1)//2小的元素即可;如果为偶数,那么找到第(m + n)//2和(m + n)//2 + 1小的元素,再取平均即可。
- 可以看出,我们只需写一个新方法——取两个数组的第 k 小。因为两个数组都是有序的,所以有以下思路:
第一步我们初始化两个数组的搜索左边界left1, left2都是0,然后可以比较nums1[left1 + k//2 - 1]和nums2[left2 + k//2 - 1]的大小,如果前者大,那就说明目标元素肯定不在nums2的[left2: left2 + k//2]中;反之就不在nums1的[left1: left1 + k//2]中。
第二步我们更新左边界,并且更新 k 值。不妨假设第一步的结论是,目标元素不在nums2的[left2: left2 + k//2]中,那么就可以把nums2的搜索左边界设置为left2 + k//2,并且把 k 更新为k - k//2。
第三步就是在新的搜索空间中,寻找第 k 小的元素。
需要注意的是,left1 + k//2 - 1或者left2 + k//2 - 1可能是越界的,需要和数组长度取min。整个过程是不断循环的,所以可以写成while循环或者递归的形式,终止条件是:如果k == 1,那么就取两个数组左边界中的最小值;如果某一数组的左边界已经越界,那就直接取另一数组的第 k 小即可。
代码
class Solution:
def findMedianSortedArrays(self, nums1: List[int], nums2: List[int]) -> float:
total_len = len(nums1) + len(nums2)
if total_len % 2:
return self.func(nums1, nums2, 0, 0, (total_len + 1) // 2)
return (self.func(nums1, nums2, 0, 0, total_len//2) + self.func(nums1, nums2, 0, 0,total_len//2 + 1)) / 2
def func(self, nums1, nums2, left1, left2, k):
m, n = len(nums1), len(nums2)
if m <= left1: # 越界
return nums2[left2 + k - 1]
if n <= left2: # 越界
return nums1[left1 + k - 1]
if k == 1: # 相当于取两数组的最小值
return min(nums1[left1], nums2[left2])
pos1, pos2 = min(m - 1, left1 + k//2 - 1), min(n - 1, left2 + k//2 - 1) # 设置两个数组的比较元素
if nums1[pos1] > nums2[pos2]: # 更新 left2 和 k
k -= pos2 - left2 + 1
left2 = pos2 + 1
else: # 更新 left1 和 k
k -= pos1 - left1 + 1
left1 = pos1 + 1
return self.func(nums1, nums2, left1, left2, k)
测试结果
执行用时:52 ms, 在所有 Python3 提交中击败了72.87% 的用户
内存消耗:15 MB, 在所有 Python3 提交中击败了60.63% 的用户
说明
算法题来源:力扣(LeetCode)