专题概述

本专题将讲解的题目为leetcode中1, 15, 18, 454四道题,两道相关题目16和167作为练习题。主要介绍哈希表和指针两种方法来解决该类问题,从两个数之和引申到三个数之和,再从四个数之和的问题上思考如何构建出一种通用的代码(可以解决N个数之和)。相信通过这四道题的讲解,当再次遇到类似问题,一定可以顺利的解决。

目录

代码相关所有代码在leetcode英文网站上都通过了测试。

github地址,欢迎star。本专题代码在code/sum-in-array中。dyq666/leetcode-pythongithub.com

python 两个数组内各个元素相加 python两个数组求和_python 两个数组内各个元素相加

如果觉得本文对你有帮助,可以收藏点赞,若文中有问题可在评论区留言

本文的内容

通过015-3Sum问题来熟悉数组求和的两种常用方法。阅读前需要掌握哈希和双指针两种方法在两个数求和中的应用,重复的内容在本文中不在叙述。

015-3Sum给定一个包含n个整数的数组 nums,判断 nums 中是否存在三个元素a,b,c ,使得 a + b + c =0 ?找出所有满足条件且不重复的三元组。

示例:例如, 给定数组 nums = [-1, 0, 1, 2, -1, -4],

满足要求的三元组集合为:

[
[-1, 0, 1],
[-1, -1, 2]
]

1. 哈希

1. O(n^2)

2. 我们可以将三个数求和的问题转变为n次不同的目标值的两个数求和问题,也就是说第一步我们先从数组中选取一个数,用目标值减去这个数得到一个新的目标值,现在问题就变成了这个新的目标值能否由两个数相加得到。这样问题又变成了001-TwoSum的问题。我建议读到次先尝试动手完成这道题,后面相关的优化问题在你亲自实现了比较粗糙的版本后才更易于理解,如果其中出现了超时情况,可以看下一条了。

3. (优化步骤)在每次执行遍历前,必须判断当前值是否与上一个值相同,如果相同就不执行后面的内容。举个例子: [1, 1, 2, 3, 4], 在我们选取第一个数时,我们做的操作仅仅是改变目标值,让问题转换成两个数求和问题。所以当我们选取第一个1和选取第二个1时新的目标值都是原目标值-1,而选取第一个目标值,后面所有两个数组合的情况都被考虑过了。所以当遍历到第二个1时我们应该跳过这个1。

3. 这里需要找多个值, 所以在找到值后需要将两个指针都向里面移动, 移动到值不同的地方.

举个例子: 1 + 2 + 3 = 0, 在1不变的情况下, 2和3都必须改变才能保证得到一组不同的值并且还满足这个等式

回到题目:(将不同于之前的内容标注了新)数组采用原地排序,不懂sort和sorted的区别,请查阅资料。

结果集使用集合,题目中规定了不允许重复值得出现

优化部分在上面提到了

集合中存储的必须是不可变的元素(可以哈希的元素),所以我们这里选用了tuple,具体哪些是不可变的元素可以查阅资料,最常用的list是可变元素。由于题目需要不能有重复,所以存储的tuple必须按照一定顺序存放((1,2) != (2, 1)),这里采取的依然是按照从小到大的顺序。

最后返回值需要返回list,leetcode才能过,虽然要求中每一项都应该是一个list,但是tuple,leetcode也能过。如过你想符合题意,可以使用list(map(list, res))

还有一点需要注意本题的target是0

nums.sort() # 新
res = set() # 新
for i, v_i in enumerate(nums):
# 优化部分 新
if i >= 1 and v_i == nums[i - 1]:
continue
left, right = i + 1, len(nums) - 1
while left < right:
two_sum = nums[left] + nums[right]
target = -v_i
if two_sum > target:
right -= 1
elif two_sum < target:
left += 1
else:# 新
res.add((v_i, nums[left], nums[right]))
left += 1
right -= 1
while left < right and nums[left] == nums[left - 1]:
left += 1
while left < right and nums[right] == nums[right + 1]:
right -= 1
return list(res) # 新

2. 双指针

1. O(n^2)

2. 与哈希基本思路相同,需要注意的是每次查找两个数的和时都需要声明一个set

3. 由于本题新的东西不多,借此机会引入一个小的优化也就是最外层循环遍历时,当数组中只剩两个元素就结束(因为起码要三个数相加!)

nums.sort()
res = set()
for i, v_i in enumerate(nums[:-2]): # 新
if i >= 1 and v_i == nums[i - 1]:
continue
seen_set = set()# 新
for num in nums[i + 1:]:
search = 0 - (v_i + num)
if search in seen_set:
res.add((v_i, search, num))
else:
seen_set.add(num)
return list(res)

总结

本文将两种方法扩展到三个数的问题中,现在你应该非常清楚这两种方法的使用了,下一章我们将讨论4个数的问题,由此引出N个数的问题