Leet Code 4 Median of 2 Sorted Array

该博客介绍了如何在LeetCode的第4题中找到两个已排序数组的中位数,目标是实现O(log(m+n))的时间复杂度。博主对比了多种方法,包括归并排序、部分合并、排序优化以及分治策略。重点讲解了通过分治策略寻找第k小元素的方法,递归地排除一半的元素,直到找到中位数或者数组长度减到1。最后提到一种来自leetcode missmary的大神解决方案,该方案在大多数情况下运行效率更高。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最优解思路五,最能接受解为思路四。

There are two sorted arrays nums1 and nums2 of size m and n respectively.

Find the median of the two sorted arrays. The overall run time complexity should be O(log (m+n)).

You may assume nums1 and nums2 cannot be both empty.

Example 1:

nums1 = [1, 3]
nums2 = [2]

The median is 2.0

Example 2:

nums1 = [1, 2]
nums2 = [3, 4]

The median is (2 + 3)/2 = 2.5

思路一:
简单粗暴使用归并排序,将 nums1 和 nums2 所有元素按照归并排序合并时的思想合并成一个列表,然后根据列表总长度输出。
代码:

class Solution:
    def findMedianSortedArrays(self, nums1, nums2):
        """
        :type nums1: List[int]
        :type nums2: List[int]
        :rtype: float
        """
        l1,l2 = len(nums1),len(nums2)
        m = (l1 + l2) // 2
        s = []
        i = j =0
        while i < l1 and j < l2:
            if nums1[i] < nums2[j]:
                s.append(nums1[i])
                i += 1
            else:
                s.append(nums2[j])
                j += 1
        s += nums1[i:]
        s += nums2[j:]

        if (l1 + l2) % 2:
            return s[m]/1
        else:
            return (s[m] + s[m-1])/2

结果:
在这里插入图片描述

分析:
额外空间 O ( m + n ) O(m+n) O(m+n),时间复杂度 O ( m + n ) O(m+n) O(m+n)

思路二:
改进思路一。一中是将两个列表完全合并,稍微改进一些,是只合并前 ( l e n ( n u m s 1 ) + ( l e n ( n u m s 2 ) ) / 2 + 1 (len(nums1) + (len(nums2)) / 2 + 1 (len(nums1)+(len(nums2))/2+1 个元素

class Solution:
    def findMedianSortedArrays(self, nums1, nums2):
        """
        :type nums1: List[int]
        :type nums2: List[int]
        :rtype: float
        """
        l1,l2 = len(nums1),len(nums2)
        m = (l1 + l2) // 2
        s = []
        i = j = 0
        while i < l1 and j < l2 and len(s) < m+1:
            if nums1[i] < nums2[j]:
                s.append(nums1[i])
                i += 1
            else:
                s.append(nums2[j])
                j += 1
        # 循环结束后,可能 nums1 与 nums2 长短差比较大,导致len(s) 可能小于 m+1
        # 接下来要判断是哪个列表导致循环结束,
        if i == l1 and len(s) < m+1:
            for ii in range(m+1-len(s)):
                s.append(nums2[j+ii])

        if j == l2 and len(s) < m+1:
            for jj in range(m+1-len(s)):
                s.append(nums1[i+jj])
                
        if (l1 + l2) % 2:
            return s[-1] / 1
        else:
            return (s[-1] + s[-2]) / 2

结果:
在这里插入图片描述

分析:
空间复杂度 O ( m + n ) O(m+n) O(m+n), 时间复杂度 O ( m + n ) O(m+n) O(m+n)

思路三:
很崩溃,找到官方的快解,速度好快呀。不知道为什么list.sort()会比归并快。


class Solution:
    def findMedianSortedArrays(self, nums1, nums2):
        """
        :type nums1: List[int]
        :type nums2: List[int]
        :rtype: float
        """
        num = nums1 + nums2
        # list.sort() 的时间复杂度为 O(nlogn)
        num.sort()
        l = len(num)
        if l % 2 == 0:
            return (num[int(l/2)] + num[int((l/2) - 1)]) / 2
        else:
            return num[int((l - 1)/ 2)]

结果:
在这里插入图片描述

分析:
空间复杂度 O ( m + n ) O(m+n) O(m+n), 时间复杂度 O ( n l o g n ) O(nlogn) O(nlogn), 但是比大多程序得分高,原因是测试用例太少,过于简单。

思路四:

以上思路均不能达到时间复杂度 O ( l o g ( m + n ) ) O(log(m+n)) O(log(m+n))。很明显,只有使用分治,才可能有 l o g log log 的时间复杂度。

可以将此题理解为求两个有序的 array 的第k小的数。由于 array 有序,可以每趟可以排除 k//2 个元素。

假设 A = A0,A1,A2,A3,A4… B = B0,B1,B2,B3,B4… 现寻找第7小的数:

比较 A 和 B 中的第 7//2=3 个元素,若 A2 < B2, 则
最多有 2 个元素(B0,B1)小于 A0,
最多有 3 个元素(B0,B1,A0)小于 A1,
最多有 4 个元素(B0,B1,A0,A1)小于 A2,
也就是说此时 A0,A1,A2 即 A 中前 7//2 个数不可能是第 7 小的元素,进而排除之。

对于 B0, 可能存在 A0 < A1 < A2 < A3 < A4 < A5 < B0,
对于 B1, 可能存在 A0 < A1 < A2 < A3 < A4 < B0 < B1,
对于 B2, 可能存在 A0 < A1 < A2 < A3 < B0 < B1 < B2,所以 B0,B1,B2均可能为第 7 小的元素。即只能排除A0,A1,A2。接下来问题转换成寻找 A = A3,A4…; B = B0,B1,B2,B3,B4…中第 (7 - 7//2) 小的数。得到 k 的更新公式为 k = k - k//2

算法每趟都是比较第 k//2 个元素,假设每次都是 A 减少元素,可能出现 k = 1,这时返回 min(A[0], B[0]); 也可能出现数组长度小于 k//2 的情况,这时将指针指向数组最后一个元素即可。这时排除过元素后会出现空列表,那么可直接返回非空列表的第 (k - len(array)) 个元素。

综上,无论是找第奇数个还是第偶数个小的数,对算法并没有影响,而且在算法进行中,k 的值都有可能有奇有偶,最终都会变为 1 或者出现一空个数组,直接返回结果。

所以可采用递归的思路,为了防止数组长度小于 k // 2 ,所以每次比较 min ( k // 2, len (array) ) 对应的数字,把小的那个对应的数组的数字排除,将两个新数组进入递归,并且 k 要减去排除的数字的个数。递归出口就是当 k = 1 或者其中一个数字长度是 0 了。

问题:为什么要从k/2开始寻找,依旧假设 k = 7, 可以比较A0 和 B6的关系么,可以这样做,但是明显的问题出现在如果A0 > B6,那么第 7 小的数应该存在于 B7 和A0 中。

如果A0 < B6,这个时间可能性就很多了,比如A0 < A1 < A2 < A3 < A4 < B0 < B1,各种可能,无法排除元素,所以还是要从 k//2 开始寻找。

class Solution:
    def findMedianSortedArrays(self, nums1, nums2):
        """
        :type nums1: List[int]
        :type nums2: List[int]
        :rtype: float
        """
        n = len(nums1) + len(nums2)
        res = self.find_k(nums1, nums2, n//2+1)
        if n%2: return res/1
        return (self.find_k(nums1, nums2, n//2) + res) /2
    
    def find_k(self,A,B,k):
    #    make sure A is the smaller array
        if len(A) > len(B): 
            return self.find_k(B, A, k)
        if len(A) == 0: return B[k-1]
        if k == 1: return min(A[0], B[0])

#         in case of out of range of list due to k//2 > len(A)
        p = min(k//2, len(A))
        if A[p-1] < B[p-1]:
            return self.find_k(A[p:], B, k-p)  
        else: 
            return self.find_k(A, B[p:], k-p)

#     改进,使用列表内部index移动,而非切片

结果:
在这里插入图片描述

思路五摘于leetcode missmary

欣赏大神直通车

时间复杂度为 O ( l o g ( m i n ( m , n ) ) ) O(log(min(m,n))) O(log(min(m,n))) but beats 70+%

class Solution:
    def findMedianSortedArrays(self,A, B):
        m, n = len(A), len(B)
        if m > n:
            A, B, m, n = B, A, n, m
        if n == 0:
            raise ValueError

        imin, imax, half_len = 0, m, (m + n + 1) // 2
        while imin <= imax:
            i = (imin + imax) // 2
            j = half_len - i
            if i < m and B[j-1] > A[i]:
                # i is too small, must increase it
                imin = i + 1
            elif i > 0 and A[i-1] > B[j]:
                # i is too big, must decrease it
                imax = i - 1
            else:
                # i is perfect

                if i == 0: max_of_left = B[j-1]
                elif j == 0: max_of_left = A[i-1]
                else: max_of_left = max(A[i-1], B[j-1])

                if (m + n) % 2 == 1:
                    return max_of_left

                if i == m: min_of_right = B[j]
                elif j == n: min_of_right = A[i]
                else: min_of_right = min(A[i], B[j])

                return (max_of_left + min_of_right) / 2.0
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值