一. 题目
-
题目
给定两个大小为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。
请你找出这两个正序数组的中位数,并且要求算法的时间复杂度为 O(log(m + n))。
你可以假设 nums1 和 nums2 不会同时为空。 -
示例
二. 方法一: 合并求解(达不到题目要求的时间复杂度)
-
解题思路
-
解题代码
def findMedianSortedArrays(self, nums1: List[int], nums2: List[int]) -> float: len1 = len(nums1) len2 = len(nums2) # 如果两个列表都为空, 则返回0 if len1 == 0 and len2 == 0: return 0 i, j = 0, 0 # 从头开始比较两个列表中元素的大小 while len1 != 0 and len2 != 0 and i < len(nums1) and j < len2: # 如果nums1中当前元素大于等于nums2中的当前元素, # 则将nums2中的当前元素插入到nums1的当前位置 if nums2[j] <= nums1[i]: nums1.insert(i, nums2[j]) # 并将nums2中的指针移到下一位 j += 1 # 否则, 将nums1中的指针移到下一位 else: i += 1 # 此时, nums1的指针已经指到最后一位, 但nums中还有元素未插入 # 将nums2中剩余的元素插入到nums1的后面 while len2 != 0 and j < len2: nums1.append(nums2[j]) j += 1 size = len(nums1) # 返回中位数 if size % 2 == 1: return nums1[int((size - 1) / 2)] else: return (nums1[int(size / 2 - 1)] + nums1[int(size / 2)]) / 2
-
分析
时间复杂度: O(m+ n)
空间复杂度: O(n)
三. 方法二: 奇数求解(达不到题目要求的时间复杂度)
-
解题思路
-
我们不需要将两个数组真的合并,我们只需要找到中位数在哪里就可以了。
-
用 len 表示合并后数组的长度
- 如果是奇数,我们需要知道第 (len+1)/2 个数就可以了,如果遍历的话需要遍历 int(len/2 ) + 1 次。
- 如果是偶数,我们需要知道第 len/2和 len/2+1 个数,也是需要遍历 len/2+1 次。所以遍历的话,奇数和偶数都是 len/2+1 次。
-
返回中位数的话,奇数需要最后一次遍历的结果就可以了,偶数需要最后一次和上一次遍历的结果。
-
所以我们用两个变量 left 和 right,right 保存当前循环的结果,在每次循环前将 right 的值赋给 left。这样在最后一次循环的时候,left 将得到 right 的值,也就是上一次循环的结果,接下来 right 更新为最后一次的结果。
-
用 aStart 和 bStart 分别表示当前指向 A 数组和 B 数组的位置。
- 如果 aStart 还没有到最后并且此时 A 位置的数字小于 B 位置的数组,那么就可以后移了。也就是aStart<m&&A[aStart]< B[bStart]。
- 如果 B 数组此刻已经没有数字了,继续取数字 B[ bStart ],则会越界,所以判断下 bStart 是否大于数组长度了,这样 || 后边的就不会执行了,也就不会导致错误了,所以增加为 aStart<m&&(bStart) >= n||A[aStart]<B[bStart]) 。
-
-
解题代码
def findMedianSortedArrays(nums1: List[int], nums2: List[int]): len1 = len(nums1) len2 = len(nums2) size = len1 + len2 # left 用于存放遍历的前一个值 # right 用于存放遍历的当前值 left, right = -1, -1 # aStart, bStart 用于表示指针的移动 aStart, bStart = 0, 0 # 只需要遍历 size/2 + 1次就遍历到中位数了 for num in range(size // 2 + 1): # 每一次循环, 就将当前的值赋值给前一个值 left = right # 当nums1还没有遍历到最后, 且 # aStart指向的值小于bStart执行的值 或者 nums2已经遍历到最后了 # 此时就移动nums1 if aStart < len1 and (bStart >= len2 or nums1[aStart] < nums2[bStart]): right = nums1[aStart] aStart += 1 # 否则就移动nums2 else: right = nums2[bStart] bStart += 1 # 如果列表长度和为偶数, 则中位数是中间两个数之和再求平均 if size % 2 == 0: return (left + right) / 2 # 如果类别长度为奇数, 则中位数是最中间的那个数 else: return right
-
分析
时间复杂度: O(m + n)
空间复杂度: O(1)