这里写几个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: 在排序数组中查找元素的第一个和最后一个位置 明白了这个之和,代码就变得很简单,也可以把两个融合到一起,但是没有必要。 leetcode 16:最接近的三数之和 leetcode 18:4sum leetcode 454 :4sum2 这个问题也没啥的,关键在于4个数要满足的个数,那么拿出我们的大砍刀:dict。同样key放具体数字,value放统计个数。这里,我们可以a+b先放入dict,然后用c+d去match,总体就是O(n2)+O(n2*O(1)),因为dict的in为1:
这道题个人认为极佳,如果以后我当面试官,我一定会考这道题。这道题的时间要求是O(lg(n)),也就是说,不能有一般直接查找的操作。虽然很多提交时间很短,但是都有找到点后两侧遍历的操作,这种操作在大规模上必然会爆,能ac仅仅是lc的case太少了。
要我来解,必然只能纯查找,也就是,找到起点,找到终点。binary search的模板已经很清晰了,看起来大了就减end,小了就加start的操作似乎也不能改变,故能变的也就是==
的情况了
假设寻找第一个,那么当midclass 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 j
这个问题听起来也很直接,给一个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
其实可以看出来道理就是那么个道理,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
我要写的最后一题了。给定四个包含整数的数组列表 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
解释:
两个元组如下:
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