LeetCode50天刷题计划(Day 22—— 寻找两个正序数组的中位数、合并两个有序数组(13.30-16.30)

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

上难度!感觉难题抛开思路之外也就是多了一些情况,但这些情况的解决需要一定数学推导、找规律+实现上多写点判断语句。

一、题目

寻找两个正序数组的中位数

给定两个大小分别为 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

        

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值