forked from youngyangyang04/leetcode-master
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
9f090e6
commit 975e42a
Showing
12 changed files
with
910 additions
and
40 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
|
||
<p align="center"> | ||
<a href="https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ"><img src="https://img.shields.io/badge/知识星球-代码随想录-blue" alt=""></a> | ||
<a href="https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw"><img src="https://img.shields.io/badge/刷题-微信群-green" alt=""></a> | ||
<a href="https://img-blog.csdnimg.cn/20201210231711160.png"><img src="https://img.shields.io/badge/公众号-代码随想录-brightgreen" alt=""></a> | ||
<a href="https://space.bilibili.com/525438321"><img src="https://img.shields.io/badge/B站-代码随想录-orange" alt=""></a> | ||
</p> | ||
|
||
## 1. 两数之和 | ||
|
||
https://leetcode-cn.com/problems/two-sum/ | ||
|
||
给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。 | ||
|
||
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素不能使用两遍。 | ||
|
||
**示例:** | ||
|
||
给定 nums = [2, 7, 11, 15], target = 9 | ||
|
||
因为 nums[0] + nums[1] = 2 + 7 = 9 | ||
|
||
所以返回 [0, 1] | ||
|
||
|
||
## 思路 | ||
|
||
很明显暴力的解法是两层for循环查找,时间复杂度是O(n^2)。 | ||
|
||
建议大家做这道题目之前,先做一下这两道 | ||
* [242. 有效的字母异位词](https://mp.weixin.qq.com/s/vM6OszkM6L1Mx2Ralm9Dig) | ||
* [349. 两个数组的交集](https://mp.weixin.qq.com/s/N9iqAchXreSVW7zXUS4BVA) | ||
|
||
[242. 有效的字母异位词](https://mp.weixin.qq.com/s/vM6OszkM6L1Mx2Ralm9Dig) 这道题目是用数组作为哈希表来解决哈希问题,[349. 两个数组的交集](https://mp.weixin.qq.com/s/N9iqAchXreSVW7zXUS4BVA)这道题目是通过set作为哈希表来解决哈希问题。 | ||
|
||
本题呢,则要使用map,那么来看一下使用数组和set来做哈希法的局限。 | ||
|
||
* 数组的大小是受限制的,而且如果元素很少,而哈希值太大会造成内存空间的浪费。 | ||
* set是一个集合,里面放的元素只能是一个key,而两数之和这道题目,不仅要判断y是否存在而且还要记录y的下表位置,因为要返回x 和 y的下表。所以set 也不能用。 | ||
|
||
此时就要选择另一种数据结构:map ,map是一种key value的存储结构,可以用key保存数值,用value在保存数值所在的下表。 | ||
|
||
C++中map,有三种类型: | ||
|
||
|映射 |底层实现 | 是否有序 |数值是否可以重复 | 能否更改数值|查询效率 |增删效率| | ||
|---|---| --- |---| --- | --- | ---| | ||
|std::map |红黑树 |key有序 |key不可重复 |key不可修改 | O(logn)|O(logn) | | ||
|std::multimap | 红黑树|key有序 | key可重复 | key不可修改|O(logn) |O(logn) | | ||
|std::unordered_map |哈希表 | key无序 |key不可重复 |key不可修改 |O(1) | O(1)| | ||
|
||
std::unordered_map 底层实现为哈希表,std::map 和std::multimap 的底层实现是红黑树。 | ||
|
||
同理,std::map 和std::multimap 的key也是有序的(这个问题也经常作为面试题,考察对语言容器底层的理解)。 更多哈希表的理论知识请看[关于哈希表,你该了解这些!](https://mp.weixin.qq.com/s/g8N6WmoQmsCUw3_BaWxHZA)。 | ||
|
||
**这道题目中并不需要key有序,选择std::unordered_map 效率更高!** | ||
|
||
解题思路动画如下: | ||
|
||
<video src='https://code-thinking.cdn.bcebos.com/gifs/1.%E4%B8%A4%E6%95%B0%E4%B9%8B%E5%92%8C.mp4' controls='controls' width='640' height='320' autoplay='autoplay'> Your browser does not support the video tag.</video></div> | ||
|
||
C++代码: | ||
|
||
```C++ | ||
class Solution { | ||
public: | ||
vector<int> twoSum(vector<int>& nums, int target) { | ||
std::unordered_map <int,int> map; | ||
for(int i = 0; i < nums.size(); i++) { | ||
auto iter = map.find(target - nums[i]); | ||
if(iter != map.end()) { | ||
return {iter->second, i}; | ||
} | ||
map.insert(pair<int, int>(nums[i], i)); | ||
} | ||
return {}; | ||
} | ||
}; | ||
``` | ||
------------------------ | ||
* 微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) | ||
* B站:[代码随想录](https://space.bilibili.com/525438321) | ||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) | ||
![](../pics/公众号.png) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,213 @@ | ||
|
||
<p align="center"> | ||
<a href="https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ"><img src="https://img.shields.io/badge/知识星球-代码随想录-blue" alt=""></a> | ||
<a href="https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw"><img src="https://img.shields.io/badge/刷题-微信群-green" alt=""></a> | ||
<a href="https://img-blog.csdnimg.cn/20201210231711160.png"><img src="https://img.shields.io/badge/公众号-代码随想录-brightgreen" alt=""></a> | ||
<a href="https://space.bilibili.com/525438321"><img src="https://img.shields.io/badge/B站-代码随想录-orange" alt=""></a> | ||
</p> | ||
|
||
|
||
> 用哈希表解决了[两数之和](https://mp.weixin.qq.com/s/uVAtjOHSeqymV8FeQbliJQ),那么三数之和呢? | ||
# 第15题. 三数之和 | ||
|
||
https://leetcode-cn.com/problems/3sum/ | ||
|
||
给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有满足条件且不重复的三元组。 | ||
|
||
**注意:** 答案中不可以包含重复的三元组。 | ||
|
||
示例: | ||
|
||
给定数组 nums = [-1, 0, 1, 2, -1, -4], | ||
|
||
满足要求的三元组集合为: | ||
[ | ||
[-1, 0, 1], | ||
[-1, -1, 2] | ||
] | ||
|
||
|
||
# 思路 | ||
|
||
**注意[0, 0, 0, 0] 这组数据** | ||
|
||
## 哈希解法 | ||
|
||
两层for循环就可以确定 a 和b 的数值了,可以使用哈希法来确定 0-(a+b) 是否在 数组里出现过,其实这个思路是正确的,但是我们有一个非常棘手的问题,就是题目中说的不可以包含重复的三元组。 | ||
|
||
把符合条件的三元组放进vector中,然后在去去重,这样是非常费时的,很容易超时,也是这道题目通过率如此之低的根源所在。 | ||
|
||
去重的过程不好处理,有很多小细节,如果在面试中很难想到位。 | ||
|
||
时间复杂度可以做到O(n^2),但还是比较费时的,因为不好做剪枝操作。 | ||
|
||
大家可以尝试使用哈希法写一写,就知道其困难的程度了。 | ||
|
||
哈希法C++代码: | ||
```C++ | ||
class Solution { | ||
public: | ||
vector<vector<int>> threeSum(vector<int>& nums) { | ||
vector<vector<int>> result; | ||
sort(nums.begin(), nums.end()); | ||
// 找出a + b + c = 0 | ||
// a = nums[i], b = nums[j], c = -(a + b) | ||
for (int i = 0; i < nums.size(); i++) { | ||
// 排序之后如果第一个元素已经大于零,那么不可能凑成三元组 | ||
if (nums[i] > 0) { | ||
continue; | ||
} | ||
if (i > 0 && nums[i] == nums[i - 1]) { //三元组元素a去重 | ||
continue; | ||
} | ||
unordered_set<int> set; | ||
for (int j = i + 1; j < nums.size(); j++) { | ||
if (j > i + 2 | ||
&& nums[j] == nums[j-1] | ||
&& nums[j-1] == nums[j-2]) { // 三元组元素b去重 | ||
continue; | ||
} | ||
int c = 0 - (nums[i] + nums[j]); | ||
if (set.find(c) != set.end()) { | ||
result.push_back({nums[i], nums[j], c}); | ||
set.erase(c);// 三元组元素c去重 | ||
} else { | ||
set.insert(nums[j]); | ||
} | ||
} | ||
} | ||
return result; | ||
} | ||
}; | ||
``` | ||
## 双指针 | ||
**其实这道题目使用哈希法并不十分合适**,因为在去重的操作中有很多细节需要注意,在面试中很难直接写出没有bug的代码。 | ||
而且使用哈希法 在使用两层for循环的时候,能做的剪枝操作很有限,虽然时间复杂度是O(n^2),也是可以在leetcode上通过,但是程序的执行时间依然比较长 。 | ||
接下来我来介绍另一个解法:双指针法,**这道题目使用双指针法 要比哈希法高效一些**,那么来讲解一下具体实现的思路。 | ||
动画效果如下: | ||
![15.三数之和](https://code-thinking.cdn.bcebos.com/gifs/15.%E4%B8%89%E6%95%B0%E4%B9%8B%E5%92%8C.gif) | ||
拿这个nums数组来举例,首先将数组排序,然后有一层for循环,i从下表0的地方开始,同时定一个下表left 定义在i+1的位置上,定义下表right 在数组结尾的位置上。 | ||
依然还是在数组中找到 abc 使得a + b +c =0,我们这里相当于 a = nums[i] b = nums[left] c = nums[right]。 | ||
接下来如何移动left 和right呢, 如果nums[i] + nums[left] + nums[right] > 0 就说明 此时三数之和大了,因为数组是排序后了,所以right下表就应该向左移动,这样才能让三数之和小一些。 | ||
如果 nums[i] + nums[left] + nums[right] < 0 说明 此时 三数之和小了,left 就向右移动,才能让三数之和大一些,直到left与right相遇为止。 | ||
时间复杂度:O(n^2)。 | ||
## 双指针法C++代码 | ||
```C++ | ||
class Solution { | ||
public: | ||
vector<vector<int>> threeSum(vector<int>& nums) { | ||
vector<vector<int>> result; | ||
sort(nums.begin(), nums.end()); | ||
// 找出a + b + c = 0 | ||
// a = nums[i], b = nums[left], c = nums[right] | ||
for (int i = 0; i < nums.size(); i++) { | ||
// 排序之后如果第一个元素已经大于零,那么无论如何组合都不可能凑成三元组,直接返回结果就可以了 | ||
if (nums[i] > 0) { | ||
return result; | ||
} | ||
// 错误去重方法,将会漏掉-1,-1,2 这种情况 | ||
/* | ||
if (nums[i] == nums[i + 1]) { | ||
continue; | ||
} | ||
*/ | ||
// 正确去重方法 | ||
if (i > 0 && nums[i] == nums[i - 1]) { | ||
continue; | ||
} | ||
int left = i + 1; | ||
int right = nums.size() - 1; | ||
while (right > left) { | ||
// 去重复逻辑如果放在这里,0,0,0 的情况,可能直接导致 right<=left 了,从而漏掉了 0,0,0 这种三元组 | ||
/* | ||
while (right > left && nums[right] == nums[right - 1]) right--; | ||
while (right > left && nums[left] == nums[left + 1]) left++; | ||
*/ | ||
if (nums[i] + nums[left] + nums[right] > 0) { | ||
right--; | ||
} else if (nums[i] + nums[left] + nums[right] < 0) { | ||
left++; | ||
} else { | ||
result.push_back(vector<int>{nums[i], nums[left], nums[right]}); | ||
// 去重逻辑应该放在找到一个三元组之后 | ||
while (right > left && nums[right] == nums[right - 1]) right--; | ||
while (right > left && nums[left] == nums[left + 1]) left++; | ||
// 找到答案时,双指针同时收缩 | ||
right--; | ||
left++; | ||
} | ||
} | ||
} | ||
return result; | ||
} | ||
}; | ||
``` | ||
|
||
# 思考题 | ||
|
||
既然三数之和可以使用双指针法,我们之前讲过的[两数之和](https://mp.weixin.qq.com/s/uVAtjOHSeqymV8FeQbliJQ),可不可以使用双指针法呢? | ||
|
||
如果不能,题意如何更改就可以使用双指针法呢? **大家留言说出自己的想法吧!** | ||
|
||
两数之和 就不能使用双指针法,因为[两数之和](https://mp.weixin.qq.com/s/uVAtjOHSeqymV8FeQbliJQ)要求返回的是索引下表, 而双指针法一定要排序,一旦排序之后原数组的索引就被改变了。 | ||
|
||
如果[两数之和](https://mp.weixin.qq.com/s/uVAtjOHSeqymV8FeQbliJQ)要求返回的是数值的话,就可以使用双指针法了。 | ||
|
||
> 更过算法干货文章持续更新,可以微信搜索「代码随想录」第一时间围观,关注后,回复「Java」「C++」 「python」「简历模板」「数据结构与算法」等等,就可以获得我多年整理的学习资料。 | ||
|
||
|
||
|
||
## tmp | ||
|
||
```python | ||
class Solution: | ||
def threeSum(self, nums: List[int]) -> List[List[int]]: | ||
res = [] | ||
nums.sort() | ||
for i in range(len(nums)): | ||
if nums[i] > 0 : | ||
return res | ||
if nums[i] == nums[i-1] and i>0: | ||
continue | ||
left = i + 1 | ||
right = len(nums) - 1 | ||
while left < right: | ||
if nums[i] + nums[left] + nums[right] > 0: | ||
right -= 1 | ||
elif nums[i] + nums[left] + nums[right] < 0: | ||
left += 1 | ||
else: | ||
res.append([nums[i], nums[left], nums[right]]) | ||
while left < right and nums[right] == nums[right-1]: | ||
right -= 1 | ||
while left < right and nums[left] == nums[left+1]: | ||
left += 1 | ||
left += 1 | ||
right -= 1 | ||
return res | ||
``` | ||
------------------------ | ||
* 微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) | ||
* B站:[代码随想录](https://space.bilibili.com/525438321) | ||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) | ||
![](../pics/公众号.png) |
Oops, something went wrong.