提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
前言
上难度!感觉难题抛开思路之外也就是多了一些情况,但这些情况的解决需要一定数学推导、找规律+实现上多写点判断语句。
一、题目
寻找两个正序数组的中位数
给定两个大小分别为 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
二、思路
本题看到题目之后,很好想到的一种解法是利用归并排序的过程,双指针交叉向后遍历,找到合适的位置,即为数组的中位数。
PS 归并排序不熟悉的话可以去看leetcode第88题 合并两个有序数组
但是这种方法时间复杂度为O(m+n),如果需要达到O(log(m+n))的时间复杂度,那么很容易联想到二分查找的方法。
在第一种解法中,双指针向后遍历是线性的,一次遍历就去掉不可能是中位数的一个值,也就是一个一个排除。
联系二分查找的方法,如果每次排除一半的元素呢?
比如,假设我们要找第 k 位数,我们可以每次循环排除掉 k/2 个数。
首先,我们需要找到每个数组的第一个中位数的位置(即奇数个元素数组的中位数,偶数个元素数组的前一个数)
即第 k=(length+1)// 2
个数,那么本次排除的个数就是某组的前(k-1)// 2
个元素(一定不可能是所寻元素)(i=j=(k-1)// 2-1),若此组内元素不足,=最后一个元素
排除掉这些元素后,k=k-排除掉的元素个数,进入下一次循环
直到k==1,比较两个元素即可
或某组内元素没了,在另一个组找k个即可
三、代码
1.(练手)leetcode第88题——合并两个有序数组
class Solution:
def merge(self, nums1: List[int], m: int, nums2: List[int], n: int) -> None:
"""
Do not return anything, modify nums1 in-place instead.
"""
#把第二个序列插入第一个序列的合适位置,优点是不用建立中间数组,缺点是慢,每向nums1插入一个数就要pop一个0
count=0 #第一个数组中遍历过的元素个数
i=0 #指向第一个数组最后的位置
j=0 #指向第二个数组当前位置
while(count<m and j<n):
if(nums1[i]>nums2[j]): #第二个数组中的当前元素小
nums1.insert(i,nums2[j]) #插入第一个中
nums1.pop() #删除一个0
j+=1 #不做处理,指针后移
count-=1
i+=1#第一个数组指针后移
count+=1
if(j<n): #第一个数组遍历完了,但第二个数组还有
nums1[i:]=nums2[j:]
2.二路归并 O(m+n)
class Solution:
def findMedianSortedArrays(self, nums1: List[int], nums2: List[int]) -> float:
m=len(nums1)
n=len(nums2)
mid1=(m+n-1)//2 #第一个中位的位置
mid2=mid1+1 #第二个
i=j=0 #三个指针,i指向第一个数组下标,j指向第二个数组的下标
k=-1 #k指向当前已排好的下标
if(nums1):
val1=nums1[0] #val1记录当前k指向的元素的值,开始k指向i的元素
while(k<mid1 and i<m and j<n): #先找mid1
if(nums2[j]<nums1[i]):
val1=nums2[j]
j+=1
else:
val1=nums1[i]
i+=1
k+=1
while(k<mid1 and i<m): #num2没了
val1=nums1[i]
i+=1
k+=1
while(k<mid1 and j<n): #num1没了
val1=nums2[j]
j+=1
k+=1
#此时val1就是中位数1(奇数个的中位数,偶数个的第一个中位数)
#偶数个的第二个中位数为i和j当前所指元素的小的那个
if((m+n)%2==1):
return val1
else:
if(i<m and j<n): #俩都有
print(i,j)
val2=min(nums1[i],nums2[j])
elif(i<m): #nums2没了
val2=nums1[i]
else: #nums1没了
val2=nums2[j]
return (val1+val2)/2