LeetCode 4.Median of Two Sorted Arrays 寻找两个有序数组的中位数详解

7 篇文章 0 订阅
6 篇文章 0 订阅

LeetCode 4.Median of Two Sorted Arrays 寻找两个有序数组的中位数 标准答案详解


https://leetcode.com/problems/median-of-two-sorted-arrays/
鉴于这道题本事有明确的时间复杂度要求,并且题面原始的输入结构又是两个List,最好不要贪图省事直接合并两个List为一个List,重新sort再找中位数这种方法。不客气的说,这种答案对于学习实践数据结构算法几乎没有意义,遗憾的是评论区都是这样的答案。 https://leetcode.com/problems/median-of-two-sorted-arrays/discuss/?currentPage=1&orderBy=hot&query=

class Solution:
    def findMedianSortedArrays(self, nums1: List[int], nums2: List[int]) -> float:
        nums = nums1 + nums2
        numsSorted = sorted(nums)
        return self.median(numsSorted)
    
    def median(self, numbers: list) -> float:
        length = len(numbers)
        if length%2==0: 
            return (numbers[int(length/2)]+numbers[int(length/2-1)])/2
        else: return float(numbers[int((length-1)/2)])

鉴于此,我觉得读官方解题是非常重要。因为能想出出题人都没想到的point固然很好但是,有些看似高效的解,实则应用了原生函数,且忽略题面复杂度条件就不好了。官网上的解答,虽然对于代码细节有些时候也没有讲得很清楚,但是思路清晰。鉴于此,这篇会主要Dig官网的内容。

审题

https://leetcode.com/problems/median-of-two-sorted-arrays/solution/
中位数 median的数学意义是什么?

Dividing a set into two equal length subsets, that one subset is always greater than the other.

翻译过来就是把一个集合平分成两个子集,一个子集里的所有数比另一个子集里面的所有数值大。这里一共两个条件。数学表达是我乱写的,欢迎质疑指正。

  1. l e n ( L e f t ) = l e n ( R i g h t ) len(Left) = len(Right) len(Left)=len(Right)
  2. ∀ L e f t &lt; ∀ R i g h t \forall Left&lt;\forall Right Left<Right

再来分析我们的题目。
已有A,B两个排序好的数组。如果我们先对A进行拆分。找到A合适的划分间隔(时间复杂度 O ( m − 1 ) O(m-1) O(m1)),再用同样的方法找到集合B的划分间隔(时间复杂度 O ( n − 1 ) O(n-1) O(n1))。现在内存空间中共有 A-left,A-right,B-left,B-right四个组。但是注意把A-left和B-left放Left把A-right,B-right放进Right 只能保证条件2。

正式因为没办法直接满足条件我们需要把上面提到的条件break down到两个数组A和B数组组成完整数组的情况,也就是根据A和B对于中位数重新定义上面两个条件。i代表A的索引,j代表B的索引。A长度m,B长度n。

  1. i + j = m − i + n − j ( o r : m − i + n − j + 1 ) i+j=m−i+n−j (or: m - i + n - j + 1) i+j=mi+nj(or:mi+nj+1)
  2. B [ j − 1 ] ≤ A [ i ] a n d A [ i − 1 ] ≤ B [ j ] B[j−1]≤A[i] \quad and \quad \text{A}[i-1] \leq \text{B}[j] B[j1]A[i]andA[i1]B[j]

注意公式1就是: 2 ( i + j ) = m + n 2(i+j) = m+n 2(i+j)=m+n。再转换下更明显: m + n 2 = i + j \frac{m+n}{2} = i+j 2m+n=i+j其实还是在定义条件1。A数组的间隔索引i加上B数组的间隔索引j需要刚好等于总数据量的一半。一会儿补个图更清晰。另外由于i和j实际上都必须大于0。原作者补充了一句。
i f n ≥ m : i = 0 ∼ m , j = m + n + 1 2 − i if \quad n \geq m : i = 0 \sim m, j = \frac{m + n + 1}{2} - i ifnm:i=0m,j=2m+n+1i

条件2类推也是对于原来公式2的发展。左边最后一个数组一定比右边第一个数字大。
注意这里原作者说了他只是写了个简单的表达忽略了i,j =0,和i=m,j=n的情况。

推导到此结束,实际运算会用的运算逻辑就是
从区间[0,m]中选择i,i需要符合下面两个大条件

  • B [ j − 1 ] ≤ A [ i ] a n d A [ i − 1 ] ≤ B [ j ] \text{B}[j-1] \leq \text{A}[i] \quad and \quad \text{A}[i-1] \leq \text{B}[j] B[j1]A[i]andA[i1]B[j]
  • j = m + n + 1 2 − i j = \frac{m + n + 1}{2} - i j=2m+n+1i
    好好利用数据已经排好序的这个特点,可以用两分法更快的找到 i i i和其对应的 j j j。这个和正常的二分查找差不多。

二分查找

  1. 设置i的查找区间 i m i n = 0 , i m a x = m imin = 0, imax = m imin=0,imax=m
  2. 初始化A和B的查找分隔点 i = i m i n + i m a x 2 i = \frac{imin+imax}{2} i=2imin+imax j = m + n + 1 2 − i \quad j= \frac{m + n + 1}{2} - i j=2m+n+1i
  3. 1和2 的初始化设置可以满足条件2使得我们目标的Left和Right等长但是并不满足条件1,所以还需要根据判断结果进行操作。
  • 直接满足 B [ j − 1 ] ≤ A [ i ] a n d A [ i − 1 ] ≤ B [ j ] \text{B}[j-1] \leq \text{A}[i] \quad and \quad \text{A}[i-1] \leq \text{B}[j] B[j1]A[i]andA[i1]B[j]结束游戏
  • B [ j − 1 ] &gt; A [ i ] B[j−1]&gt;A[i] B[j1]>A[i] 我们需要大一点的i,imin+1再次执行二分查找
  • A [ i − 1 ] &gt; B [ j ] : A[i−1]&gt;B[j]: A[i1]>B[j]: 我们需要小一点的i,imax-1再次执行二分查找
  1. j, i 确定后有两种情况
    • m+n为odd, m e d i a n = m a x ( A [ i − 1 ] , B [ j − 1 ] ) median = max(A[i−1],B[j−1]) median=max(A[i1],B[j1])
    • m+n为even, m e d i a n = m a x ( A [ i − 1 ] , B [ j − 1 ] ) + m i n ( A [ i ] , B [ j ] ) 2 median = \frac{max(A[i−1],B[j−1])+min(A[i],B[j])}{2} median=2max(A[i1],B[j1])+min(A[i],B[j])

代码

看完上面之后代码应该已经非常清楚了。当然你和官网下面的comments一样觉得不清楚的化,我在browse的过程中发现大家都推荐

https://medium.com/@hazemu/finding-the-median-of-2-sorted-arrays-in-logarithmic-time-1d3f2ecbeb46/

都是在说同一个事儿,这个有图可能清楚很多。因为限制了算法复杂度,只有按着这个思路来的才是正确答案。

class Solution {
    public double findMedianSortedArrays(int[] A, int[] B) {
        int m = A.length;
        int n = B.length;
        if (m > n) { // to ensure m<=n
            int[] temp = A; A = B; B = temp;
            int tmp = m; m = n; n = tmp;
        }
        int iMin = 0, iMax = m, halfLen = (m + n + 1) / 2;
        while (iMin <= iMax) {
            int i = (iMin + iMax) / 2;
            int j = halfLen - i;
            if (i < iMax && B[j-1] > A[i]){
                iMin = i + 1; // i is too small
            }
            else if (i > iMin && A[i-1] > B[j]) {
                iMax = i - 1; // i is too big
            }
            else { // i is perfect
                int maxLeft = 0;
                if (i == 0) { maxLeft = B[j-1]; }
                else if (j == 0) { maxLeft = A[i-1]; }
                else { maxLeft = Math.max(A[i-1], B[j-1]); }
                if ( (m + n) % 2 == 1 ) { return maxLeft; }

                int minRight = 0;
                if (i == m) { minRight = B[j]; }
                else if (j == n) { minRight = A[i]; }
                else { minRight = Math.min(B[j], A[i]); }

                return (maxLeft + minRight) / 2.0;
            }
        }
        return 0.0;
    }
}
def median(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

算法复杂度解释

时间复杂度

  1. 由于集合A间隔i取值有m种情况所以为 O ( m ) O(m) O(m)
  2. 由于二分查找的使用每次查找后搜寻空间少一半所以有 O ( l o g ( m ) ) O(log(m)) O(log(m))
  3. 又由于其实m和n的值相互制约的,实际上m和n之间最少值确定了取值范围 O ( l o g ( m i n ( m , n ) ) ) O(log(min(m,n))) O(log(min(m,n)))
    空间复杂度
    由于一直是在算索引,只需要几个记录的常数,和ij变量中间都不再存数组相关值共9个[A,B,m,n,i,j,imin,imax,half_len]。
    空间复杂度
    O ( 1 ) O(1) O(1)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值