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

题目描述:

给定两个大小分别为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。请你找出并返回这两个正序数组的 中位数 。

算法的时间复杂度应该为 O(log (m+n)) 。

示例 1:

输入:nums1 = [1,3], nums2 = [2]
输出:2.00000
解释:合并数组 = [1,2,3] ,中位数 2
示例 2:

输入:nums1 = [1,2], nums2 = [3,4]
输出:2.50000
解释:合并数组 = [1,2,3,4] ,中位数 (2 + 3) / 2 = 2.5
 

提示:

nums1.length == m
nums2.length == n
0 <= m <= 1000
0 <= n <= 1000
1 <= m + n <= 2000
-106 <= nums1[i], nums2[i] <= 106

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/median-of-two-sorted-arrays
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

分析:看到了log(m+n)的复杂度,我就知道肯定用二分,但是怎么用死活想不出来,只能去看题解了,唉~

        看完题解更加怀疑自己的脑子是不是比别人差很多,这方法让我想,一辈子都想不出来。。。

        不多分析了,附上题解吧

 

来源:https://leetcode-cn.com/problems/median-of-two-sorted-arrays/solution/xun-zhao-liang-ge-you-xu-shu-zu-de-zhong-wei-s-114/ 

我根据题解写的代码:

class Solution {
public:
    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
        // 计算nums1和nums2的长度n和m
        int n=nums1.size();
        int m=nums2.size();
        // 如果二者长度为0或者其中一个长度为1,直接返回即可
        if(m==0&&n==0) return 0;
        if(m==0&n==1) return nums1[0];
        if(m==1&n==0) return nums2[0];
        // res为最终结果
        double res;
        // 如果m+n为奇数 只需要找到第(m+n)/2个数,该数即为中位数
        if((m+n)%2==1)
        res=findKthnumber(nums1,nums2,(m+n)/2+1);
        else
        {
            // 如果m+n为偶数 需要找到第(m+n)/2个数和第(m+n)/2+1个数,两个数的平均数是中位数
            int temp1=findKthnumber(nums1,nums2,(m+n)/2);
            
            int temp2=findKthnumber(nums1,nums2,(m+n)/2+1);
            res=(temp1+temp2)*1.0/2;
        }
        return res;
    }
    // 该函数的功能是在nums1和nums2中寻找第k大的数
    int findKthnumber(vector<int>& nums1, vector<int>& nums2,int k)
    {
        // 计算两个数组长度
        int n=nums1.size();
        int m=nums2.size();
        // 如果某个数组长度为0,那么直接返回另一个数字的第k个数即可
        if(n==0)
        return nums2[k-1];
        if(m==0)
        return nums1[k-1];
        // 计算总长度len
        int len=m+n;
        // l1代表nums1数组的左边界 r1代表右边界
        int l1=0,r1=n-1;
        // l2代表nums2数组的左边界 r2代表右边界
        int l2=0,r2=m-1;
        // s1代表在l1和r1中第k/2-1个数的位置
        int s1=0,s2=0;
        while(1)
        {
            // 如果nums1越界,那么返回nums2的第k个数
            if(l1>r1) return nums2[l2+k-1];
            // 如果nums2越界,那么返回nums1的第k个数
            if(l2>r2) return nums1[l1+k-1];
            // 如果k为1,说明下个数就是要寻找的第k个数
            if(k==1)
            {
                // 如果nums1和nums2均未越界,返回nums1第一个数和nums2第一个数中的最小值
                if(l1<=r1&&l2<=r2)
                return nums1[l1]<nums2[l2]?nums1[l1]:nums2[l2];
                // 如果nums1没有越界,而nums2越界了,返回nums1的第一个数
                else if(l1<=r1&&l2>r2)
                return nums1[l1];
                // 如果nums2没有越界,而nums1越界了,返回nums2的第一个数
                else if(l1>r1&&l2<=r2)
                return nums2[l2];
                    
                
            }
            // k1和k2为nums1和nums2中的第k/2-1个数,用来比较大小,并且移动l1和l2
            int k1=k/2-1;
            int k2=k/2-1;
            // s1代表的是从l1开始,第k1个数的下标
            s1=l1+k1;
            // s2代表的是从l2开始,第k2个数的下标
            s2=l2+k2;
            // 如果s1或者s2越界,令s1、s2等于右边界r1、r2
            if(s1>r1) s1=r1;
            if(s2>r2) s2=r2;
            // temp1和temp2为nums1和nums2中的第k/2-1个数
            int temp1=nums1[s1];
            int temp2=nums2[s2];
            // 比较temp1和temp2
            if(temp1<=temp2)
            {
                // 如果temp1<=temp2,说明在nums1中,从l1~l1+k/2-1,中间的所有数均可以排除

                // 如果l1的第k/2-1个数越界,那么k减少的值为r1-l1+1
                if(l1+k1>r1)
                k=k-(r1-l1+1);
                // 如果l1的第k/2-1个数没有越界,那么k减少的值为k/2
                else
                k-=k/2;
                // 并且重新规划nums1的左边界l1,为s1的下一位
                l1=s1+1;

            }
            else
            {
                // 如果temp1>temp2,说明在nums2中,从l2~l2+k/2-1,中间的所有数均可以排除

                // 如果l2的第k/2-1个数越界,那么k减少的值为r2-l2+1
                if(l2+k2>r2)
                k=k-(r2-l2+1);
                // 如果l2的第k/2-1个数没有越界,那么k减少的值为k/2
                else
                k-=k/2;
                // 并且重新规划nums2的左边界l2,为s2的下一位
                l2=s2+1;


            }
        }
        
        return 0;
    }
};

 

 注意几点

1.findKthnumber函数的功能最好简单一些,寻找第k个数,不要试图m+n为奇数的时候返回中位数,m+n为偶数的时候也返回中位数,不然会死得很惨。。。。非常麻烦。像题解那样在findMedianSortedArrays函数里把奇偶拆开讨论是非常明智的选择

2.可以can k参考官方题解中的这个例子,如果这个例子分析明白了,这道题就差不多了(不过偶数的稍微要复杂一些)

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
寻找两个正序数组中位数可以使用归并的方式,合并两个有序数组,得到一个大的有序数组。然后找到大的有序数组的中间位置的元素,即为中位数。另一种方法是使用双指针的方式,维护两个指针,初始时分别指向两个数组的下标0的位置。每次将指向较小值的指针后移一位(如果一个指针已经到达数组末尾,则只需要移动另一个数组的指针),直到到达中位数的位置。这样可以在O(log(m+n))的时间复杂度内找到中位数。 具体步骤如下: 1. 初始化指针p1和p2分别指向两个数组的起始位置0。 2. 判断两个指针所指的元素大小,较小的元素所在的指针后移一位,直到其中一个指针到达数组末尾。 3. 若两个数组的长度之和为奇数,那么中位数即为当前指针指向的元素; 若两个数组的长度之和为偶数,那么中位数为当前指针指向的元素与其下一个元素的平均值。 4. 返回中位数作为结果。 需要注意的是,为了保证时间复杂度为O(log(m+n)),在每次移动指针时,应该移动的步数应该是当前指针所在数组长度的一半,即k/2,其中k为两个数组的长度之和。 以下是一个示例代码,用于说明上述方法的实现: ``` int findMedianSortedArrays(int[] nums1, int[] nums2) { int m = nums1.length; int n = nums2.length; int total = m + n; int middle = total / 2; int p1 = 0, p2 = 0; int prev = 0, curr = 0; for (int i = 0; i <= middle; i++) { prev = curr; if (p1 < m && (p2 >= n || nums1[p1 < nums2[p2])) { curr = nums1[p1++]; } else { curr = nums2[p2++]; } } if (total % 2 == 0) { return (prev + curr) / 2; } else { return curr; } } ``` 该方法可以在O(log(m+n))的时间复杂度内找到两个正序数组中位数。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [算法寻找两个正序数组中位数。](https://blog.csdn.net/en_joker/article/details/107179641)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* [寻找两个正序数组中位数](https://blog.csdn.net/wulila/article/details/124483500)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值