LeetCode题目详解(三)——寻找两个有序数组的中位数

参考:【分步详解】两个有序数组中的中位数和Top K问题

题目描述

给定两个大小为 m 和 n 的有序数组 nums1 和 nums2。

请你找出这两个有序数组的中位数,并且要求算法的时间复杂度为 O(log(m + n))。

你可以假设 nums1 和 nums2 不会同时为空。

示例 1:

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

则中位数是 2.0
示例 2:

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

则中位数是 (2 + 3)/2 = 2.5

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/median-of-two-sorted-arrays

思路分析

一个增序数组

要寻求一个数组的中位数,很容易让人联想到用二分法解决

对于偶数个元素的数组A,假定其元素个数为 n,下标从0开始,其中位数是 (A[(n-1)/2] + A[n/2])/2

对于偶数个元素的数组B,假定其元素个数为m,下标从0开始,其中位数是A[m/2]

隐含条件:任何一个存在中位数的数组,其中位数都会将数组分割成等长的两份,并且左边小于中位数,右边大于中位数

两个增序数组

接下来推广,从上面的结论很容易得出,对于两个增序数组A,B,如果数组A中存在一个下标 i ,数组B中存在一个下标 j ,使得 len(数组A+数组B的左边) == len(数组A+数组B的右边),并且存在一个数使得A和B的左边都比它小,A和B的右边都比它大,那这个数就是这两个增序数组的中位数。

left_part Cut right_part
A[0],A[1],…,A[i] / A[i+1],A[i+2],…,A[m-1]
B[0],B[1],…,B[j] / B[j+1],B[j+2],…,B[n-1]

用表达式来表示上面的两个条件就是

  • i+j == m-1-i + n-1-j

  • A[i] <= B[j+1] && B[j] <= A[i+1]

双数组长度的奇偶

一个数组的奇偶有两种情况,可以想象,如果考虑双数组的奇偶,将是会有 2*2 = 4种情况,这将会大大增加编程难度,那么有没有一种方法让我们不用考虑奇偶。

让数组恒为奇数

操作很简单,就是给每个元素的左右加一个字符,比如 ‘#’,长度变为2*n+1,ex:

before len after len
{1,2,3} 3 {#,1,#,2,#,3,#} 7
{4,5,6,7} 4 {#,4,#,5,#,6,#,7,#} 9

这样加的好处不光解决了奇偶的问题,还让获取元素的方法化简了,对于任何一个元素,都可以通过 现在的位置/2 得到原来的位置,比如{#,1,#,2,#,3,#}中的2,它的位置是a[3],通过 3/2 == 1,可以得到原来的位置,{1,2,3}中的a[1] == 2,{#,4,#,5,#}中的5,位置是a[3],3/2 == 1,即{4,5,6,7}中的a[1] == 5

当然这个操作并不需要在代码里实现,这里只是帮助理解

等式变换

接下来看这个等式

  • i+j == m-1-i + n-1-j

由于数组长度增加,第一个等式变为

  • i+j == 2*m+1-1-i + 2*n+1-1-j
  • i+j == 2*m-i + 2*n-j
  • i+j == m+n

由此可得出 j == m+n-i,其中 i (0~2*m)

由于 j > 0,因此可以得出 m+n-i > 0,由 i 的取值范围,最终得出此关系成立的条件是 n>m,即第二个数组的大小一定要比第一个大。

分治策略

接下来看这个等式

  • A[i] <= B[j+1] && B[j] <= A[i+1]

那么分治的策略就很明显了

如果 A[i] > B[j+1],说明A数组的左边太长了,应该向左二分

如果 B[j] > A[i+1],说明A数组的左边太短了,应该向右二分

临界点

如果 i == 0 或 j == 0 或 i == 2*m 或 j == 2*n,那么A[i+1],B[j+1]等等都不存在,此时,到达临界的数组整体可能都比另一数组中的某一值小(大),那么中值在另一数组中。

代码

#include <bits/stdc++.h>

using namespace std;

class Solution
{
public:
    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2)
    {
        int m = nums1.size();
        int n = nums2.size();

        if(m>n)             //保证数组1长度<数组2长度
            return findMedianSortedArrays(nums2,nums1);

        int l1 = 0,l2 = 0,r1 = 0,r2 = 0,i = 0,j = 0,imin = 0,imax = 2*m; //下标为0~2*m
        while(imin <= imax)   //存在空数组的情况,因此要有=
        {
            i = (imax+imin)/2;
            j = m+n-i;

            l1 = (i == 0)?INT_MIN:nums1[(i-1)/2];
            r1 = (i == 2*m)?INT_MAX:nums1[i/2];
            l2 = (j == 0)?INT_MIN:nums2[(j-1)/2];
            r2 = (j == 2*n)?INT_MAX:nums2[j/2];

            if(l1>r2)
                imax = i-1;
            else if(l2>r1)
                imin = i+1;
            else
                break;
        }
        return (max(l1,l2)+min(r1,r2))/2.0;
    }

};

int main()
{
    vector<int> a = {1,2,4,5};
    vector<int> b = {3};
    Solution Sve;
    cout<<Sve.findMedianSortedArrays(a,b);
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章