LeetCode的sum问题

这里写几个sum问题的总结。
首先是
leetcode 1:two sum
解法很简单,就是哈希表。哈希表的查找速度是O(1),当然是平均时间,最差是O(n),对应全部冲突。当然,以python的dict为例,dict是会扩容的,扩容的时候会rehash。所以这个时候的内部也会O(n)一次。anyway,不讨论这个情况的话,确实是hash最快。因此我们的实现如下:

def two sum(nums, target):
    d={}
    for i, num in enumerate(nums):
        # 需要注意的是,这里要以num作为key,index作为value,只有这样查询的时候才是O(1)
        if num in d:
            return [i, d[num]]
        # target-num作为key,所以如果in 满足的话就是当前的num和target-num了
        d[target-num]=i

leetcode 167:已经排序的two sum
这个问题有意思在,已经排好序了,那么哈希似乎显得不够灵活了,我们考虑,可以用一种叫做“双指针”的东西,双指针应用及其广,最牛逼的用法就是链表求环,堪称天人。不过这里不说那个,我们认为,首先把一个指针放在头部,另一个指向尾部,然后如果大了就尾指针向内,小了就头指针向后。也很简单,如下:

def two sum2(nums, target):
    i = 0
    j = len(nums)-1
    while i target:
            j -= 1
        else:
            i += 1

leetcode 653:wo sum, Input is a BST
这道题是给的是一颗二叉树,我们要找到target,否则返回False。做法就是dfs一棵树,而内部这个查找的过程和two sum是一样的,为了快速,我们用一个set(set和dict一样都是hashable,查找速度O(1)),这个set里保存target-root.val的值,因此便利的时候只要val in s,就说明找到了。同时,这样保存还有一个好处,如果真的只能保存一个值,那随着我们遍历,我们会不停回溯,麻烦的一笔。而如果我们把候选都放到set里,每次只需看看需要的在不在就行了。第一题之所以用dict,是因为需要同时保存标号和数值,而这个只需要标号就好,故而使用set。

def two sum4(root, target):
    s = set()
    return dfs(root, target, s)

def dfs(root, target, s):
    if not root:
        return False
    if root.val in s:
        return True
    else:
        s.add(target-root.val)
        return dfs(root.left, target, s) or dfs(root.right, target, s)

leetcode34: 在排序数组中查找元素的第一个和最后一个位置
这道题个人认为极佳,如果以后我当面试官,我一定会考这道题。这道题的时间要求是O(lg(n)),也就是说,不能有一般直接查找的操作。虽然很多提交时间很短,但是都有找到点后两侧遍历的操作,这种操作在大规模上必然会爆,能ac仅仅是lc的case太少了。
要我来解,必然只能纯查找,也就是,找到起点,找到终点。binary search的模板已经很清晰了,看起来大了就减end,小了就加start的操作似乎也不能改变,故能变的也就是==的情况了
假设寻找第一个,那么当mid

class Solution(object):
    def searchRange(self, nums, target):
        start=self.findfirst(nums,target)
        end=self.findlast(nums,target)
        return [start,end]
    def findfirst(self,nums,target):
        l=0
        r=len(nums)-1
        index=-1
        while(l<=r):
            mid=l+(r-l)//2
            if nums[mid]

明白了这个之和,代码就变得很简单,也可以把两个融合到一起,但是没有必要。
leetcode 15:三数之和
这个题其实也是双指针,不过因为有3个数,所以需要第3个指针来控制遍历,其余的和已排序的2sum一样。之所以一样,是因为对于O(n^2)的复杂度而言,排序的O(nlgn)不算太过分:

def threesum(nums):  # 这道题需要求出三数和为0
    nums.sort()
    # i = 0 
    res=set()
    for i in range(len(nums)-2):
        j = i + 1
        k = len(nums)-1
        while j0:
                k-=1
            elif sums<0:
                j+=1
            else:
                res.add((nums[i],nums[j],nums[k]))
                j+=1
                k-=1
    return list(res)  #这里一个比较有意思的地方就是,如何把数组转化set去重,然后还能返回list?就是set里以tuple的形式插入,然后直接list它就好

leetcode 16:最接近的三数之和
这个问题听起来也很直接,给一个target,找最接近的3个数。
因此,我们需要保存2个值,一个是当前的和,另一个是和与目标的距离。当当前和的差值小于距离时更新,否则看和是大于目标还是小于,接着就按照3sum的做法继续遍历。
具体在实现的时候有个问题,一开始的时候这个距离咋办?2种方法,1是用初始值-目标作为距离,然后遍历的时候跳过这种情况,直接计算大于目标还是小于。2是直接定义一个最大值,可以是c++的int_max,Java的Integer.MAX_VALUE,也可以是python的float('inf')。因此实现如下:

def threesumclosest(nums, target):
    nums.sort()
    dis=float('inf')
    for i in range(len(nums)-2):
        j=i+1
        k=len(nums)-1
        while jtarget:
                k-=1
            elif sums

leetcode 18:4sum
其实可以看出来道理就是那么个道理,2sum的可以用hash,数量>=3的,就排个序,然后乖乖使用双指针。这里需要i,j架构好循环次数,i1 i2 j1 j2来实际移动,这里i1 j1是外层元素,就像3sum里的i一样。代码也很简单:

def fourSum(nums, target):
    """
    :type nums: List[int]
    :type target: int
    :rtype: List[List[int]]
    """
    nums.sort()
    res=[]
    length=len(nums)
    for i in range(length-3):
        for j in range(length-3):
            i1=i
            i2=i+1
            j1=length-1-j
            j2=length-2-j
            while i2target:
                    j2-=1
                else:
                    i2+=1
    return list(set([tuple(r) for r in res]))

leetcode 454 :4sum2
我要写的最后一题了。给定四个包含整数的数组列表 A , B , C , D ,计算有多少个元组 (i, j, k, l) ,使得 A[i] + B[j] + C[k] + D[l] = 0。
输入:
A = [ 1, 2]
B = [-2,-1]
C = [-1, 2]
D = [ 0, 2]
输出:
2
解释:
两个元组如下:

  1. (0, 0, 0, 1) -> A[0] + B[0] + C[0] + D[1] = 1 + (-2) + (-1) + 2 = 0
  2. (1, 1, 0, 0) -> A[1] + B[1] + C[0] + D[0] = 2 + (-1) + (-1) + 0 = 0

这个问题也没啥的,关键在于4个数要满足的个数,那么拿出我们的大砍刀:dict。同样key放具体数字,value放统计个数。这里,我们可以a+b先放入dict,然后用c+d去match,总体就是O(n2)+O(n2*O(1)),因为dict的in为1:

    def fourSumCount(A, B, C, D):
        """
        :type A: List[int]
        :type B: List[int]
        :type C: List[int]
        :type D: List[int]
        :rtype: int
        """
        cnt=0
        ab_dict={}
        for a in A:
            for b in B:
                ab_dict[a+b]=ab_dict.get(a+b,0)+1
        
        for c in C:
            for d in D:
                if -(c+d) in ab_dict:
                    cnt+=ab_dict[-(c+d)]
                    
                    
        return cnt

你可能感兴趣的:(LeetCode的sum问题)