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

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


题目:寻找两个正序数组的中位数

给定两个大小分别为m,n的正序(从小到大)数组nums1和nums2。请找出并返回这两个正序数组的中位数
在这里插入图片描述

题目来自力扣


一、题目分析

给定的两个数组nums1和nums2均为有序(从小到大),第一时间想到的是将两个数组拼接成一个新的有序数组new,在有序数组中寻找中位数存在两种情况:

  1. m+n为奇数,其中位数是new的第(m+n+1)//2个数——medi = new[(m+n+1)//2-1]
  2. m+n为偶数,其中位数则为new的第(m+n+1)//2和第(m+n+1)//2 +1个数的平均值。——media = mean(new[(m+n+1)//2-1], new[(m+n+1)//2])

如果使用暴力解决的话,使用新的数组接收重新排序后的数组,空间和时间复杂度都太高了。
此时,可以换一种思路,并不非要对数据进行排序,因为两个数组本身已经是有序的,而中位数的索引是可以确定的,所以可以转化为寻找两个数组中第k小的数的问题。

寻找数组中第k个数,可以通过比较nums1[k//2-1]和nums2[k//2-1]的大小(注意此处的k是指的数组中的位置,不是索引下标,所以需要-1),即可排除掉较小的那个数及其左边的所有数。如nums1[k//2-1]<nums2[k//2-1],意味着nums1[k//2-1]及其左边的k//2-2个数均小于nums2[k//2-1],然后将nums1[k//2:]作为新的nums1,k更新为k-k//2,重新寻找第k个数。不断递归,当k==1时,即可返回两个数组第一个数中更小的那个。
{插图}
有几种特殊情况需要考虑

  1. 存在一个数组为空的情况,因为1<= m+n <= 2000。所以只会有一个数组为空,当存在一个数组为空时,可以直接返回另一个数组的第k个数。
  2. 存在一个数组远短于另一个数组,如[1]和[2,3,4,5,6,7],此时k=4,k//2=2,大于第一个数组的长度。此时可以从比较第k//2个数——>比较较短数组末尾的数,再次更新数组和k的值,即可回到第一个特殊情况。

二、实现

代码实现如下所示:

class Solution:
    def media(self, nums1, nums2, k):
        m = len(nums1)
        # print(nums1)
        # print(nums2)
        # print(k)
        n = len(nums2)

        if m == 0:
            return nums2[k - 1]
        elif n == 0:
            return nums1[k - 1]

        if k == 1:
            return min(nums1[0], nums2[0])
        else:
            index = k // 2 - 1
            if index +1 > m:
                index2 = m-1
                if nums1[index2] < nums2[index2]:
                    nums1 = nums1[index2 + 1:]
                else:
                    nums2 = nums2[index2 + 1:]
                k = k - index2 - 1
                return self.media(nums1, nums2, k)
            elif index +1 > n:
                index2 = n - 1
                if nums1[index2] < nums2[index2]:
                    nums1 = nums1[index2 + 1:]
                else:
                    nums2 = nums2[index2 + 1:]
                k = k - index2 - 1
                return self.media(nums1, nums2, k)
            else:
                if nums1[index] < nums2[index]:
                    nums1 = nums1[index + 1:]
                else:
                    nums2 = nums2[index + 1:]
                k = k - index - 1
                return self.media(nums1, nums2, k)

    def mean(self, nums):
        # print(nums)
        if len(nums) == 1:
            return nums[0]
        else:
            return (nums[0] + nums[1]) / 2

    def findMedianSortedArrays(self, nums1, nums2) -> float:
        m = len(nums1)
        n = len(nums2)
        if m + n == 1 or m + n == 2:
            # print(nums1)
            # print(nums2)
            nums1.extend(nums2)
            return self.mean(nums1)
        else:
            if (m + n) % 2 == 0:
                ans = [self.media(nums1, nums2, (m + n) // 2), self.media(nums1, nums2, (m + n) // 2 + 1)]
            else:
                ans = [self.media(nums1, nums2, (m + n + 1) // 2)]
        return self.mean(ans)

三.需要注意的问题

  1. 比较之后,在修改数组时,只可以修改较小的那个数组,因为只能保证nums1的前k//2个数小于nums2的第k//2个数,无法保证nums2[k//2-1]<nums1[k//2],也无法保证nums[k//2-1]<nums[k//2+1],即较大数组的大小关系无法确定。
  2. 如果一开始m+n=1or2的话,即可直接返回中位数。

总结

其实算法就是如何更好的解决问题,要不更快,要不就更省资源。优化的过程其实就是等价问题转换的过程。很多时候思考问题,我们下意识会以人脑的思维去思考,如:第一时间想到合并生成新的有序数组。但是事实上,在合并新的有序数组时,我们也在比较数组间的大小关系,考虑把数放在什么位置。只要排完前多少位就可以不必再找了,相当于一个中间过程。
我的收获,其实学习计算机的过程,也是一种审视自己思考的过程,算法题是永远做不完的,但是我们可以去提升自己分析问题的能力和解决问题的效率。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值