LeetCode-4:Median of Two Sorted Arrays (两个排序数组的中位数)

版权声明:本文为博主原创文章,未经允许不得转载。 https://blog.csdn.net/Koala_Tree/article/details/80243099

题目:

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)).

例子:

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

问题解析:

给定两个排序的数组,给出两个数组的中位数。

链接:

思路标签

双指针割和二分法两个排序数组的第k个元素

解答:

  • 问题为寻找中位数,这类问题属于寻找两个排序数组中的第k个元素。
  • 下面给出双指针合并和割的第k个元素。

1. 双指针合并数组求中位数

  • 利用两个指针分别指向第一和第二个数组;
  • 利用两个数组的大小,使用计数直接找到对应的中位数。
class Solution {
public:
    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
        int m = nums1.size();
        int n = nums2.size();
        int length = m + n;
        int midl = 0, midr = 0;
        if(length % 2 == 0){
            midl = length/2;
            midr = length/2+1;
        }
        else{
            midl = length/2+1;
            midr = length/2+1;
        }

        int k = 0;
        int i = 0, j = 0;
        int mid[2] = {0,0};
        while(k < midr){
            int a = (i < m)? nums1[i] : INT_MAX;
            int b = (j < n)? nums2[j] : INT_MAX;

            if(a <= b){
                k++;
                i++;
            }else{
                k++;
                j++;
            }

            if(k == midl)
                mid[0] = min(a, b);
            if(k == midr)
                mid[1] = min(a, b);
        }
        return (mid[0] + mid[1])/2.0;
    }
};

2. 利用割的思想,以及二分法来寻找中位数。

  • 利用割的思想寻找两个数组中的第k个元素:我们设:
    • Ci为第i个数组的割;
    • Li为第i个数组割后的左元素;
    • Ri为第i个数组割后的右元素。
  • 对于两个数组:
    • 首先Li<=Ri是一定的(因为数组有序);
    • 如果我们令L1<=R2 && L2<=R1,那么左半边 全小于右半边;
    • 如果左边的元素个数相加刚好等于k,那么第k个元素就是Max(L1,L2);
    • 如果 L1>R2,说明数组1的左边元素太大,我们把C1减小,把C2增大。
    • L2>R1同理,把C1增大,C2减小。
  • 例子:
    • 假设k=3
    • 对于 [1 4 7 9] [2 3 5]
    • 设C1 = 2,那么C2 = k-C1 = 1
    • [1 4/7 9] [2/3 5]
    • 这时候,L1(4)>R2(3),说明C1要减小,C2要增大,C1 = 1,C2=k-C1 = 2
    • [1/4 7 9] [2 3/5]
    • 这时候,满足了L1<=R2 && L2<=R1,第3个元素就是Max(1,3) = 3。
  • 中位数的特殊处理:(考虑奇偶的问题)
    • 虚拟加入‘#’(这个trick在manacher算法中也有应用),让数组长度恒为奇数(2n+1恒为奇数)。
    • 加入前:[1 4 7 9],长度4;加入后:[# 1 # 4 # 7 # 9 #],长度9;
    • 加入前:[2 3 5],长度3;加入后:[# 2 # 3 # 5 #],长度7。
    • 加完之后,每个位置可以通过/2得到原来元素的位置。
    • 无论是奇数还是偶数的情况都有:
    • Li = (Ci-1)/2, Ri = Ci/2
  • 把2个数组看做一个虚拟的数组A,目前有2m+2n+2个元素,找中位数,割在m+n+1处,所以我们只需找到m+n+1位置的元素和m+n+2位置的元素就行了。
  • 左边:A[m+n+1] = Max(L1+L2)
  • 右边:A[m+n+2] = Min(R1+R2)
Mid = (A[m+n+1]+A[m+n+2])/2 = (Max(L1+L2) + Min(R1+R2) )/2
  • 更快地寻找割的位置的方法:二分法
    • 只要C1或C2确定,另外一个也就确定了。这里,为了效率,我们肯定是选长度较短的做二分,假设为C1。
    • L1>R2,把C1减小,C2增大。—> C1向左二分
    • L2>R1,把C1增大,C2减小。—> C1向右二分
  • 如果C1或C2已经到头怎么办?这种情况出现在:如果有个数组完全小于或大于中值。可能有4种情况:
    • C1 = 0 :数组1整体都比中值大,L1赋小值取L2,中值在2中;
    • C1 = 2*m :数组1整体都比中值小,R1赋大值取R2,中值在2中;
    • C2 = 0 :数组2整体都比中值大,L2赋小值取L1,中值在1中;
    • C2 = 2*n:数组2整体逗比中值小,R2赋大值取R1,中值在1中。
class Solution {
public:
    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
        int m = nums1.size();
        int n = nums2.size();
        if(m > n)
            return findMedianSortedArrays(nums2, nums1);

        int L1, R1, L2, R2, C1, C2, low = 0, high = 2*m;
        while(low <= high){
            C1 = (low + high)/2;
            C2 = m+n - C1;

            L1 = (C1 == 0)? INT_MIN : nums1[(C1-1)/2];
            R1 = (C1 == 2*m)? INT_MAX : nums1[C1/2];
            L2 = (C2 == 0)? INT_MIN : nums2[(C2-1)/2];
            R2 = (C2 == 2*n)? INT_MAX : nums2[C2/2];

            if(L1 > R2)
                high = C1-1;
            else if(L2 > R1)
                low = C1+1;
            else
                break;
        }
        return (max(L1, L2) + min(R1, R2))/2.0;
    }
};
阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页