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<j:
        sums = nums[i]+nums[j]
        if sums == target:
            return [i+i, j+1]  # 这里+1是因为原题要求坐标从1开始
        elif sums > 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<target时,我们正常start右移,mid+1。如果大于,那么end左移,mid-1,可是如果==?这里不会return,而是继续缩小右边界(即,end左移),反之,start右移。

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]<target:
                l=mid+1
            else:
                r=mid-1
            if nums[mid]==target:
                index=mid
        return index
    
    def findlast(self,nums,target):
        l=0
        r=len(nums)-1
        index=-1
        while(l<=r):
            mid=l+(r-l)//2
            if nums[mid]<=target:
                l=mid+1
            else:
                r=mid-1
            if nums[mid]==target:
                index=mid
        return index 

明白了这个之和,代码就变得很简单,也可以把两个融合到一起,但是没有必要。
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 j<k:
            sums=nums[i]+nums[j]+nums[k]
            if sums>0:
                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 j<k:
            sums=nums[i]+nums[j]+nums[k]
            if abs(target-sums)<dis:
                s=sums
                dis=abs(target-sums)
            if sums>target:
                k-=1
            elif sums<target:
                j+=1
            elif:
                return s
    return s

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 i2<j2:
                sum_nums=nums[i1]+nums[i2]+nums[j1]+nums[j2]
                if sum_nums==target:
                    res.append([nums[i1],nums[i2],nums[j1],nums[j2]])

                    j2-=1
                    i2+=1
                elif sum_nums>target:
                    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
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,117评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,328评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,839评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,007评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,384评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,629评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,880评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,593评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,313评论 1 243
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,575评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,066评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,392评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,052评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,082评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,844评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,662评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,575评论 2 270

推荐阅读更多精彩内容

  • LeetCode 刷题随手记 - 第一部分 前 256 题(非会员),仅算法题,的吐槽 https://leetc...
    蕾娜漢默阅读 16,516评论 2 36
  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 11,036评论 1 32
  • 本文内容为练习LeetCode题目时的解题思路和不同算法的记录,实现语言为C++,代码保存在Github,均已在L...
    SK木眠阅读 943评论 0 0
  • 总是习惯在夜里 将幽冷的时光 浇上孤独 然后静待花开 我知道,这不过是幻想 如此的朦胧,竟然潮湿了 南来北往的春风...
    冷冬年阅读 182评论 0 5
  • 风追着跑 急促地推搡着秋 她的脸羞到极致,碾压群芳 来路过客,痴迷着她的热烈,树树倾城 果真是少女怀春,忐忑的心随...
    言舒华阅读 267评论 1 3