LeetCode刷题记录----4.寻找两个正序数组的中位数(Hard)

2025/9/6

题目(Hard):

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


解题思路:

我们常规可以想到的思路是把这两个数组合并为一个有序数组,然后直接再获取它的中位数值。但是这题要求时间复杂度为O(log(m + n)),因此多少有点暗示我们用二分查找的思路来处理。

考虑二分查找的本质是根据条件,更新边界,查找到一个对应的目标值。在这一题中,我们可以知道如果合并后数组长度为奇数则中位数是第k个元素,偶数则是第k个和第k+1个元素的平均值。因此我们可以把他转化为在这两个数组中去查找第k小的元素(当然实现了这个方法后同样可以查找第k+1小的元素)

因此整体的思路是

①实现一个可以传入两个数组,并找到其中第k小的元素的函数

②在主方法中根据两个数组长度和的奇偶性来获取对应需要的元素

具体如下:

double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
        //转化为查找到第K小的数
        //通过二分查找两个数组的值的比较来排除不可能的数以缩小检索范围

        int totalLength = nums1.size() + nums2.size() ;
        int k = (totalLength + 1)/2;

        //根据总长度是奇数还是偶数选择不同的返回值
        if(totalLength % 2 != 0){
            return getMinKNum(nums1, nums2, k);
        }
        else{
            return (getMinKNum(nums1,nums2, k) + getMinKNum(nums1, nums2, k+1))/2.0;
        }
    }

那接下来我们只要考虑如何实现这个找到第k小的元素的函数getMinKNum(nums1, nums2, k);即可

这里我们可以给两个数组分别分配指针index1,index2。它们表示当前还没被排除的元素的起始位置,即[index1, nums1.size()] , [index2, nums2.size()] 分别表示nums1和nums2中可能存在中位数元素的区间 

先考虑边界条件:

①如果index1 == nums1.size(),此时nums1被排除干净,中位数就是nums2中第k小的元素

②如果index2 == nums2.size(),此时num2被排除干净,中位数就是nums1中第k小的元素

③如果k == 1 ,那就是当前nums1和nums2中最小的元素(第一小元素嘛)

之后考虑常规的情况:

①每次更新,我们用k/2 - 1来判断边界,所以新的区间值是index1 + k/2 -1,但是如果这个值越界了的话,那他就设置回当前数组的末尾位置。总之每次更新的时候是判断[index1, index1+k/2-1]这个范围的元素是否能够被排除

②排除的条件是当nums1[index1+k/2-1] <= nums2[index + k/2 -1]的时候。此时nums1的该位置不可能是第k小的数,最多是第k-1小的数,所以他被排除。

③每次排除了之后,更新k的值和index1的值。

以上情况对nums2和他对应的index2的更新情况同理

LeetCode题解中提供了一个详细的例子来描述上述的方法

具体如下:

//用于查找到两个数组的元素中第K小的元素
    int getMinKNum(vector<int>& nums1, vector<int>& nums2, int k){
        int n = nums1.size(), m = nums2.size();
        int index1 = 0, index2 = 0; //当前在两个数组中的指针位置

        while(true){
            //处理边界条件
            if(index1 == n){
                //此时nums1中元素被全部排除
                return nums2[index2 + k - 1];
            }
            if(index2 == m){
                //此时nums2中元素被全部排除
                return nums1[index1 + k - 1];
            }
            if(k == 1){
                //此时返回数组当前指向的两个元素中比较小的一个即为第1个小的值
                return min(nums1[index1], nums2[index2]);
            }

            //按常规情况处理,判断哪个范围的元素需要被排除
            int newIndex1 = min(index1 + k/2 - 1, n-1);
            int newIndex2 = min(index2 + k/2 - 1, m-1);
            int midNum1 = nums1[newIndex1];
            int midNum2 = nums2[newIndex2];
            
            if(midNum1 <= midNum2){
                //此时说明[index1, newIndex1]范围的元素都被排除
                k -= newIndex1 - index1 + 1;
                index1 = newIndex1 + 1;
            }
            else{
                //此时说明[index2, newIndex2]范围内元素都被排除
                k -= newIndex2 - index2 + 1;
                index2 = newIndex2 + 1;
            }    
        }
    }

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

空间复杂度:O(1)


总结:

①对于时间复杂度为O(log)这样要求的情况,多半是暗示使用二分查找。

②对于这题,最主要的思路是把问题转化为查找第K小的元素。从而让我们能考虑更细节的东西,而不用老是在意数组长度的奇偶性。

③主要是利用了[0,k/2-1]和[0,k/2-1]在中前面元素的个数最多只能达到k-2,从而nums1[k/2-1]和nums[k/2-1]比较小的那一个它一定不是中位数,来达到把它[0, k/2-1]这部分的元素都排除了的方式来缩小搜素范围。同时利用指针来模拟被排除的这个过程(指针向前移动作为起始位置)

③在排除了不需要的元素之后,同样需要更新k的值

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

萘柰奈

谢谢老板喵

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值