zoukankan      html  css  js  c++  java
  • Leetcode(4)-两个排序数组的中位数

     
    给定两个大小为 m 和 n 的有序数组 nums1 和 nums2 。

    请找出这两个有序数组的中位数。要求算法的时间复杂度为 O(log (m+n)) 。

    示例 1:

    nums1 = [1, 3]
    nums2 = [2]
    
    中位数是 2.0
    

    示例 2:

    nums1 = [1, 2]
    nums2 = [3, 4]
    
    中位数是 (2 + 3)/2 = 2.5

    自己的思路:既然两个数组都是有序的,我可以将两个数组合并成一个数组,依然有序,然后根据奇偶来判断出中位数。

    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2)
    {
        vector<int>nums;
        int i=0,j=0;
        while(i<nums1.size() && j<nums2.size())
        {
            if(nums1[i]<nums2[j])
            {
                nums.push_back(nums1[i++]);
            }
            else
            {
                nums.push_back(nums2[j++]);
            }
        }
        if(i==nums1.size())
        {
            while(j<nums2.size())
                nums.push_back(nums2[j++]);
        }
        else if(j==nums2.size())
        {
            while(i<nums1.size())
                nums.push_back(nums1[i++]);
        }
        if(nums.size()%2)
            return nums[nums.size()/2];
        else
        {
            return (double)(nums[nums.size()/2]+nums[nums.size()/2-1])/2;
        }
    }

    但是这个算法很明显的不符合题目中的算法复杂度的要求,所以整理了他人的优秀实现方法

    (1)中位数,其实就是找到第k个大小的元素的特例。在单数组中实现方式简单,关键是如何在两个数组中找到第k大的元素。

    难就难在要在两个未合并的有序数组之间使用二分法,这里我们需要定义一个函数来找到第K个元素,由于两个数组长度之和的奇偶不确定,因此需要分情况来讨论,对于奇数的情况,直接找到最中间的数即可,偶数的话需要求最中间两个数的平均值。下面重点来看如何实现找到第K个元素,首先我们需要让数组1的长度小于或等于数组2的长度,那么我们只需判断如果数组1的长度大于数组2的长度的话,交换两个数组即可,然后我们要判断小的数组是否为空,为空的话,直接在另一个数组找第K个即可。还有一种情况是当K = 1时,表示我们要找第一个元素,只要比较两个数组的第一个元素,返回较小的那个即可。

    首先假设数组A和B的元素个数都大于k/2,我们比较A[k/2-1]和B[k/2-1]两个元素,这两个元素分别表示A的第k/2小的元素和B的第k/2小的元素。这两个元素比较共有三种情况:>、<和=。如果A[k/2-1]大于B[k/2-1],则A[k/2-1]小于合并之后的第k小值。

    证明也很简单,可以采用反证法。假设A[k/2-1]大于合并之后的第k小值,我们不妨假定其为第(k+1)小值。由于A[k/2-1]小于B[k/2-1],所以B[k/2-1]至少是第(k+2)小值。但实际上,在A中至多存在k/2-1个元素小于A[k/2-1],B中也至多存在k/2-1个元素小于A[k/2-1],所以小于A[k/2-1]的元素个数至多有k/2+ k/2-2,小于k,这与A[k/2-1]是第(k+1)的数矛盾。

    同理当A[k / 2 - 1] > B[k / 2 -1]时存在类似的结论

    当A[k / 2 - 1] = B[k / 2 -1]时,表示,在在A的k/2 -1之前已经有k/2 -1和数小于A[k / 2 -1],同理在B 之前也是一样的,所以此时已经找到了第k小的数,即这个相等的元素。

        double findKth(vector<int> &nums1, int i, vector<int> &nums2, int j, int k)
        {
            // 首先需要让数组1的长度小于或等于数组2的长度
            if (nums1.size() - i > nums2.size() - j) {
                return findKth(nums2, j, nums1, i, k);
            }
            // 判断小的数组是否为空,为空的话,直接在另一个数组找第K个即可
            if (nums1.size() == i) {
                return nums2[j + k - 1];
            }
            // 当K = 1时,表示我们要找第一个元素,只要比较两个数组的第一个元素,返回较小的那个即可
            if (k == 1) {
                return min(nums1[i], nums2[j]);
            }
            int pa = min(i + k / 2, int(nums1.size())), pb = j + k - pa + i;
            
            if (nums1[pa - 1] < nums2[pb - 1]) {
                return findKth(nums1, pa, nums2, j, k - pa + i);
            }
            else if (nums1[pa - 1] > nums2[pb - 1]) {
                return findKth(nums1, i, nums2, pb, k - pb + j);
            }
            else {
                return nums1[pa - 1];
            }
        }
         double findMedianSortedArrays(vector<int> A, vector<int> B) {
            int sizeA = A.size(), sizeB = B.size();
            if (sizeA <= 0 && sizeB <= 0) {
                return 0;
            }
            int total = sizeA + sizeB;
            if (total % 2 == 1) {
                return findKth(A, 0, B, 0, total / 2 + 1);
            }
            else {
                return (findKth(A, 0, B, 0, total / 2) + findKth(A, 0, B, 0, total / 2 + 1)) / 2;
            }
        }

    这里比较难理解的点是判断(nums1[pa - 1] < nums2[pb - 1])之后执行了return findKth(nums1, pa, nums2, j, k - pa + i);其实这个操作是因为目前nums1的分界线的值小于nums2分界线的值,那么证明nums1分界线以及前面的值都小于合并后的第k的值,也就是中位数。那么我们可以从这里开始,继续寻找第k-(pa-i)的值,直到两个值相等为止。

    (2)分治法。

    https://hk029.gitbooks.io/leetbook/%E5%88%86%E6%B2%BB/004.%20Median%20of%20Two%20Sorted%20Arrays[H]/004.%20Median%20of%20Two%20Sorted%20Arrays[H].html

        double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
            if(nums1.size() == 0)
                return MedofArray(nums2);
            if(nums2.size() == 0)
                return MedofArray(nums1);
            int n = nums1.size();
            int m = nums2.size();
            if(n > m)   //保证数组1一定最短
                return findMedianSortedArrays(nums2,nums1);
            int L1,L2,R1,R2,c1,c2,lo = 0, hi = 2*n;  //我们目前是虚拟加了'#'所以数组1是2*n+1长度
            while(lo <= hi)   //二分
            {
                c1 = (lo+hi)/2;  //c1是二分的结果
                c2 = m+n- c1;
                L1 = (c1 == 0)?INT_MIN:nums1[(c1-1)/2];   //map to original element
                R1 = (c1 == 2*n)?INT_MAX:nums1[c1/2];
                L2 = (c2 == 0)?INT_MIN:nums2[(c2-1)/2];
                R2 = (c2 == 2*m)?INT_MAX:nums2[c2/2];
    
                if(L1 > R2)
                    hi = c1-1;
                else if(L2 > R1)
                    lo = c1+1;
                else
                    break;
            }
            return (max(L1,L2)+ min(R1,R2))/2.0;
        }
        double MedofArray(vector<int>& nums)
        {
            if(nums.size() == 0)    return -1;
            return (nums[nums.size()/2]+nums[(nums.size()-1)/2])/2.0;
        }

    需要细细思考下

  • 相关阅读:
    SQL Server CheckPoint的几个误区
    MongoDB集群与LBS应用系列(一)
    也来“玩”Metro UI之磁贴
    Python基础:函数式编程
    ASP.NET MVC 用户登录Login
    巧用 .NET 中的「合并运算符」获得 URL 中的参数
    Razor.js,基于JavaScript的Razor实现
    大话数据结构-排序
    hdu 1498
    3.7 检测两个表中是否有相同的数据
  • 原文地址:https://www.cnblogs.com/mini-coconut/p/9066508.html
Copyright © 2011-2022 走看看