leetcode 4.寻找两个正序数组的中位数

原题链接:寻找两个正序数组的中位数

01.合并

时间复杂度: O ( m + n ) O(m + n) O(m+n), 空间复杂度: O ( m + n ) O(m + n) O(m+n)

nums1nums2合并到merge中,直接输出中位数

class Solution {
public:
    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
        int n = nums1.size(), m = nums2.size();
        vector<int> merge(m + n);
        int i = 0, j = 0, idx = 0;
        while(i < n && j < m)
        {
            if(nums1[i] < nums2[j])
                merge[idx ++] = nums1[i ++];
            else
                merge[idx ++] = nums2[j ++];
        }
        while(i < n) merge[idx ++] = nums1[i ++];
        while(j < m) merge[idx ++] = nums2[j ++];
        int c = (idx + 1) >> 1;
        if(idx & 1) return (double)merge[c - 1];
        return (merge[c] + merge[c - 1]) / 2.0;
    }
};

02.指针

时间复杂度: O ( m + n ) O(m + n) O(m+n), 空间复杂度: O ( 1 ) O(1) O(1)

通过指针l1、r1、l2、r2来指向nums1nums2的最大、最小值,依次删除整体的最大和最小值,重复(m + n - 1) / 2次,留下的即是中位数。当l1 > r1时,则中位数在num2中;当l2 > r2时,则中位数在nums1中;否则中位数即是nums1nums2中各有一个。

class Solution {
public:
    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
        int n = nums1.size(), m = nums2.size();
        if(m + n == 1)
        {
            if(m) return (double)nums2[0];
            else return (double)(nums1[0]);
        }
        if(m == 1 && n == 1)
            return (nums1[0] + nums2[0]) / 2.0;

        int l1 = 0, l2 = 0, r1 = n - 1, r2 = m - 1;
        int c = (m + n - 1) >> 1;
        for(int i = 0; i < c; ++ i)
        {
            if(l1 >= n) ++ l2;
            else if(l2 >= m) ++ l1;
            else
            {
                if(nums1[l1] < nums2[l2]) ++ l1;
                else ++ l2;
            }
            
            if(r1 < 0) -- r2;
            else if(r2 < 0) -- r1;
            else
            {
                if(nums1[r1] > nums2[r2]) -- r1;
                else -- r2;
            }
        }
        if(l1 > r1) return (nums2[l2] + nums2[r2]) / 2.0;
        else if(l2 > r2) return (nums1[l1] + nums1[r1]) / 2.0;
        return (nums1[l1] + nums2[l2]) / 2.0;
    }
};

03.划分数组

时间复杂度: O ( l o g ( m i n ( m , n ) ) ) O(log(min(m, n))) O(log(min(m,n))), 空间复杂度: O ( 1 ) O(1) O(1)

首先,我们确保第一个数组 nums1 的长度小于或等于第二个数组 nums2 的长度。如果不满足这个条件,可以交换两个数组以确保满足。

接下来,我们使用二分查找的方法在第一个数组 nums1 中选择一个分割点 pos1,该分割点将数组划分为左右两个部分。同样,在第二个数组 nums2 中选择一个分割点 pos2,也将数组划分为左右两个部分。分割点的位置可以通过以下公式计算得到:

pos1= (l + r) / 2; pos2 = (n + m + 1) / 2 - pos1

其中,lowhigh 分别代表第一个数组的起始和结束位置,nm 分别代表两个数组的长度。

根据分割点的位置,我们可以计算出左侧部分的最大值和右侧部分的最小值。假设 maxLeft1minRight1 分别表示第一个数组左侧部分的最大值和右侧部分的最小值,maxLeft2minRight2 分别表示第二个数组左侧部分的最大值和右侧部分的最小值。

接下来,我们比较 maxLeft1minRight2 以及 maxLeft2minRight1 的大小关系。如果满足 maxLeft1 <= minRight2maxLeft2 <= minRight1,那么说明分割点的位置划分得合理,左侧部分的元素都小于等于右侧部分的元素。这时,根据数组长度之和的奇偶性,我们可以得到中位数的值。

  • 如果数组长度之和 (n + m) 是偶数,那么中位数是左侧部分的最大值 max(maxLeft1, maxLeft2) 和右侧部分的最小值 min(minRight1, minRight2) 的平均值。
  • 如果数组长度之和 (n + m) 是奇数,那么中位数是左侧部分的最大值 max(maxLeft1, maxLeft2)

如果上述条件不满足,我们需要调整分割点的位置。

  • 如果 maxLeft1 > minRight2,说明第一个数组的左侧部分太大,我们需要将第一个数组的分割点向左移动,即 r = pos1 - 1
  • 如果 maxLeft2 > minRight1,说明第二个数组的左侧部分太大,我们需要将第一个数组的分割点向右移动,即 l = pos1 + 1

通过不断调整分割点的位置,我们最终可以找到合适的分割点,使得两个分割点左侧部分的元素个数与右侧部分的元素个数相等(或差距不超过1),从而得到中位数。

总的来说,这个算法通过二分查找的思想,将问题的规模不断缩小一半,因此时间复杂度为 O ( l o g ( m i n ( n , m ) ) ) O(log(min(n, m))) O(log(min(n,m))),其中 n 和 m 分别是两个数组的长度。这种算法可以在对数时间复杂度内寻找两个已排序数组的中位数。

class Solution {
public:
    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
        int n = nums1.size();
        int m = nums2.size();
        if (n > m) {
            return findMedianSortedArrays(nums2, nums1);
        }
        int l = 0;
        int r = n;
        while (l <= r) {
            int pos1 = (l + r) / 2;
            int pos2 = (n + m + 1) / 2 - pos1;
            int maxLeft1 = (pos1 == 0) ? INT_MIN : nums1[pos1 - 1];
            int minRight1 = (pos1 == n) ? INT_MAX : nums1[pos1];
            int maxLeft2 = (pos2 == 0) ? INT_MIN : nums2[pos2 - 1];
            int minRight2 = (pos2 == m) ? INT_MAX : nums2[pos2];
            if (maxLeft1 <= minRight2 && maxLeft2 <= minRight1) {
                if ((n + m) % 2 == 0) {
                    return (max(maxLeft1, maxLeft2) + min(minRight1, minRight2)) / 2.0;
                } else {
                    return max(maxLeft1, maxLeft2);
                }
            } else if (maxLeft1 > minRight2) {
                r = pos1 - 1;
            } else {
                l = pos1 + 1;
            }
        }
        return -1;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值