nSum 问题
n=2 Two Sum
给定一个整数数组 nums
和一个整数目标值 target
,请你在该数组中找出 和为目标值 target
的那 两个 整数,并返回它们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。
你可以按任意顺序返回答案。
示例 1:
1 | 输入:nums = [2,7,11,15], target = 9 |
示例 2:
1 | 输入:nums = [3,2,4], target = 6 |
示例 3:
1 | 输入:nums = [3,3], target = 6 |
题解
可以通过一个哈希表减少时间复杂度:
1 | int[] twoSum(int[] nums, int target) { |
这样,由于哈希表的查询时间为 O(1),算法的时间复杂度降低到 O(N),但是需要 O(N) 的空间复杂度来存储哈希表。不过综合来看,是要比暴力解法高效的。
我觉得 Two Sum 系列问题就是想教我们如何使用哈希表处理问题。我们接着往后看。
TwoSum II
这里我们稍微修改一下上面的问题。我们设计一个类,拥有两个 API:
1 | class TwoSum { |
如何实现这两个 API 呢,我们可以仿照上一道题目,使用一个哈希表辅助 find
方法:
1 | class TwoSum { |
进行 find
的时候有两种情况,举个例子:
情况一:add
了 [3,3,2,5]
之后,执行 find(6)
,由于 3 出现了两次,3 + 3 = 6,所以返回 true。
情况二:add
了 [3,3,2,5]
之后,执行 find(7)
,那么 key
为 2,other
为 5 时算法可以返回 true。
除了上述两种情况外,find
只能返回 false 了。
对于这个解法的时间复杂度呢,add
方法是 O(1),find
方法是 O(N),空间复杂度为 O(N),和上一道题目比较类似。
但是对于 API 的设计,是需要考虑现实情况的。比如说,我们设计的这个类,使用 find
方法非常频繁,那么每次都要 O(N) 的时间,岂不是很浪费费时间吗?对于这种情况,我们是否可以做些优化呢?
是的,对于频繁使用 find
方法的场景,我们可以进行优化。我们可以参考上一道题目的暴力解法,借助哈希集合来针对性优化 find
方法:
1 | class TwoSum { |
这样 sum
中就储存了所有加入数字可能组成的和,每次 find
只要花费 O(1) 的时间在集合中判断一下是否存在就行了,显然非常适合频繁使用 find
的场景。
三、总结
对于 TwoSum 问题,一个难点就是给的数组无序。对于一个无序的数组,我们似乎什么技巧也没有,只能暴力穷举所有可能。
一般情况下,我们会首先把数组排序再考虑双指针技巧。TwoSum 启发我们,HashMap 或者 HashSet 也可以帮助我们处理无序数组相关的简单问题。
另外,设计的核心在于权衡,利用不同的数据结构,可以得到一些针对性的加强。
最后,如果 TwoSum I 中给的数组是有序的,应该如何编写算法呢?答案很简单,前文 双指针技巧汇总 写过:
1 | int[] twoSum(int[] nums, int target) { |
- Post title:nSum 问题的思想
- Post author:Yuxuan Wu
- Create time:2021-07-06 19:03:23
- Post link:yuxuanwu17.github.io2021/07/06/2021-07-06-1-nSum 问题的思想/
- Copyright Notice:All articles in this blog are licensed under BY-NC-SA unless stating additionally.