秒杀 2Sum 3Sum 4Sum 算法题

2 Sum 这题是 Leetcode 的第一题,相信大部分小伙伴都听过的吧。
作为一道标着 Easy 难度的题,它真的这么简单吗?
我在之前的刷题视频里说过,大家刷题一定要吃透一类题,为什么有的人题目做着越来越少,有的人总觉得刷不完的题,就是因为没有分类吃透。
单纯的追求做题数量是没有意义的,Leetcode 的题目只会越来越多,就像高三时的模考试卷一样做不完,但分类总结,学会解决问题的方式方法,才能遇到新题也不手足无措。
2 Sum

这道题题意就是,给一个数组和一个目标值,让你在这个数组里找到两个数,使得它俩之和等于这个目标值的。
比如题目中给的例子,目标值是 9,然后数组里 2 + 7 = 9,于是返回 2 和 7 的下标。
方法一
在我多年前还不知道时空复杂度的时候,我想这还不简单嘛,就每个组合挨个试一遍呗,也就是两层循环。
后来我才知道,这样时间复杂度是很高的,是 O(n^2);但另一方面,这种方法的空间复杂度最低,是 O(1)。
所以,面试时一定要先问面试官,是希望优化时间还是优化空间。
一般来说我们追求优化时间,但你不能默认面试官也是这么想的,有时候他就是想考你有没有这个意识呢。
如果一个方法能够兼具优化时间和空间那就更好了,比如斐波那契数列这个问题中从递归到 DP 的优化,就是时间和空间的双重优化,不清楚的同学后台回复「递归」快去补课~
我们来看下这个代码:
class Solution {
public int[] twoSum(int[] nums, int target) {
for (int i = 0; i < nums.length; i++) {
for (int j = i + 1; j < nums.length; j++) {
if (nums[i] + nums[j] == target) {
return new int[]{i, j};
}
}
}
return new int[]{-1, -1};
}
}
时间复杂度:O(n^2) 空间复杂度:O(1)
喏,这速度不太行诶。

方法二
那在我学了 HashMap 这个数据结构之后呢,我又有了新的想法。
HashMap 或者 HashSet 的最大优势就是能够用 O(1) 的时间获取到目标值,那么是不是可以优化方法一的第二个循环呢?
有了这个思路,假设当前在看 x,那就是需要把 x 之前或者之后的数放在 HashSet 里,然后看下 target - x 在不在这个 hashSet 里,如果在的话,那就匹配成功~
诶这里有个问题,这题要求返回这俩数的下标,可是 HashSet 里的数是无序的...
那就用升级版——HashMap 嘛~~还不了解 HashMap 的原理的同学快去公众号后台回复「HashMap」看文章啦。
HashMap 里记录下数值和它的 index 这样匹配成功之后就可以顺便得到 index 了。
这里我们不需要提前记录所有的值,只需要边过数组边记录就好了,为了防止重复,我们只在这个当前的数出现之前的数组部分里找另一个数。
总结一下,
HashMap里记录的是下标i之前的所有出现过的数;对于每个 nums[i],我们先检查target - nums[i]是否在这个map里;如果在就直接返回了,如果不在就把当前 i的信息加进map里。
class Solution {
public int[] twoSum(int[] nums, int target) {
int[] res = new int[2];
Map<Integer, Integer> map = new HashMap<>();
for (int i = 0; i < nums.length; i++) {
if (map.containsKey(target - nums[i])) {
res[0] = map.get(target - nums[i]);
res[1] = i;
return res;
}
map.put(nums[i], i);
}
return res;
}
}
时间复杂度:O(n) 空间复杂度:O(n)
喏,速度提升至 beat 99.96%

拓展
这是最基本的 2 Sum 问题,这个题可以有太多的变种了:
如果这个数组里有不止一组结果,要求返回所有组合,该怎么做?
如果这个数组里有重复元素,又该怎么做?
如果这个数组是一个排好序了的数组,那如何利用这个条件呢?- Leetcode 167
如果不是数组而是给一个
BST,该怎么在一棵树上找这俩数呢?- Leetcode 653
...
这里讲一下排序数组这道题,之后会在 BST 的文章里会讲 653 这题。
排序数组

我们知道排序算法中最快的也需要 O(nlogn),所以如果是一个 2 Sum 问题,那没必要专门排序,因为排序会成为运算的瓶颈。
但如果题目给的就是个排好序了的数组,那肯定要好好收着了呀!
因为当数组是排好序的时候,我们可以进一步优化空间,达到 O(n) 的时间和 O(1) 的空间。
该怎么利用排好序这个性质呢?
那就是说,在 x 右边的数,都比 x 要大;在 x 左边的数,都比 x 要小。
如果
x + y > target,那么就要y往左走,往小的方向走;如果
x + y < target,那么就要x往右走,往大的方向走。

这也就是典型的 Two pointer 算法,两个指针相向而行的情况,我之后也会出文章详细来讲哒。
class Solution {
public int[] twoSum(int[] numbers, int target) {
int left = 0;
int right = numbers.length - 1;
while (left < right) {
int sum = numbers[left] + numbers[right];
if (sum == target) {
return new int[]{left + 1, right + 1}; //Your returned answers are not zero-based.
} else if (sum < target) {
left ++;
} else {
right --;
}
}
return new int[]{-1, -1};
}
}
3 Sum

3 Sum 的问题其实就是一个 2 Sum 的升级版,因为 1 + 2 = 3 嘛。。
那就是外面一层循环,固定一个值,在剩下的数组里做 2 Sum 问题。
反正 3 Sum 怎么着都得 O(n^2) ,就可以先排序,反正不在乎排序的这点时间了,这样就可以用 Two pointer 来做了。
还需要注意的是,这道题返回的是数值,而非 index,所以它不需要重复的数值——The solution set must not contain duplicate triplets.
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> res = new ArrayList<>();
Arrays.sort(nums);
for (int i = 0; i + 2 < nums.length; i++) {
if (i > 0 && nums[i] == nums[i - 1]) {
// skip same result
continue;
}
int j = i + 1;
int k = nums.length - 1;
int target = -nums[i];
while (j < k) {
if (nums[j] + nums[k] == target) {
res.add(Arrays.asList(nums[i], nums[j], nums[k]));
j++;
k--;
while (j < k && nums[j] == nums[j - 1]) {
j++; // skip same result
}
while (j < k && nums[k] == nums[k + 1]) {
k--; // skip same result
}
} else if (nums[j] + nums[k] > target) {
k--;
} else {
j++;
}
}
}
return res;
}
}
4 Sum
最后就是 4 Sum 问题啦。

这一题如果只是 O(n^3) 的解法没什么难的,因为就是在 3 Sum 的基础上再加一层循环嘛。
但是如果在面试中只做出 O(n^3) 恐怕就过不了了哦
秒杀 2Sum 3Sum 4Sum 算法题的更多相关文章
- 求和问题总结(leetcode 2Sum, 3Sum, 4Sum, K Sum)
转自 http://tech-wonderland.net/blog/summary-of-ksum-problems.html 前言: 做过leetcode的人都知道, 里面有2sum, 3sum ...
- LeetCode解题报告--2Sum, 3Sum, 4Sum, K Sum求和问题总结
前言: 这几天在做LeetCode 里面有2sum, 3sum(closest), 4sum等问题, 这类问题是典型的递归思路解题.该这类问题的关键在于,在进行求和求解前,要先排序Arrays.sor ...
- 2Sum,3Sum,4Sum,kSum,3Sum Closest系列
1).2sum 1.题意:找出数组中和为target的所有数对 2.思路:排序数组,然后用两个指针i.j,一前一后,计算两个指针所指内容的和与target的关系,如果小于target,i右移,如果大于 ...
- 算法题丨3Sum Closest
描述 Given an array S of n integers, find three integers in S such that the sum is closest to a given ...
- 经典算法题每日演练——第十六题 Kruskal算法
原文:经典算法题每日演练--第十六题 Kruskal算法 这篇我们看看第二种生成树的Kruskal算法,这个算法的魅力在于我们可以打一下算法和数据结构的组合拳,很有意思的. 一:思想 若存在M={0, ...
- 一道java算法题分析
最近在面试中遇到这样的一道算法题: 求100!的结果的各位数之和为多少? 如:5!=5*4*3*2*1=120,那么他们的和为1+2+0=3这道题不算难,不过倒是注意的细节也有 ...
- FCC上的初级算法题
核心提示:FCC的算法题一共16道.跟之前简单到令人发指的基础题目相比,难度是上了一个台阶.主要涉及初步的字符串,数组等运算.仍然属于基础的基础,官方网站给出的建议完成时间为50小时,超出了之前所有非 ...
- 解决一道leetcode算法题的曲折过程及引发的思考
写在前面 本题实际解题过程是 从 40秒 --> 24秒 -->1.5秒 --> 715ms --> 320ms --> 48ms --> 36ms --> ...
- js 中的算法题,那些经常看到的
js中遇到的算法题不是很多,可以说基本遇不到.但面试的时候,尤其是一些大公司,总是会出这样那样的算法题,考察一个程序员的逻辑思维能力.如下: 1.回文. 回文是指把相同的词汇或句子,在下文中调换位置或 ...
随机推荐
- 学习 bypass csp记录
最近看到一篇bypas csp的记录复现学习下 配置csp 这里直接设置html头达到配置csp的效果. Content-Security-Policy: script-src 'self' 'uns ...
- Linux切换用户时报错/.bash_profile: Permission denied,命令行(终端提示符)出现-bash-4.2$
Linux切换用户时报错/.bash_profile: Permission denied,命令行(终端提示符)出现-bash-4.2$ 利用su - 切换用户时,发现有一个用户切时出现如下情况 [r ...
- 在Ubuntu 18.04中安装Wine QQ、微信、TIM
近日重新安装了Ubuntu 18.04,因此要重新安装一下Wine QQ.微信之类的,完整安装Wine系列软件一直是一个老大难的问题,网上搜集到的博客也比较零散,因此这里特此写篇博客记录一下 0. 这 ...
- 国外的非常好的开发提问回答的网站https://stackoverflow.com/questions
https://stackoverflow.com/questions 可以进行搜索,在页面顶部,有搜索输入框
- hostapd阅读(openwrt)-3
从官网下载相对而言比较干净的源码版本http://w1.fi/hostapd/,然后将其移植到openwrt下,方便在源码阅读时候进行调试编译,移植的过程总结如下心得. 1. openwrt编译与cl ...
- File类的基本概念与递归
一.File类 1.概念 File类:是文件和目录路径名的抽象表示形式. 即,Java中把文件或者目录(文件夹)都封装成File对象.也就是说如果我们要去操作硬盘上的文件,或者文件夹只要找到File这 ...
- 豆瓣 9.0 分的《Python学习知识手册》|百度网盘免费下载|
豆瓣 9.0 分的<Python学习知识手册>|百度网盘免费下载| 提取码:nuak 这是之前入门学习Python时候的学习资料,非常全面,从Python基础.到web开发.数据分析.机器 ...
- 十进制转换为其他进制(不使用format)
base = [str(x) for x in range(10)] + [chr(x) for x in range(ord('A'), ord('A') + 6)] # ['0', '1', '2 ...
- luogu P3920 [WC2014]紫荆花之恋
LINK:紫荆花之恋 每次动态加入一个节点 统计 有多少个节点和当前节点的距离小于他们的权值和. 显然我们不能n^2暴力. 考虑一个简化版的问题 树已经给出 每次求某个节点和其他节点的贡献. 不难想到 ...
- Android中的LruCache的原理和使用
Android中的LruCache的原理和使用 LruCache,虽然很多文章都把LRU翻译成"最近最少使用"缓存策略,但Android中的LruCache真的如此吗? 答案是No ...