LeetCode-4 两组有序数组寻找中位数

本文介绍了一种寻找两个有序数组中位数的方法,包括归并和二分查找两种策略。归并策略分为真归并与伪归并,后者通过优化空间复杂度提升效率。二分查找方案则更加高效地定位目标值。

题目参见寻找两个正序数组的中位数。思路来自LeetCode的官方题解。

寻找两个正序数组的中位数 给定两个大小为 m 和 n 的正序(从小到大)数组 nums1 和nums2。请你找出并返回这两个正序数组的中位数。如果是奇数,则返回第(m+n+1)/2小的数字,如果是偶数,则返回第(m+n)/2小的数和第(m+n)/2+1小的数两数平均值。

方案一:归并。

方案1.1 真归并

由于题目中的两个数组都是有序数组,所以可以开辟一个空间大小为 m + n m+n m+n的数组,用以保存归并后的新有序数组。每次从两个数组之中选择一个较小的数字放到归并后的新数组中,最后从新数组里面挑选题目要求的中位数,并返回即可。

方案1.2 伪归并

事实上,我们并不需要真正的开辟一个空间大小为 m + n m+n m+n这么大的新数组,我们其实只需要第(m+n+1)/2大的这一个数字(如果是偶数,则需要两个)数字而已,何必要开一个完整数组用以保存呢?所以,维护一个数字,在两个数组间进行两两比较的时候,只存留两个数组间最小的那个数字,一直递增下标,直到找到第(m+n+1)/2小的数字为止即可。这样空间复杂度则降为了 O ( 1 ) O(1) O(1)级别。代码如下:

class Solution {
public:
    double findMedianSortedArrays(vector<int>& a, vector<int>& b) {
        int la = a.size();
        int lb = b.size();
        int i = 0, j = 0, id = 0, ans = 0, n = la + lb;
        if(n==1){
            for(auto t:a)return t;
            for(auto t:b)return t;
        }
        if(n%2){
            do{
                if(i<la&&j<lb){
                    if(a[i]<b[j]) ans = a[i++];
                    else ans = b[j++];
                }
                else if(i < la) ans = a[i++];
                else if(j < lb) ans = b[j++];
                id++;
            }while(id!=(n+1)/2);
            return ans;
        }
        else{
            double sum = 0;
            do{
                if(i<la&&j<lb){
                    if(a[i]<b[j]) ans = a[i++];
                    else ans = b[j++];
                }
                else if(i < la) ans = a[i++];
                else if(j < lb) ans = b[j++];
                id++;
                if(id==n/2||id==n/2+1){
                    sum += ans;
                }
            }while(id!=n/2+1);
            return sum / 2;
        }
    }
};

方案二:二分查找

更普遍而言,我们考虑寻找两个有序数组中的第K小的数字。既然是两个数组,第一个数组中的第 K 2 \frac{K}{2} 2K个数字必然大于第一个数组中前面第 K 2 − 1 \frac{K}{2}-1 2K1个数字,第二个数组中的第 K 2 \frac{K}{2} 2K个数字必然大于第二个数组中的前面第 K 2 − 1 \frac{K}{2}-1 2K1个数字;这二者中最小的数字,最多也只能是大于了前面第 K 2 − 1 + K 2 − 1 = K − 2 \frac{K}{2}-1+\frac{K}{2}-1=K-2 2K1+2K1=K2个数字(为什么是“最多”呢,因为很有可能这个数字比另一组数字中的前 K 2 − 1 \frac{K}{2}-1 2K1个数字都要小),不会是第 K K K小的数字了,需要从该数字后面进行下一次二分查找,同时也排除掉了 K 2 \frac{K}{2} 2K个数字(如果该数组中容量不足 K 2 \frac{K}{2} 2K个,则会减少掉该数组的总容量,我们记排除掉的数字为 t t t个),下次寻找的应该是第 K − t K-t Kt小的数字了。思路清楚后,编写代码即可。

class Solution {
public:
    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
        int m = nums1.size();
        int n = nums2.size();
        if ((m + n) %2 == 1){
            return getTopK((m+n)/2+1,m,n,nums1,nums2);
        }else{
            return (getTopK((m+n)/2,m,n,nums1,nums2)+getTopK((m+n)/2+1,m,n,nums1,nums2))/2.0;
        }
    }
    int getTopK(int k, int m, int n, vector<int>& nums1, vector<int>& nums2){
        int index1 = 0, index2 = 0;
        int mid_index1,nums_index1;
        int mid_index2,nums_index2;
        while(true){
            if(index1 == m){
                return nums2[index2 + k - 1];
            }
            if(index2 == n){
                return nums1[index1 + k -1];
            }
            if(k == 1){
                return min(nums1[index1],nums2[index2]);
            }
            mid_index1 = min(index1 + k / 2 - 1 , m-1);
            mid_index2 = min(index2 + k / 2 -1 ,n-1);
            nums_index1 = nums1[mid_index1];
            nums_index2 = nums2[mid_index2];
            if (nums_index1 < nums_index2){
                k -= mid_index1 - index1 + 1;
                index1 = mid_index1  +1;
            }else{
                k -= mid_index2 - index2 + 1;
                index2 = mid_index2  + 1;
            }
        }
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

ProfSnail

谢谢老哥嗷

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

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

打赏作者

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

抵扣说明:

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

余额充值