Leetcode日练笔记12 #287 #4 Find the Duplicate Number (Medium) & Median of Two Sorted Array (Hard)

#287 Find the Duplicate Number

Given an array of integers nums containing n + 1 integers where each integer is in the range [1, n] inclusive.

There is only one repeated number in nums, return this repeated number.

You must solve the problem without modifying the array nums and uses only constant extra space.

解题思路:

用for循环看元素是否出现过,出现过输出,没出现过存在count里。

class Solution:
    def findDuplicate(self, nums: List[int]) -> int:
        count = {}
        for num in nums:
            if num in count:
                return num
            else:
                count[num] = 1
        return None

runtime:

556 smaple solution:

一样?好吧……

再想了一下有没有可能用二分搜索。因为sorted()是会建立一个新的list,不会改变到原有的nums。

就再sorted之后,看中值与头值的差是否等于中idx与头idx的差。如果相等,那么说明重复值在右边。反之则反。

然后这里是用到第三类的binary template。即筛选到最后剩两个值,就是重复的值。然后随意输出一个。

class Solution:
    def findDuplicate(self, nums: List[int]) -> int:
        n = sorted(nums)
        l, r = 0, len(n)-1
        while l+1 < r:
            m = (l+r)//2
            if n[m]-n[l] < m-l:
                r = m
            else:
                l = m 
        return n[l]

runtime: 

但sorted的过程,time complexity就是O(nlogn)了。不再是题目要求的linear,就是O(n)。同时也违反了题目的要求,虽然原list没变。但是涉及到了rearrange这步操作。所以也不行……

而且这个解法默认了题目给的list一定是consecutive的,虽然accepted了,但是那个二分搜索的依据就不成立了。

值得反复看官方solution的一道题。目前水平不够,等能轻松刷easy的题时,再琢磨研究medium的更多解法思路。(居然有龟兔赛跑的思路……

#287  Median of Two Sorted Arrays (Hard)

Given two sorted arrays nums1 and nums2 of size m and n respectively, return the median of the two sorted arrays.

The overall run time complexity should be O(log (m+n)).

解题思路:

本来这题不想做的……看到Hard就觉得自己现在应该做不了。

特别是限定条件是time complexity是O(log(m+n))。

不知道怎么能用二分搜索做出来。

硬着头皮先忽略这个限定条件看照自己的理解能不能解出来。

先merge两个list再sorted。然后看长度是单还是双,单就取中值,双就取中两值的平均数。

class Solution:
    def findMedianSortedArrays(self, nums1: List[int], nums2: List[int]) -> float:
        merged = sorted(nums1 + nums2)
        if len(merged) % 2:
            return merged[(len(merged)+1)//2-1]
        else:
            return (merged[len(merged)//2-1]+merged[len(merged)//2])/2

没想到runtime还行:

(ps 从这里开始我不确定这个runtime还是代表time complexity了……因为这个解法的time complexity应该是O(m+n)。

然后看64ms的solution:

比我idx的计算简洁(是我想复杂了),大体思路一样。 

还有一种72ms解法没看懂:

class Solution:
     def findMedianSortedArrays(self, nums1: List[int], nums2: List[int]) -> float:
         a = [float("-inf")] + nums1 + [float("inf")]
         b = [float("-inf")] + nums2 + [float("inf")]
        
         if len(a) > len(b):
             a, b = b, a
        
         total = len(a) + len(b)
         half = total // 2
         l, r = 0, len(a) - 1
        
         while l <= r:
             i = (l + r) // 2
             j = half - i - 2 # avoid out of index error
            
             a_left, a_right = a[i], a[i+1]
             b_left, b_right = b[j], b[j+1]
            
             if a_left <= b_right and b_left <= a_right:
                 if total % 2 == 1: # odd
                     return min(a_right, b_right)
                 # even
                 return (max(a_left, b_left) + min(a_right, b_right)) / 2
            
             elif a_left > b_right:
                 r = i - 1
             else:
                 l = i + 1
       

看了一下discussion里推荐的视频:

https://www.youtube.com/watch?v=LPFhl65R7ww&t=1396shttps://www.youtube.com/watch?v=LPFhl65R7ww&t=1396s看完懂了。主要是切割了个list。然后把左右指针放在短list的头尾,找切割位置。

切割位置的判断标准是,短切左边最后一个值必须小于长切右边最小一个值,以及长切左边最后一个值小于短切右边最小一个值。这样才能保证切割完之后,左边长短list合并之后的每一个元素都小于右边的每一个元素。(只要决定了短list的切割位置,长lis的切割位置自然也就定了。因为median的性质决定的,必须分开的两边元素个数相等)。只有找到了切割位置之后,再从左边挑最大值(长list短list的左边末尾两个值)和右边最小值(长list短list的右边最头的两个值),取平均数,才是题目要求的median。

没有找到切割位置的时候,如果短左尾大于长右头,那么说明切割位置还要再往左找。移右标。反之则反。

对了,要确保寻找切割位置的list是短list。

补上造这个思路重写一遍的结果。

class Solution:
    def findMedianSortedArrays(self, nums1: List[int], nums2: List[int]) -> float:
        if len(nums1) > len(nums2):
            nums1, nums2 = nums2, nums1
            
        nums1, nums2 = [float('-inf')] + nums1 + [float('inf')], [float('-inf')] + nums2 + [float('inf')]
            
        l, r = 0, len(nums1)-1 
        half = len(nums1 + nums2) // 2 - 2  # the number of elements each side should have
        
        while l <= r:
            nums1_l = (l+r)//2  # nums1, short list, cutting point
            nums2_l = half - nums1_l  # nums2, long list, cutting point
            
            sl, sr, ll, lr = nums1[nums1_l], nums1[nums1_l+1], nums2[nums2_l], nums2[nums2_l+1]
            if sl <= lr and ll <= sr:
                if len(nums1+nums2) % 2:
                    return min(lr, sr)
                else:
                    return (max(sl, ll) + min(lr, sr))/2
            elif sl > lr:
                r = nums1_l - 1
            else:
                l = nums1_l + 1
        

尤其要注意的是,两个list前后加了负无穷和正无穷之后(为的是解决当切割线位于头尾时那个判断切割线位置对不对的条件会出现idx出界的问题),half记得要多减去2,两个负无穷占了两个位。否则长list的切割线位置会不对, 偏右两位。(还是感觉那里有点别扭,回头再想想)

 runtime:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值