This commit is contained in:
youngyangyang04
2020-08-30 07:29:27 +08:00
parent ca02da9c23
commit 6f833722ae
9 changed files with 184 additions and 42 deletions

View File

@ -39,17 +39,17 @@ https://leetcode-cn.com/problems/letter-combinations-of-a-phone-number/
**那么在来讲一讲回溯法,回溯法的模板如下:**
```
回溯函数() {
backtracking() {
if (终止条件) {
存放结果;
}
for (枚举同一个位置的所有可能性,可以想成节点孩子的数量) {
递归,处理下一个孩子;
(递归的下面就是回溯的过程);
递归,处理节点;
backtracking();
回溯,撤销处理结果
}
}
```
按照这个模板,不难写出如下代码:

View File

@ -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++代码
```

View File

@ -39,7 +39,7 @@ https://leetcode-cn.com/problems/remove-element/
删除过程如下:
<img src='../../World/Writing/leetcode/video/27.移除元素-暴力解法.gif' width=600> </img></div>
<img src='../video/27.移除元素-暴力解法.gif' width=600> </img></div>
很明显暴力解法的时间复杂度是O(n^2)这道题目暴力解法在leetcode上是可以过的。
@ -73,7 +73,7 @@ public:
删除过程如下:
<img src='../../World/Writing/leetcode/video/27.移除元素-双指针法.gif' width=600> </img></div>
<img src='../video/27.移除元素-双指针法.gif' width=600> </img></div>
**双指针法(快慢指针法)在数组和链表的操作中是非常常见的,很多考察数组和链表操作的面试题,都使用双指针法。**

View File

@ -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是路人**。
这里一圈下来,我们要画每四条边,这四条边怎么画,每画一条边都要坚持一致的左闭右开,或者左开又闭的原则,这样这一圈才能按照统一的规则画下来。
那么我按照左闭右开的原则,来画一圈,大家看一下:
<img src='../pics/螺旋矩阵.png' width=600> </img></div>
很多同学,做这道题目之所以一直写不好,代码越写越乱,就是因为 在画每一条边的时候,没有保证统一原则
这里每一种颜色,代表一条边,我们遍历的长度,可以看出每一个拐角处的处理规则,拐角处让给新的一条边来继续画。
例如:模拟矩阵上边的时候 左闭右开,然后模拟矩阵右列的时候又开始了左闭又闭,那岂能不乱
这也是坚持了每条边左闭右开的原则。
## 解法
一些同学做这道题目之所以一直写不好,代码越写越乱。
就是因为在画每一条边的时候,一会左开又闭,一会左闭右闭,一会又来左闭右开,岂能不乱。
代码如下已经详细注释了每一步的目的可以看出while循环里判断的情况是很多的代码里处理的原则也是统一的左闭右开。
# C++代码
```C++
class Solution {

View File

@ -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 数组是 231243来看一下动画效果
<video src='../video/209.长度最小的子数组.mp4' controls='controls' width='640' height='320' autoplay='autoplay'> Your browser does not support the video tag.</video></div>
代码如下:
## 暴力解法
```
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 数组是 231243来看一下查找的过程
<img src='../video/209.长度最小的子数组.gif' width=600> </img></div>
最后找到 43 是最短距离。
其实从动画中可以发现滑动窗口也可以理解为双指针法的一种!只不过这种解法更像是一个窗口的移动,所以叫做滑动窗口更适合一些。
在本题中实现滑动窗口,主要确定如下三点:
* 窗口内是什么?
* 如何移动窗口的起始位置?
* 如何移动窗口的结束位置?
窗口就是 满足其和 ≥ s 的长度最小的 连续 子数组。
窗口的起始位置如何移动如果当前窗口的值大于s了窗口就要向前移动了也就是该缩小了
窗口的结束位置如何移动:窗口的结束位置就是遍历数组的指针,窗口的起始位置设置为数组的起始位置就可以了。
解题的关键在于 窗口的起始位置如何移动,如图所示:
<img src='../pics/leetcode_209.png' width=600> </img></div>
可以发现**滑动窗口的精妙之处在于根据当前子序列和大小的情况不断调节子序列的起始位置。从而将O(n^2)的暴力解法降为O(n)。**
# C++滑动窗口代码
```
// 滑动窗口
class Solution {
public:
int minSubArrayLen(int s, vector<int>& 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」「简历模板」「数据结构与算法」等等就可以获得我多年整理的学习资料。

View File

@ -94,15 +94,15 @@ class Solution {
private:
// unordered_map<出发城市, map<到达城市, 航班次数>> targets
unordered_map<string, map<string, int>> targets;
bool backtracking(int ticketNum, int index, vector<string>& result) {
if (index == ticketNum + 1) {
bool backtracking(int ticketNum, vector<string>& result) {
if (result.size() == ticketNum + 1) {
return true;
}
for (pair<const string, int>& 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;
}
};
```

View File

@ -0,0 +1,38 @@
## 题目地址
https://leetcode-cn.com/problems/robot-return-to-origin/
## 思路
这道题目还是挺简单的,大家不要想复杂了,一波哈希法又一波图论算法啥的,哈哈。
其实就是xy坐标初始为0然后
* if (moves[i] == 'U') y++;
* if (moves[i] == 'D') y--;
* if (moves[i] == 'L') x--;
* if (moves[i] == 'R') x++;
最后判断一下xy是否回到了(0, 0)位置就可以了。
如图所示:
<img src='../pics/657.机器人能否返回原点.png' width=600> </img></div>
## 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」「简历模板」「数据结构与算法」等等就可以获得我多年整理的学习资料。