【leetcode第4题】寻找两个正序数组的中位数

寻找两个正序数组的中位数题解——划分数组法

  题目内容

       给定两个大小为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。请你找出并返回这两个正序数组的中位数。

# 示例 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
# 示例 3:
	# 输入
	>>>nums1 = [], nums2 = [1]
	# 输出
	>>>1.00000
	# 输入
	>>>nums1 = [2], nums2 = []
	# 输出
	>>>2.00000
	提示:
		0 <= m <= 1000 , 0 <= n <= 1000 
		1 <= m + n <= 2000
		-1000000 <= nums1[i], nums2[i] <= 1000000

  解题方法

       看到这个题我们首先要思考,中位数的作用是什么。

       将一个集合划分为两个长度相等的子集,其中一个子集中的元素总是大于另一个子集中的元素。

       
       我们将nums1与nums2分别在 i,j 的位置划分为左右两部分试试看:

		# 假设:  m, n = len(nums1), len(nums2)
				left		 |		 right
		nums1[0, ... , i-1]  |  nums1[i, ... , m-1]
		nums2[0, ... , j-1]  |  nums2[j, ... , n-1]

       若把两个数组的左半边合并成一个新的集合:left,右半边合并成另一个新的集合:right
       
       我们希望能找到一种划分方式,使left与right集合恰好是中位数划分出的左右两子集
       
       为此我们需要满足两个条件:
              1.left集合的最大元素小于right集合的最小元素
              2.left集合的长度与right集合的长度相同

		# 条件1:
		# left集合最大元素为		max(nums1[i-1], nums2[j-1])
		# right集合最小元素为	min(nums1[i], nums2[j])
		# 由于在划分阶段,我们需要遍历较小长度的数组以寻找最优方案
		# 所以仅控制nums1[i-1]即可
		# nums1[i-1] <= nums1[i]	# 由于是正序数组,该条件一定满足
		nums1[i-1] <= nums2[j]
		

       考虑到总长度的奇偶性,我们要考虑更详细完善的约束条件:
              1.若总长度为偶数,中位数实际上由中间的两个数共同组成,上述条件不用更改
              2.若总长度为奇数,我们将中位数放入left集合,使left集合比right集合多一个元素,上述条件需要修正

		# 条件2:
		# left集合长度为:		i + j
		# 两正序数组总长度为:	m + n
		
		# 当总长度为偶数时:
		if (m + n) % 2 == 0:
			j = (m + n) / 2 - i
		# 当总长度为奇数时:
		if (m + n) % 2 == 1
			j = (m + n + 1) / 2 - i
		
		# 我们发现可以将奇偶情况下的约束合并为一条语句,省去判奇偶的时间:
		j = (m + n + 1) // 2 - i
		

       
       为了更快的找到划分的位置,我们可以采用二分查找的方法,快速找到 i 的位置:

	# 借助双指针left与right来计算 i 的位置
	i = (left + right) // 2
	
	# 在最初状态,i 应该在nums1中间
	left, right = 0, m
	
	# 若 i 取大了, 将right指针移至 i 的左边:
	若i太大:
		right = i - 1
	
	# 若 i 取小了, 将left指针移至 i 的右边:
	若i太小:
		left = i + 1

       
       找到合适的划分方式后,根据总长度的奇偶情况返回合适的数即可
       

  代码实现

class Solution:
    def findMedianSortedArrays(self, nums1: List[int], nums2: List[int]) -> float:
        if len(nums1) > len(nums2):
           return self.findMedianSortedArrays(nums2, nums1)
        
        MAXNUM = 10**6
        
        m = len(nums1)
        n = len(nums2)

        # 左右指针,寻找nums1划分点
        left, right = 0, m

        # 前半部分中位数, 后半部分中位数
        median1, median2 = 0, 0

        # nums1划分点 + nums2划分点
        i, j = 0, 0

        numsim1, numsi, numsjm1, numsj = 0, 0, 0, 0

        while left <= right:
            i = (left + right) // 2
            j = (m + n + 1) // 2 - i
            
            # nums1[0...i-1], nums1[i...m-1]
            numsim1 = ( nums1[i-1] if i != 0 else -MAXNUM )
            numsi = ( nums1[i] if i != m else MAXNUM )

            # nums2[0...j-1], nums2[j...n-1]
            numsjm1 = ( nums2[j-1] if j != 0 else -MAXNUM )
            numsj = ( nums2[j] if j != n else MAXNUM )

            if numsim1 <= numsj:
                median1, median2 = max( numsim1, numsjm1), min(numsi, numsj)
                left = i + 1
            else:
                right = i - 1
        
        return (median1 + median2) / 2 if (m + n) % 2 == 0 else median1

  复杂度分析

       时间复杂度:O(logmin(m,n)))。
       其中 m 和 n 分别是数组nums1 和 nums2 的长度。查找的区间是[0,m],而该区间的长度在每次循环之后都会减少为原来的一半。所以,只需要执行 logm 次循环。由于每次循环中的操作次数是常数,所以时间复杂度为 O(logm)。由于我们可能需要交换 nums1 和 nums2 使得m≤n,因此时间复杂度是 O(logmin(m,n)))。
       
       空间复杂度:O(1)。
       

  执行结果

在这里插入图片描述

       
       欢迎各位大佬交流讨论

       本文参考leecode题库第4题官方题解

猜你喜欢

转载自blog.csdn.net/weixin_42721167/article/details/112637470