• LeetCode(C++)刷题计划:4-寻找两个有序数组的中位数


    4-寻找两个有序数组的中位数

    @Author:CSU张扬
    @Email:csuzhangyang@gmail.com or csuzhangyang@qq.com

    1. 题目

    给定两个大小为 m 和 n 的有序数组 nums1 和 nums2。
    请你找出这两个有序数组的中位数,并且要求算法的时间复杂度为 O(log(m + n))
    你可以假设 nums1 和 nums2 不会同时为空。

    示例 1:  
    nums1 = [1, 3]
    nums2 = [2]  
    则中位数是 2.0
    
    示例 2:  
    nums1 = [1, 2]
    nums2 = [3, 4]  
    则中位数是 (2 + 3)/2 = 2.5
    

    来源:力扣(LeetCode)
    链接:https://leetcode-cn.com/problems/median-of-two-sorted-arrays

    2. 解法

    2.1 解法一:归并排序

    将两个数组进行归并排序,从小到大依次存入vector中。取中位数即可。

    执行用时: 24 ms, 在所有 cpp 提交中击败了76.59%的用户
    内存消耗: 11.3 MB, 在所有 cpp 提交中击败了72.07%的用户

    class Solution {
    public:
        double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
            vector<double> v;
            int i1 = 0, i2 = 0;
            while(i1 != nums1.size() && i2 != nums2.size()) {
                if (nums1[i1] <= nums2[i2]) {
                    v.push_back(nums1[i1]);
                    ++ i1;
                }
                else {
                    v.push_back(nums2[i2]);
                    ++ i2;
                }
            }
            if (i1 != nums1.size()) {
                for (i1; i1 < nums1.size(); ++ i1)
                    v.push_back(nums1[i1]);
            }
            else {
                for (i2; i2 < nums2.size(); ++ i2)
                    v.push_back(nums2[i2]);
            }
            if ((nums1.size() + nums2.size())%2)
                return v[(nums1.size() + nums2.size())/2];
            else
                return (v[(nums1.size() + nums2.size())/2] + v[(nums1.size() + nums2.size())/2 - 1]) / 2;
        }
    };
    
    

    2.2 解法二:递归二分法

    1. 题目要求复杂度为O(log(m+n)),所以肯定要使用二分法。
    2. 同时我们可以将题目转化为求第k个最小数,即k=(m+n)/2
    3. 对两个序列的第k/2个值进行比较,若nums1[k] > nums2[k],则nums2的前k/2个数中肯定没有我们要求的第k个最小数。我们将前nums2中前k/2个数剔除。
    4. 对新序列的nums1和nums2继续进行求第k1(此时k1=k-k/2)个最小数。
    5. 当然我们会遇到一些特殊情况,比如某序列太短,例如(nums1.size() < nums2.size()),此时肯定是nums1可能会出现没有第k/2个数(nums2不会出现这种情况),此时替换为该序列的最后一个值即可。为了简化代码,如果(nums1.size() > nums2.size()),我们就递归函数getKthElem重新传参,将参数调换(即nums1 <-> nums2等)。
    6. 由此产生的另外一种需要考虑的问题是,步骤4中的k1=k-k/2是否完全正确?我们每次剔除的元素该不该是k/2个? 当然不一定,因为有的序列可能没有那么多元素,这时我们需要令k=k-我们剔除的数,即:k=k-min(k/2,len)。在代码中我们不能将 min函数 作为一个参数,因为我们定义了int i = start1 + min(k / 2, len1) - 1, 在实际代码中我们使用(j - start2 + 1)来替代它。
    7. 那么问题来了,6中这种替换是正确的吗? 其实这种替换是无妨的,因为们剔除的元素始终小于k/2个,当len < k/2时,剔除了len个元素,我们此时只是不是严格的二分了。我们每次少剔除几个元素在逻辑上是没有问题的(当然多的话就会有问题),因为在k/2前的这几个元素肯定不是我们要找的第k个最小数
    8. 递归函数什么时候结束呢? 两种情况:一是某个序列的长度变为了0,那么我们只需在另一个序列中找第k个最小数即可;二是k=1时,此时找的是两个序列的第1个最小值,那么就是两个序列第一个元素较小的那个。

    执行用时: 8 ms, 在所有 cpp 提交中击败了99.98%的用户
    内存消耗: 9.7 MB, 在所有 cpp 提交中击败了86.47%的用户

    class Solution {
    public:
        double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
            int m = nums1.size();
            int n = nums2.size();
            int k = (m + n) / 2;
            if ((m + n) % 2)
                return getKthElem(nums1, 0, m - 1, nums2, 0, n - 1, k + 1);
            else
                return (getKthElem(nums1, 0, m - 1, nums2, 0, n - 1, k) + getKthElem(nums1, 0, m - 1, nums2, 0, n - 1, k + 1)) * 0.5;
        }
        int getKthElem(vector<int>& nums1, int start1, int end1, vector<int>& nums2, int start2, int end2, int k) {
            int len1 = end1 - start1 + 1;
            int len2 = end2 - start2 + 1;
            if (len1 > len2)
                return getKthElem(nums2, start2, end2, nums1, start1, end1, k);
            if (len1 == 0)
                return nums2[start2 + k - 1];
            if (k == 1)
                return min(nums1[start1], nums2[start2]);
            int i = start1 + min(k / 2, len1) - 1;
            int j = start2 + min(k / 2, len2) - 1;
            if (nums1[i] > nums2[j])
                // 注意:(j - start2 + 1) = min(k / 2, len1)
                // 但是我们不能将min函数作为参数。
                return getKthElem(nums1, start1, end1, nums2, j + 1, end2, k - (j - start2 + 1));
            else
                return getKthElem(nums1, i + 1, end1, nums2, start2, end2, k - (i - start1 + 1));
        }
        // 参考https://leetcode-cn.com/problems/median-of-two-sorted-arrays/solution/xiang-xi-tong-su-de-si-lu-fen-xi-duo-jie-fa-by-w-2/
    };
    
  • 相关阅读:
    Pytest框架之命令行参数2
    Pytest框架之命令行参数1
    [编程题] 二维数组中的查找
    [编程题]二叉树镜像
    补充基础:栈与队列模型
    6641. 【GDOI20205.20模拟】Sequence
    瞎讲:任意模数MTT
    瞎讲:FFT三次变二次优化
    小米oj 重拍数组求最大和
    小米oj 有多少个公差为2的等差数列
  • 原文地址:https://www.cnblogs.com/MagicConch/p/12179167.html
Copyright © 2020-2023  润新知