LeetCode-4. 寻找两个正序数组的中位数【数组 二分查找 分治】
题目描述:
给定两个大小分别为 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
-10^6^ <= nums1[i], nums2[i] <= 10^6^
解题思路一:二分查找 分治。
- 找第k小数
对于两个有序数组,我们要找第k小的数
由于时间复制度要求是log,所以自然的想法就是对两个数组每次切一半。
好,假设我们取两个数组k/2位置上的数(这里暂时不考虑上溢)
如果nums1[k/2]>=nums2[k/2],这意味着:
nums2数组的左半边都不需要考虑了,因为肯定会比第k小的数要来得小。
所以我们可以切掉nums2数组的一半,如此递归,每次都能切走一半
自然能达到O(log(m+n))复杂度的要求了。
在具体的代码实现中,为了方便处理边界情况,
我们可以令nums1始终是长的那个数组,
然后令t = min(k//2,len(nums2))便可以防止上溢的发生。
python
def helper(nums1,nums2,k):
if(len(nums1) <len(nums2) ):
nums1, nums2 = nums2 , nums1 #保持nums1比较长
if(len(nums2)==0):
return nums1[k-1] # 短数组空,直接返回
if(k==1):
return min(nums1[0],nums2[0]) #找最小数,比较数组首位
t = min(k//2,len(nums2)) # 保证不上溢
if( nums1[t-1]>=nums2[t-1] ):
return helper(nums1 , nums2[t:],k-t)
else:
return helper(nums1[t:],nums2,k-t)
- 找中位数
为了处理奇偶时候中位数不同的计算方法,
这里可以采用一个小技巧:
令 k1 = ( len(nums1) + len(nums2) + 1 ) // 2
令 k2 = ( len(nums1) + len(nums2) + 2 ) // 2
对于偶数情况,k1对应中间左边,k2对应中间右边
对于奇数情况,k1,k2都对应中间
所以我们得到了获得中位数的统一方法:(helper(k1)+helper(k2))/2
缺点是:用了两倍的计算量;优点是:代码统一、清晰。
python
class Solution:
def findMedianSortedArrays(self, nums1: List[int], nums2: List[int]) -> float:
k1 = (len(nums1) + len(nums2) + 1) // 2
k2 = (len(nums1) + len(nums2) + 2) // 2
def helper(nums1, nums2, k): #本质上是找第k小的数
if len(nums1) < len(nums2): #保持nums1比较长
nums1, nums2 = nums2, nums1
if len(nums2) == 0: # 短数组空,直接返回
return nums1[k-1]
if k == 1:
return min(nums1[0], nums2[0]) #找最小数,比较数组首位
t = min(k//2, len(nums2)) # 保证不上溢
if nums1[t-1] >= nums2[t-1]:
return helper(nums1, nums2[t:], k-t)
else:
return helper(nums1[t:], nums2, k-t)
return (helper(nums1,nums2,k1) + helper(nums1, nums2, k2)) / 2
时间复杂度:O(log(n+m))
空间复杂度:O(1)
解题思路二:0
python
时间复杂度:O(n)
空间复杂度:O(n)
解题思路三:0
python
时间复杂度:O(n)
空间复杂度:O(n)