求两个排序数组的中间值

本文探讨如何求两个已排序数组的中间值。当数组元素总数为奇数时,中间值是归并排序后的中间元素;若总数为偶数,则是中间两个数的平均值。文中提供了解法1和解法3,详细解释了每种方法的思路,并链接到原理解证明。

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

题目:数组A、B分别已经按照升序进行排列,元素个数分别为N1和N2。求这两个数组的中间值。

定义:当两个数组的元素个数之和为奇数时,中间数为两个数组归并排序后,排在中间的那个数;当两个数组的元素个数之和为偶数时,中间数为两个数组归并排序后,中间的两个数的平均值。


解决的思路:

将两个数组分别拆成两半,使得两个数组左半边的元素个数之和等于右半边的元素个数之和。


下面给出3种解法:

解法1:

import sys
class Solution(object):
    def findMedianSortedArrays(self, nums1, nums2):
        N1 = len(nums1)
        N2 = len(nums2)
        if N1 < N2:
            return self.findMedianSortedArrays(nums2, nums1)
        if N1 == 0:
            return 0
        elif N2 == 0:
            return (nums1[(N1-1)/2] + nums1[N1/2]) / 2.0
        flag = (N1 + N2) % 2 == 0
        Half = (N1 + N2) / 2 if flag else (N1 + N2) / 2 + 1
        lo = 0
        hi = N1 - 1
        while lo <= hi:
            mid = (hi + lo) / 2
            C1 = mid + 1
            C2 = Half - C1
            if C2 > N2:
                lo = mid + 1
                continue
            elif C2 == 0:
                if nums1[C1 - 1] > nums2[C2]:
                    hi = mid - 1
                    continue
                R1 = sys.maxint if C1 >= N1 else nums1[C1]
                return nums1[C1 - 1] if not flag else (nums1[C1 - 1] + min(R1, nums2[0])) / 2.0
            elif C2 < 0:
                hi = mid - 1
                continue

            if C2 < N2 and nums1[C1 - 1] > nums2[C2]:
                hi = mid - 1
            elif C2 > 0 and nums2[C2 - 1] > nums1[C1]:
                lo = mid + 1
            else:
                R1 = sys.maxint if C1 == N1 else nums1[C1]
                L1 = -sys.maxint - 1 if C1 == 0 else nums1[C1 - 1]
                R2 = sys.maxint if C2 == N2 else nums2[C2]
                L2 = -sys.maxint - 1 if C2 == 0 else nums2[C2 - 1]
                return max(L1, L2) if not flag \
                    else (max(L1, L2) + min(R1, R2)) / 2.0
        if hi < 0:
            L1 = -sys.maxint - 1
            R1 = nums1[0]
            L2 = nums2[Half - 1]
            R2 = sys.maxint if Half == N2 else nums2[Half]
            return (max(L1, L2) + min(R1, R2)) / 2.0

解法2:

class Solution(object):
    def findMedianSortedArrays(self, nums1, nums2):
        N1 = len(nums1)
        N2 = len(nums2)
        if N1 > N2:
            N1, N2, nums1, nums2 = N2, N1, nums2, nums1
        if N2 == 0:
            return 0
        elif N1 == 0:
            return (nums2[(N2-1)/2] + nums2[N2/2]) / 2.0
        flag = (N1 + N2) % 2 == 0
        Half = (N1 + N2 + 1) / 2
        lo = 0
        hi = N1
        while lo <= hi:
            C1 = (hi + lo) / 2
            C2 = Half - C1
            if C1 > 0 and C2 < N2 and nums1[C1 - 1] > nums2[C2]:
                hi = C1 - 1
            elif C2 > 0 and C1 < N1 and nums2[C2 - 1] > nums1[C1]:
                lo = C1 + 1
            else:
                if C2 == 0:
                    L = nums1[C1 - 1]
                elif C1 == 0:
                    L = nums2[C2 - 1]
                else:
                    L = max(nums1[C1 - 1], nums2[C2 - 1])
                if not flag:
                    return L
                if C1 == N1:
                    R = nums2[C2]
                elif C2 == N2:
                    R = nums1[C1]
                else:
                    R = min(nums1[C1], nums2[C2])
                return (L+R) / 2.0

解法3:

import sys
class Solution(object):
    def findMedianSortedArrays(self, nums1, nums2):
        """
        :type nums1: List[int]
        :type nums2: List[int]
        :rtype: float
        """
        N1 = len(nums1)
        N2 = len(nums2)
        if N1 < N2:
            return self.findMedianSortedArrays(nums2, nums1)
        if N1 == 0:
            return 0
        elif N2 == 0:
            return (nums1[(N1-1)/2] + nums1[N1/2]) / 2
    
        lo = 0
        hi = 2 * N1
        while lo <= hi:
            mid = (lo + hi) / 2
            C1 = mid
            C2 = N1 + N2 - C1
            L1 = nums1[(C1 - 1) / 2] if C1 != 0 else -sys.maxint - 1
            R1 = nums1[C1 / 2] if C1 != 2 * N1 else sys.maxint
            L2 = nums2[(C2 - 1) / 2] if C2 != 0 else -sys.maxint - 1
            R2 = nums2[C2 / 2] if C2 != 2 * N2 else sys.maxint
    
            if L1 > R2:
                hi = mid - 1
            elif L2 > R1:
                lo = mid + 1
            else:
                return (max(L1, L2) + min(R1, R2)) / 2.0
        return -1

其中解法1代码最繁琐,解法3最简洁。解法1和解法2的结题思路相同,都是遍历数组A左半边元素的个数,根据数组A左半边元素个数确定数组B左半边的元素个数。最终可以找到一种分割方式,使得数组A、B左半边的元素的数值比右半边的元素的数值都小,此时,可以根据分割的边界确定中间值。

解法1之所以比解法2繁琐,主要是由于没有选择好数组A。假设某一次分割中,数组A左边元素个数为C1,则数组B左边的元素个数为C2=(N1+N2+1)/2-C1。如果B[C2-1]>A[C1],需要增加C1后继续迭代。此时,如果N1<N2,可以保证下一次迭代,mid=(lo + hi)/2的数值不会超过(N1+N2+1)/2,从而保证C2>0。而如果N1>N2,则无法保证下一次迭代C2>0,因此需要额外添加许多判断逻辑。

解法3的思路较难想到,是从leetcode上copy过来的。具体证明过程见原文https://leetcode.com/discuss/41621/very-concise-iterative-solution-with-detailed-explanation。原文在推导index_L和index_R与N的关系时,只证明了分割点为数组中间值的情况。一般情况可以从该特殊情况出发,分N为奇数和偶数两种情况进一步推导得到。最终可以发现一般情况与特殊情况的切割点左右的下标表达式与特殊情况相同。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值