zoukankan      html  css  js  c++  java
  • LeetCode 4.寻找两个正序数组的中位数

    题目:


    给定两个大小为 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


    思路:

    思路:

    看示例得知,中位数的概念是奇数情况:为中间位;偶数情况为中间2位之和平均数

    python 中的% 和/ 的意思

    %取余数

    /取整数,

    本题的难点是限制时间复杂度为 O(log(m + n))

    方法一:

    一般解法,先不考虑时间复杂度要求,如果除法运算想保留小数点,被除数保留小数点即可,这样就会有完整结果。

    class Solution(object):
        def findMedianSortedArrays(self, nums1, nums2):
            """
            :type nums1: List[int]
            :type nums2: List[int]
            :rtype: float
            """
            tmp = []
            result = 0
            for i in nums1:
                tmp.append(i)
            for n in nums2:
                tmp.append(n)
            tmp.sort() #排序
            p = len(tmp)%2
            if p ==0: #偶数
                result = (tmp[len(tmp)/2-1]+tmp[len(tmp)/2])*/2.0 
            else: #奇数
                result = tmp[(len(tmp)+1)/2-1]
            return result
    
    

    执行用时 :48 ms, 在所有 Python 提交中击败了76.69%的用户

    内存消耗 :12.8 MB, 在所有 Python 提交中击败了11.11%的用户

    或者是选用合并再sort,代码更简洁些。

    class Solution(object):
        def findMedianSortedArrays(self, nums1, nums2):
            """
            :type nums1: List[int]
            :type nums2: List[int]
            :rtype: float
            """
            nums = nums1 + nums2
            nums.sort()
            count = len(nums)
            if count % 2 == 0:
                return (nums[int(count / 2 - 1)] + nums[int(count / 2)]) / 2.0
            else:
                return nums[int(count / 2)]
    

    方法二:

    想不出来关于时间复杂度的方法实现,这里摘自一个题解大大的思路(作者:louis-38),我觉得很棒,从另一个角度分析了二分法

    在统计中,中位数被用来:

    将一个集合划分为两个长度相等的子集,其中一个子集中的元素总是大于或等于另一个子集中的元素。
    和中位数无关的元素
    

    假设一个集合A被中位数划分成两个长度相等的子集A1和A2,A1中的元素总是小于或等于A2中的元素。

    如上图所示的集合A,其中位数是4.5。

    当我们同时从A1和A2中移除任意n个元素(不移除A1最大元素和A2最小元素),集合A的中位数是不变的。

    即我们移除A1中的1,2,3这三个元素中的任意n个,同时移除A2中的6,7,8这三个元素的任意n个,集合A的中位数还是4.5。
    所以我称1,2,3,6,7,8这几个元素是和中位数无关的元素。即对称地移除他们时,对集合的中位数没有影响。

    元素个数为奇数的情况稍有不同,但是也是类似的。

    中位数的位置
    回到这个题目上。

    基于上述的原理,我们是否可以通过排除和中位数无关的元素,最终得到中位数?

    此解法的重点是:要知道中位数的位置。(这不是扯淡吗,我知道中位数的位置直接返回不就完了。- -!)

    我们这里不是求中位数的确切位置,而是求中位数的大概位置。

    假设题目给出的两个有序集合为B1和B2,他们的长度为m和n。
    设 midM = (m-1)/2(暂不考虑m=0的边界情况)
    设 midN = (n-1)/2
    如果n%2=0:
    设midNP = midN + 1
    否则midNP = midN

    那么这两个集合的并集的中位数的值median,必定是:2<=median<=7。否则median无法将B1和B2的并集切分为等长的两部分。(证明过程就不给出了,数学比较差,但是代码运行结果证明这个推论是正确的。)

    所以,根据上述推论可以得出,1,8是和中位数无关的元素。

    消除元素1和8从而得到更短的两个集合,如下图。其并集中位数不变,还是4.5。

    接下来重复以上的步骤。由于每次排除元素的数量是较短集合的一半元素,所以经过log(min(m,n))次迭代之后,到达边界条件。

    过程如动图:

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

    边界条件处理:

    最终到达边界的情况下,nums1的长度可能是1或者2。对于这两种情况,我们只需拿nums1剩下的数和nums2中间几位数进行排序,就能得到中位数。
    由于进行排序的数组长度最长是6,故其时间复杂度为O(1)。

    class Solution(object):
        def findMedianSortedArrays(self, nums1, nums2):
            """
            :type nums1: List[int]
            :type nums2: List[int]
            :rtype: float
            """
            m = len(nums1)
            n = len(nums2)
            if n < m:
                temp = nums1
                nums1 = nums2
                nums2 = temp
                m = len(nums1)
                n = len(nums2)
    
            mid_m = (m - 1) / 2
            mid_n = (n - 1) / 2
    
            if m == 0:
                return nums2[mid_n] if (n % 2) == 1 else (nums2[mid_n] + nums2[mid_n + 1]) / 2.0
    
            if m == 1 or m == 2:
                if n < 3:
                    nums1.extend(nums2)
                elif n % 2 == 1:
                    nums1.extend(nums2[mid_n - 1:mid_n + 2])
                else:
                    nums1.extend(nums2[mid_n - 1:mid_n + 3])
                nums1.sort()
                m = len(nums1)
                mid_m = (m - 1) / 2
                return nums1[mid_m] if m % 2 == 1 else (nums1[mid_m] + nums1[mid_m + 1]) / 2.0
    
            mid_np = mid_n if n % 2 == 1 else mid_n + 1
    
            if nums1[mid_m] == nums2[mid_np]:
                return nums1[mid_m]
            if nums1[mid_m] < nums2[mid_np]:
                return self.findMedianSortedArrays(nums1[mid_m:],nums2[:n-mid_m])
            return self.findMedianSortedArrays(nums1[:m-mid_m], nums2[mid_m:])
    

    下面是依据官方题解简化后的代码,比较简洁,但是理解的话需要参考下官方的思路

    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(object):
        def findMedianSortedArrays(self, nums1, nums2):
            """
            :type nums1: List[int]
            :type nums2: List[int]
            :rtype: float
            """
            def getkth(k, l1, l2):  
                if l1 == m: return nums2[l2 + k - 1]
                if l2 == n: return nums1[l1 + k - 1]
                if k == 1: return min(nums1[l1], nums2[l2])
                # 每次比较从开始起第 k/2-1 个数,并去掉较小的数组的前面部分 
                new_l1, new_l2 = min(l1 + k / 2, m), min(l2 + k / 2, n)
                if nums1[new_l1 - 1] < nums2[new_l2 - 1]:
                    return getkth(k - new_l1 + l1, new_l1, l2)
                else:
                    return getkth(k - new_l2 + l2, l1, new_l2)
            
            m, n, s = len(nums1), len(nums2), len(nums1) + len(nums2)
            if s % 2: 
                return getkth((s + 1) / 2, 0, 0) 
            return (getkth(s / 2, 0, 0) + getkth(s / 2 + 1, 0, 0)) / 2.0
    
  • 相关阅读:
    安装VC6.0遇到的问题
    开发、测试环境
    OPENGL绘制文字
    C++实现文件关联
    MFC多国语言——配置文件
    MFC 资源记录
    如何解决——汉化英文界面出现乱码
    项目配置——添加第三方资源
    队列&生产者消费者模型
    抢票小程序
  • 原文地址:https://www.cnblogs.com/xiaoqiangink/p/12920570.html
Copyright © 2011-2022 走看看