第4题,难度hard,思想方法是很巧妙的一种二分思想,但是不是严格意义的二分法。
如果不考虑时间复杂度,可以维护一个采用双指针的方法,分别从两个数组的头开始查找,然后找到k大的数字,时间复杂度是 O ( n + m ) O(n+m) O(n+m)
但是这里限制了时间复杂度是lg级别的,因此很自然的想到采用二分的策略。
进一步解释,每次我们分别对两个数组进行查找,找到的两个target中小的那个必然是不满足的,因为小于这个数字的只有 k − 2 k-2 k−2个,算上自身也只有 k − 1 k-1 k−1。我们删除这些数字(或者坐标进行偏移),相应的修正下一次需要寻找的k,重复计算。
那么结束循环的条件是什么呢?当k是1的时候,表示需要找到剩余数字中第一小的,因此返回min(nums1[index], nums2[index])
就可以。
但是还有两个特殊的情况,就是当某个数组查询到了该数组最大的元素时候,依然无法满足第k小(考虑nums1的全部元素都小于nums2),此时也可以直接结束循环,因为很明确第k小的元素一定在nums2中(实际上两个数组直接合并就是有序的了)。
class Solution:
def findMedianSortedArrays(self, nums1: List[int], nums2: List[int]) -> float:
def findkelement(k):
index1 = 0
index2 = 0
while True:
## 考虑可以返回的条件,某一个数组查询到了边界或者需要得到最小的那个
if index1 == n:
return nums2[index2+k-1]
if index2 == m:
return nums1[index1+k-1]
if k == 1:
return min(nums1[index1], nums2[index2])
## 在细节上需要多家注意,坐标对应的元素个数关系
new_index1 = min(index1 + k//2 - 1, n-1)# 保证大于k//2-1个,且不能超出边界
new_index2 = min(index2 + k//2 - 1, m-1)
target1 = nums1[new_index1]
target2 = nums2[new_index2]
if target1<=target2:
k -= new_index1-index1+1 # 包括自身在内的前面所有数组删除
index1 = new_index1 + 1
else:
k -= new_index2-index2 + 1
index2 = new_index2 + 1
n = len(nums1)
m = len(nums2)
total = n+m
# 对于数组的元素和是奇数还是偶数的问题,如上面的分析
if total&1:
return findkelement((total)//2+1)
else:
return (findkelement((total)//2)+findkelement((total)//2+1))/2
还有另外的方法可以将时间复杂度降低到 O ( l g ( m i n ( n , m ) ) ) O(lg(min(n,m))) O(lg(min(n,m))),但是需要用到数学的推导,参考题解