[leetcode-hard]寻找两个正序数组的中位数 | LeetCode 4

题目:
image
我看到这个题目是因为数据结构线性表上有一道一样的题目,只不过线性表上的题目里面的长度是等长。
解法:
//AI整理
这道题可以使用二分查找分割线的方法解决。首先,我们需要在两个有序数组中分别找到一个分割位置,将两个数组划分为左半部分和右半部分。
设 nums1 和 nums2 的长度分别为 m 和 n,如果从 nums1 中取 i 个元素放入左半部分,那么为了保证左半部分元素总数满足中位数的要求,从 nums2 中取出的元素个数应该为 j = (m+n+1)/2-i。
为了方便进行二分查找,我们首先保证 nums1 是较短的数组,即 m<=n,因此代码开头需要判断 if(nums1.size()>nums2.size()) swap(nums1,nums2)。
这样我们只需要在 nums1 的分割位置 i∈[0,m] 上进行二分查找,同时可以保证计算得到的 j 始终处于 nums2 的合法范围内。
对于当前的分割位置 i 和 j,定义 left1=nums1[i-1]、right1=nums1[i]、left2=nums2[j-1]、right2=nums2[j],其中 left1 和 left2 分别表示两个数组左半部分的最大值,right1 和 right2 分别表示两个数组右半部分的最小值。
当 i=0 或 i=m,以及 j=0 或 j=n 时,使用 INT_MIN 和 INT_MAX 作为边界哨兵。
由于两个数组本身已经有序,所以同一个数组内部一定满足 left1<=right1 和 left2<=right2,因此只需要判断两个交叉条件 left1<=right2 和 left2<=right1 是否同时成立。
如果两个条件同时成立,就说明当前分割正确,即左半部分所有元素都不大于右半部分所有元素。
如果 left1>right2,说明从 nums1 中取到左半部分的元素过多,即分割位置 i 过于靠右,因此应该让 i 左移,令 hi=i-1;
如果 left2>right1,说明从 nums1 中取到左半部分的元素过少,即分割位置 i 过于靠左,因此应该让 i 右移,令 lo=i+1。
找到正确的分割位置后,如果 m+n 为奇数,由于左半部分比右半部分多一个元素,所以中位数就是 max(left1,left2);
如果 m+n 为偶数,则中位数是左半部分最大值和右半部分最小值的平均值,即 (max(left1,left2)+min(right1,right2))/2.0。
整个算法只对较短的数组进行二分查找,因此时间复杂度为 O(log(min(m,n))),空间复杂度为 O(1)。

//我的思路:
首先:我们应该知道的是,我们要求:
左边元素个数 = (m + n + 1) // 2
左边所有数 ≤ 右边所有数;
这样奇数情况下中位数一定在左边,偶数情况下中位数取决于该位置元素左右的值。
因此,我们尝试找出应该从num1左侧找出多少个元素放左边,就可以算出nums2中多少个左侧元素放左边,就能算出中位数的值了;
那么我们判断此次划分是否恰当,就要看左侧最大值是否小于右侧最小值,那么,这种情况下,由于数组原先是严格递增的,那么我们知道一定有:
nums1[left1]<nums1[right1];
nums2[left2]<nums1[right2];
因此我们只需要比较:
nums1[left1]<=nums2[right2];
nums2[left2]<=nums1[right1];
是否成立即可;
如果不成立,比如
left1 > right2,说明,nums1的i(当前分割nums1的位置)过于往右了,需要减少,hi = i -1;
else left2 > right1,说明,nums1的i(当前分割nums1的位置)过于往左了,需要增加,lo = i+1;

算法答案:

public:
    #define INT_MIN -10000000
    #define INT_MAX 10000000
    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
        if (nums1.size() > nums2.size())
            swap(nums1, nums2); // 为什么一定要加这个?因为需要让i = 0的时候j = (m+n+1)/2 < n,也就是m < n;
        int m = nums1.size(),n = nums2.size();
        int left1 = INT_MIN, right1 = INT_MAX;
        int left2 = INT_MIN, right2 = INT_MAX;
        int lo = 0,hi = m;//hi=m意味着能让i取[0,m]区间,i = m意味着把nums1中元素全部放入左边;
        while(lo <= hi){
            int i = (lo+hi)/2;
            int j = (m+n+1)/2-i;
            if(i > 0)left1 = nums1[i - 1];else left1 = INT_MIN;
            if(i < m)right1 = nums1[i];else right1 = INT_MAX;
            if(j > 0)left2 = nums2[j-1];else left2 = INT_MIN;
            if(j < n)right2 = nums2[j];else right2 = INT_MAX;
            if(left1 <= right2 && left2 <= right1){
                if((m+n)%2 == 0){
                    return (max(left1,left2)+min(right1,right2))/2.0;
                }else{
                    return max(left1,left2);
                }
            }else if(left1 > right2){
                hi = i -1;
            }else{
                lo = i+1;
            }
        }
        return -1;
    }
};
posted @ 2026-07-27 21:50  q_z_chen  阅读(11)  评论(0)    收藏  举报