diff --git a/README.md b/README.md
index 43cc7ebe..9b345f32 100644
--- a/README.md
+++ b/README.md
@@ -40,6 +40,8 @@
* [哈希表:这道题目我做过?](https://mp.weixin.qq.com/s/sYZIR4dFBrw_lr3eJJnteQ)
* [哈希表:解决了两数之和,那么能解决三数之和么?](https://mp.weixin.qq.com/s/r5cgZFu0tv4grBAexdcd8A)
* [双指针法:一样的道理,能解决四数之和](https://mp.weixin.qq.com/s/nQrcco8AZJV1pAOVjeIU_g)
+* [数组:每次遇到二分法,都是一看就会,一写就废](https://mp.weixin.qq.com/s/fCf5QbPDtE6SSlZ1yh_q8Q)
+* [数组:就移除个元素很难么?](https://mp.weixin.qq.com/s/wj0T-Xs88_FHJFwayElQlA)
* 精选链表相关的面试题
* 精选字符串相关的面试题
* 精选栈与队列相关的面试题
@@ -82,6 +84,10 @@
* [0219.存在重复元素II](https://github.com/youngyangyang04/leetcode/blob/master/problems/0219.存在重复元素II.md)
* 0220.存在重复元素III
+* 循环不变量原则
+ * [0035.搜索插入位置](https://mp.weixin.qq.com/s/fCf5QbPDtE6SSlZ1yh_q8Q)
+ * [0059.螺旋矩阵II](https://github.com/youngyangyang04/leetcode/blob/master/problems/0059.螺旋矩阵II.md)
+
* 字符串经典题目
* [0344.反转字符串](https://github.com/youngyangyang04/leetcode/blob/master/problems/0344.反转字符串.md)
* [0541.反转字符串II](https://github.com/youngyangyang04/leetcode/blob/master/problems/0541.反转字符串II.md)
@@ -92,11 +98,12 @@
* [0459.重复的子字符串](https://github.com/youngyangyang04/leetcode/blob/master/problems/0459.重复的子字符串.md)
* 双指针法经典题目
- * [0015.三数之和](https://github.com/youngyangyang04/leetcode/blob/master/problems/0015.三数之和.md)
- * [0018.四数之和](https://github.com/youngyangyang04/leetcode/blob/master/problems/0018.四数之和.md)
+ * [0027.移除元素](https://mp.weixin.qq.com/s/wj0T-Xs88_FHJFwayElQlA)
+ * [0015.三数之和](https://mp.weixin.qq.com/s/r5cgZFu0tv4grBAexdcd8A)
+ * [0018.四数之和](https://mp.weixin.qq.com/s/nQrcco8AZJV1pAOVjeIU_g)
* [0026.删除排序数组中的重复项](https://github.com/youngyangyang04/leetcode/blob/master/problems/0026.删除排序数组中的重复项.md)
- * [0206.翻转链表](https://github.com/youngyangyang04/leetcode/blob/master/problems/0206.翻转链表.md)
- * [0142.环形链表II](https://github.com/youngyangyang04/leetcode/blob/master/problems/0142.环形链表II.md)
+ * [0206.翻转链表](https://mp.weixin.qq.com/s/pnvVP-0ZM7epB8y3w_Njwg)
+ * [0142.环形链表II](https://mp.weixin.qq.com/s/_QVP3IkRZWx9zIpQRgajzA)
* [0344.反转字符串](https://github.com/youngyangyang04/leetcode/blob/master/problems/0344.反转字符串.md)
* [剑指Offer05.替换空格](https://github.com/youngyangyang04/leetcode/blob/master/problems/剑指Offer05.替换空格.md)
@@ -400,7 +407,7 @@ int countNodes(TreeNode* root) {
|[0205.同构字符串](https://github.com/youngyangyang04/leetcode/blob/master/problems/0205.同构字符串.md) |哈希表 |简单| **哈希**|
|[0206.翻转链表](https://github.com/youngyangyang04/leetcode/blob/master/problems/0206.翻转链表.md) |链表 |简单| **双指针法** **递归**|
|[0209.长度最小的子数组](https://github.com/youngyangyang04/leetcode/blob/master/problems/0209.长度最小的子数组.md) |数组 |中等| **暴力** **滑动窗口**|
-|[0216.组合总和III](https://github.com/youngyangyang04/leetcode/blob/master/problems/0216.组合总和III.md) |数组/回溯 |中等| **回溯**|
+|[0216.组合总和III](https://github.com/youngyangyang04/leetcode/blob/master/problems/0216.组合总和III.md) |数组/回溯 |中等| **回溯算法**|
|[0219.存在重复元素II](https://github.com/youngyangyang04/leetcode/blob/master/problems/0219.存在重复元素II.md) | 哈希表 |简单| **哈希** |
|[0222.完全二叉树的节点个数](https://github.com/youngyangyang04/leetcode/blob/master/problems/0222.完全二叉树的节点个数.md) | 树 |简单| **递归** |
|[0225.用队列实现栈](https://github.com/youngyangyang04/leetcode/blob/master/problems/0225.用队列实现栈.md) | 队列 |简单| **队列** |
@@ -409,6 +416,7 @@ int countNodes(TreeNode* root) {
|[0237.删除链表中的节点](https://github.com/youngyangyang04/leetcode/blob/master/problems/0237.删除链表中的节点.md) |链表 |简单| **原链表移除** **添加虚拟节点** 递归|
|[0239.滑动窗口最大值](https://github.com/youngyangyang04/leetcode/blob/master/problems/0239.滑动窗口最大值.md) |滑动窗口/队列 |困难| **单调队列**|
|[0242.有效的字母异位词](https://github.com/youngyangyang04/leetcode/blob/master/problems/0242.有效的字母异位词.md) |哈希表 |简单| **哈希**|
+|[0332.重新安排行程](https://github.com/youngyangyang04/leetcode/blob/master/problems/0332.重新安排行程.md) |深度优先搜索/回溯 |中等| **深度优先搜索/回溯算法**|
|[0344.反转字符串](https://github.com/youngyangyang04/leetcode/blob/master/problems/0344.反转字符串.md) |字符串 |简单| **双指针**|
|[0347.前K个高频元素](https://github.com/youngyangyang04/leetcode/blob/master/problems/0347.前K个高频元素.md) |哈希/堆/优先级队列 |中等| **哈希/优先级队列**|
|[0349.两个数组的交集](https://github.com/youngyangyang04/leetcode/blob/master/problems/0349.两个数组的交集.md) |哈希表 |简单|**哈希**|
@@ -418,7 +426,7 @@ int countNodes(TreeNode* root) {
|[0450.删除二叉搜索树中的节点](https://github.com/youngyangyang04/leetcode/blob/master/problems/0450.删除二叉搜索树中的节点.md) |树 |中等|**递归**|
|[0454.四数相加II](https://github.com/youngyangyang04/leetcode/blob/master/problems/0454.四数相加II.md) |哈希表 |中等| **哈希**|
|[0459.重复的子字符串](https://github.com/youngyangyang04/leetcode/blob/master/problems/0459.重复的子字符串.md) |字符创 |简单| **KMP**|
-|[0491.递增子序列](https://github.com/youngyangyang04/leetcode/blob/master/problems/0491.递增子序列.md) |深度优先搜索 |中等|**深度优先搜索/回溯**|
+|[0491.递增子序列](https://github.com/youngyangyang04/leetcode/blob/master/problems/0491.递增子序列.md) |深度优先搜索 |中等|**深度优先搜索/回溯算法**|
|[0541.反转字符串II](https://github.com/youngyangyang04/leetcode/blob/master/problems/0541.反转字符串II.md) |字符串 |简单| **模拟**|
|[0575.分糖果](https://github.com/youngyangyang04/leetcode/blob/master/problems/0575.分糖果.md) |哈希表 |简单|**哈希**|
|[0617.合并二叉树](https://github.com/youngyangyang04/leetcode/blob/master/problems/0617.合并二叉树.md) |树 |简单|**递归** **迭代**|
diff --git a/pics/657.机器人能否返回原点.png b/pics/657.机器人能否返回原点.png
new file mode 100644
index 00000000..6ea5b69b
Binary files /dev/null and b/pics/657.机器人能否返回原点.png differ
diff --git a/problems/0017.电话号码的字母组合.md b/problems/0017.电话号码的字母组合.md
index e4257e24..c421c752 100644
--- a/problems/0017.电话号码的字母组合.md
+++ b/problems/0017.电话号码的字母组合.md
@@ -39,17 +39,17 @@ https://leetcode-cn.com/problems/letter-combinations-of-a-phone-number/
**那么在来讲一讲回溯法,回溯法的模板如下:**
```
-回溯函数() {
+backtracking() {
if (终止条件) {
存放结果;
}
for (枚举同一个位置的所有可能性,可以想成节点孩子的数量) {
- 递归,处理下一个孩子;
- (递归的下面就是回溯的过程);
+ 递归,处理节点;
+ backtracking();
+ 回溯,撤销处理结果
}
}
-
```
按照这个模板,不难写出如下代码:
diff --git a/problems/0018.四数之和.md b/problems/0018.四数之和.md
index ac1f699e..34430d1c 100644
--- a/problems/0018.四数之和.md
+++ b/problems/0018.四数之和.md
@@ -40,7 +40,22 @@ https://leetcode-cn.com/problems/4sum/
而[四数相加II](https://mp.weixin.qq.com/s/Ue8pKKU5hw_m-jPgwlHcbA)是四个独立的数组,只要找到A[i] + B[j] + C[k] + D[l] = 0就可以,不用考虑有重复的四个元素相加等于0的情况,所以相对于本题还是简单了不少!
-大家解决一下这两道题目就能感受出来难度的差异。
+我们来回顾一下,几道题目使用了双指针法。
+
+双指针法将时间复杂度O(n^2)的解法优化为 O(n)的解法。也就是降一个数量级,题目如下:
+* [0027.移除元素](https://mp.weixin.qq.com/s/wj0T-Xs88_FHJFwayElQlA)
+* [15.三数之和](https://mp.weixin.qq.com/s/r5cgZFu0tv4grBAexdcd8A)
+* [18.四数之和](https://mp.weixin.qq.com/s/nQrcco8AZJV1pAOVjeIU_g)
+
+双指针来记录前后指针实现链表反转:
+
+* [206.反转链表](https://mp.weixin.qq.com/s/pnvVP-0ZM7epB8y3w_Njwg)
+
+使用双指针来确定有环:
+
+* [142题.环形链表II](https://mp.weixin.qq.com/s/_QVP3IkRZWx9zIpQRgajzA)
+
+双指针法在数组和链表中还有很多应用,后面还会介绍到。
# C++代码
```
diff --git a/problems/0027.移除元素.md b/problems/0027.移除元素.md
index 1ce8fe40..7700fcf3 100644
--- a/problems/0027.移除元素.md
+++ b/problems/0027.移除元素.md
@@ -39,7 +39,7 @@ https://leetcode-cn.com/problems/remove-element/
删除过程如下:
-
+
很明显暴力解法的时间复杂度是O(n^2),这道题目暴力解法在leetcode上是可以过的。
@@ -73,7 +73,7 @@ public:
删除过程如下:
-
+
**双指针法(快慢指针法)在数组和链表的操作中是非常常见的,很多考察数组和链表操作的面试题,都使用双指针法。**
diff --git a/problems/0059.螺旋矩阵II.md b/problems/0059.螺旋矩阵II.md
index 48c4025c..c5c3301a 100644
--- a/problems/0059.螺旋矩阵II.md
+++ b/problems/0059.螺旋矩阵II.md
@@ -1,26 +1,66 @@
> 笔者在BAT从事技术研发多年,利用工作之余重刷leetcode,更多原创文章请关注公众号「代码随想录」。
-## 题目地址
+# 题目地址
https://leetcode-cn.com/problems/spiral-matrix-ii/
-## 思路
+> 一进循环深似海,从此offer是路人
-模拟顺时针画矩阵的过程
+# 题目59.螺旋矩阵II
-填充上行从左到右
-填充右列从上到下
-填充下行从右到左
-填充左列从下到上
+给定一个正整数 n,生成一个包含 1 到 n2 所有元素,且元素按顺时针顺序螺旋排列的正方形矩阵。
-在模拟的过程中最重要的思想是**保持画每一条边的原则一致,即每次都是左闭右开的原则**,如果所示
+示例:
+
+输入: 3
+输出:
+[
+ [ 1, 2, 3 ],
+ [ 8, 9, 4 ],
+ [ 7, 6, 5 ]
+]
+
+# 思路
+
+这道题目可以说在面试中出现频率较高的题目,**本题并不涉及到什么算法,就是模拟过程,但却十分考察对代码的掌控能力。**
+
+要如何画出这个螺旋排列的正方形矩阵呢?
+
+相信很多同学刚开始做这种题目的时候,上来就是一波判断猛如虎。
+
+结果运行的时候各种问题,然后开始各种修修补补,最后发现改了这里哪里有问题,改了那里这里又跑不起来了。
+
+大家还记得我们在这篇文章[数组:每次遇到二分法,都是一看就会,一写就废](https://mp.weixin.qq.com/s/fCf5QbPDtE6SSlZ1yh_q8Q)中讲解了二分法,提到如果要写出正确的二分法一定要坚持**循环不变量原则**。
+
+而求解本题依然是要坚持循环不变量原则。
+
+模拟顺时针画矩阵的过程:
+
+* 填充上行从左到右
+* 填充右列从上到下
+* 填充下行从右到左
+* 填充左列从下到上
+
+由外向内一圈一圈这么画下去。
+
+可以发现这里的边界条件非常多,在一个循环中,如此多的边界条件,如果不按照固定规则来遍历,那就是**一进循环深似海,从此offer是路人**。
+
+这里一圈下来,我们要画每四条边,这四条边怎么画,每画一条边都要坚持一致的左闭右开,或者左开又闭的原则,这样这一圈才能按照统一的规则画下来。
+
+那么我按照左闭右开的原则,来画一圈,大家看一下:
-很多同学,做这道题目之所以一直写不好,代码越写越乱,就是因为 在画每一条边的时候,没有保证统一原则
+这里每一种颜色,代表一条边,我们遍历的长度,可以看出每一个拐角处的处理规则,拐角处让给新的一条边来继续画。
-例如:模拟矩阵上边的时候 左闭右开,然后模拟矩阵右列的时候又开始了左闭又闭,那岂能不乱
+这也是坚持了每条边左闭右开的原则。
-## 解法
+一些同学做这道题目之所以一直写不好,代码越写越乱。
+
+就是因为在画每一条边的时候,一会左开又闭,一会左闭右闭,一会又来左闭右开,岂能不乱。
+
+代码如下,已经详细注释了每一步的目的,可以看出while循环里判断的情况是很多的,代码里处理的原则也是统一的左闭右开。
+
+# C++代码
```C++
class Solution {
diff --git a/problems/0209.长度最小的子数组.md b/problems/0209.长度最小的子数组.md
index 47049a0a..f206d641 100644
--- a/problems/0209.长度最小的子数组.md
+++ b/problems/0209.长度最小的子数组.md
@@ -1,19 +1,25 @@
## 题目地址
https://leetcode-cn.com/problems/minimum-size-subarray-sum/
-## 思路
+> 滑动窗口拯救了你
+
+# 题目209.长度最小的子数组
+
+给定一个含有 n 个正整数的数组和一个正整数 s ,找出该数组中满足其和 ≥ s 的长度最小的 连续 子数组,并返回其长度。如果不存在符合条件的子数组,返回 0。
+
+示例:
+
+输入:s = 7, nums = [2,3,1,2,4,3]
+输出:2
+解释:子数组 [4,3] 是该条件下的长度最小的子数组。
+
+
+# 暴力解法
+
这道题目暴力解法当然是 两个for循环,然后不断的寻找符合条件的子序列,时间复杂度很明显是O(n^2) 。
-还可以使用滑动窗口的细想来做这道题。所谓滑动窗口,**就是不断的调节子序列的起始位置,从而得出我们要想的结果**
-
-这里还是以题目中的示例来举例,s=7, 数组是 2,3,1,2,4,3,来看一下动画效果:
-
-
-
代码如下:
-## 暴力解法
-
```
class Solution {
public:
@@ -27,7 +33,6 @@ public:
sum += nums[j];
if (sum >= s) { // 一旦发现子序列和超过了s,更新result
subLength = j - i + 1; // 取子序列的长度
- // result取 result和subLength最小的那个
result = result < subLength ? result : subLength;
break; // 因为我们是找符合条件最短的子序列,所以一旦符合条件就break
}
@@ -38,12 +43,45 @@ public:
}
};
```
+时间复杂度:O(n^2)
+空间复杂度:O(1)
-## 滑动窗口
+# 滑动窗口
+
+接下来就开始介绍数组操作中另一个重要的方法:**滑动窗口**。
+
+所谓滑动窗口,**就是不断的调节子序列的起始位置和终止位置,从而得出我们要想的结果**。
+
+这里还是以题目中的示例来举例,s=7, 数组是 2,3,1,2,4,3,来看一下查找的过程:
+
+
+
+最后找到 4,3 是最短距离。
+
+其实从动画中可以发现滑动窗口也可以理解为双指针法的一种!只不过这种解法更像是一个窗口的移动,所以叫做滑动窗口更适合一些。
+
+在本题中实现滑动窗口,主要确定如下三点:
+
+* 窗口内是什么?
+* 如何移动窗口的起始位置?
+* 如何移动窗口的结束位置?
+
+窗口就是 满足其和 ≥ s 的长度最小的 连续 子数组。
+
+窗口的起始位置如何移动:如果当前窗口的值大于s了,窗口就要向前移动了(也就是该缩小了)。
+
+窗口的结束位置如何移动:窗口的结束位置就是遍历数组的指针,窗口的起始位置设置为数组的起始位置就可以了。
+
+解题的关键在于 窗口的起始位置如何移动,如图所示:
+
+可以发现**滑动窗口的精妙之处在于根据当前子序列和大小的情况,不断调节子序列的起始位置。从而将O(n^2)的暴力解法降为O(n)。**
+
+
+# C++滑动窗口代码
+
```
-// 滑动窗口
class Solution {
public:
int minSubArrayLen(int s, vector& nums) {
@@ -56,7 +94,6 @@ public:
// 注意这里使用while,每次更新 i(起始位置),并不断比较子序列是否符合条件
while (sum >= s) {
subLength = (j - i + 1); // 取子序列的长度
- // result取 result和subLength最小的那个
result = result < subLength ? result : subLength;
sum -= nums[i++]; // 这里体现出滑动窗口的精髓之处,不断变更i(子序列的起始位置)
}
@@ -67,5 +104,8 @@ public:
};
```
-> 更过算法干货文章持续更新,可以微信搜索「代码随想录」第一时间围观,关注后,回复「Java」「C++」 「python」「简历模板」「数据结构与算法」等等,就可以获得我多年整理的学习资料。
+时间复杂度:O(n)
+空间复杂度:O(1)
+
+> 更多算法干货文章持续更新,可以微信搜索「代码随想录」第一时间围观,关注后,回复「Java」「C++」 「python」「简历模板」「数据结构与算法」等等,就可以获得我多年整理的学习资料。
diff --git a/problems/0332.重新安排行程.md b/problems/0332.重新安排行程.md
index ab2a9ee2..a2a318e4 100644
--- a/problems/0332.重新安排行程.md
+++ b/problems/0332.重新安排行程.md
@@ -94,15 +94,15 @@ class Solution {
private:
// unordered_map<出发城市, map<到达城市, 航班次数>> targets
unordered_map> targets;
-bool backtracking(int ticketNum, int index, vector& result) {
- if (index == ticketNum + 1) {
+bool backtracking(int ticketNum, vector& result) {
+ if (result.size() == ticketNum + 1) {
return true;
}
for (pair& target : targets[result[result.size() - 1]]) {
if (target.second > 0 ) { // 使用int字段来记录到达城市是否使用过了
result.push_back(target.first);
target.second--;
- if (backtracking(ticketNum, index + 1, result)) return true;
+ if (backtracking(ticketNum, result)) return true;
result.pop_back();
target.second++;
}
@@ -116,9 +116,10 @@ public:
targets[vec[0]][vec[1]]++; // 记录映射关系
}
result.push_back("JFK");
- backtracking(tickets.size(), 1, result);
+ backtracking(tickets.size(), result);
return result;
}
};
+
```
diff --git a/problems/0657.机器人能否返回原点.md b/problems/0657.机器人能否返回原点.md
new file mode 100644
index 00000000..fb74c751
--- /dev/null
+++ b/problems/0657.机器人能否返回原点.md
@@ -0,0 +1,38 @@
+## 题目地址
+https://leetcode-cn.com/problems/robot-return-to-origin/
+
+## 思路
+
+这道题目还是挺简单的,大家不要想复杂了,一波哈希法又一波图论算法啥的,哈哈。
+
+其实就是,x,y坐标,初始为0,然后:
+* if (moves[i] == 'U') y++;
+* if (moves[i] == 'D') y--;
+* if (moves[i] == 'L') x--;
+* if (moves[i] == 'R') x++;
+
+最后判断一下x,y是否回到了(0, 0)位置就可以了。
+
+如图所示:
+
+
+## C++代码
+
+```
+class Solution {
+public:
+ bool judgeCircle(string moves) {
+ int x = 0, y = 0;
+ for (int i = 0; i < moves.size(); i++) {
+ if (moves[i] == 'U') y++;
+ if (moves[i] == 'D') y--;
+ if (moves[i] == 'L') x--;
+ if (moves[i] == 'R') x++;
+ }
+ if (x == 0 && y == 0) return true;
+ return false;
+ }
+};
+```
+
+> 更多算法干货文章持续更新,可以微信搜索「代码随想录」第一时间围观,关注后,回复「Java」「C++」 「python」「简历模板」「数据结构与算法」等等,就可以获得我多年整理的学习资料。