Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

0055.右旋字符串.md:修正文字错误 #2918

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
11 changes: 6 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,13 +69,14 @@

## 前序

* [「代码随想录」学习社区](https://programmercarl.com/other/kstar.html)
* [做项目(多个C++、Java、Go、前端、测开项目)](https://programmercarl.com/other/kstar.html)


* 编程语言
* [C++面试&C++学习指南知识点整理](https://github.com/youngyangyang04/TechCPP)
* [编程语言基础课](https://kamacoder.com/courseshop.php)
* [23种设计模式](https://github.com/youngyangyang04/kama-DesignPattern)
* [大厂算法笔试题](https://kamacoder.com/company.php)

* 工具
* [一站式vim配置](https://github.com/youngyangyang04/PowerVim)
Expand Down Expand Up @@ -384,10 +385,10 @@
8. [图论:孤岛的总面积](./problems/kamacoder/0101.孤岛的总面积.md)
9. [图论:沉没孤岛](./problems/kamacoder/0102.沉没孤岛.md)
10. [图论:水流问题](./problems/kamacoder/0103.水流问题.md)
11. [图论:建造最大岛屿](./problems/kamacoder/0104.建造最大岛屿.md)
12. [图论:字符串接龙](./problems/kamacoder/0110.字符串接龙.md)
13. [图论:有向图的完全可达性](./problems/kamacoder/0105.有向图的完全可达性.md)
14. [图论:岛屿的周长](./problems/kamacoder/0106.岛屿的周长.md)
11. [图论:岛屿的周长](./problems/kamacoder/0106.岛屿的周长.md)
12. [图论:建造最大岛屿](./problems/kamacoder/0104.建造最大岛屿.md)
13. [图论:字符串接龙](./problems/kamacoder/0110.字符串接龙.md)
14. [图论:有向图的完全可达性](./problems/kamacoder/0105.有向图的完全可达性.md)
15. [图论:并查集理论基础](./problems/kamacoder/图论并查集理论基础.md)
16. [图论:寻找存在的路径](./problems/kamacoder/0107.寻找存在的路径.md)
17. [图论:冗余连接](./problems/kamacoder/0108.冗余连接.md)
Expand Down
4 changes: 2 additions & 2 deletions problems/0001.两数之和.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,10 +85,10 @@ map目的用来存放我们访问过的元素,因为遍历数组的时候,

过程如下:

![过程一](https://code-thinking-1253855093.file.myqcloud.com/pics/20220711202638.png)
![过程一](images/0001.两数之和-01.png)


![过程二](https://code-thinking-1253855093.file.myqcloud.com/pics/20230220223536.png)
![过程二](images/0001.两数之和-02.png)

C++代码:

Expand Down
4 changes: 2 additions & 2 deletions problems/0005.最长回文子串.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ dp[i][j]可以初始化为true么? 当然不行,怎能刚开始就全都匹

dp[i + 1][j - 1] 在 dp[i][j]的左下角,如图:

![647.回文子串](https://code-thinking-1253855093.file.myqcloud.com/pics/20210121171032473.jpg)
![647.回文子串](images/0005.最长回文子串-01.jpg)

如果这矩阵是从上到下,从左到右遍历,那么会用到没有计算过的dp[i + 1][j - 1],也就是根据不确定是不是回文的区间[i+1,j-1],来判断了[i,j]是不是回文,那结果一定是不对的。

Expand Down Expand Up @@ -142,7 +142,7 @@ for (int i = s.size() - 1; i >= 0; i--) { // 注意遍历顺序

举例,输入:"aaa",dp[i][j]状态如下:

![647.回文子串1](https://code-thinking-1253855093.file.myqcloud.com/pics/20210121171059951.jpg)
![647.回文子串1](images/0005.最长回文子串-02.jpg)

**注意因为dp[i][j]的定义,所以j一定是大于等于i的,那么在填充dp[i][j]的时候一定是只填充右上半部分**。

Expand Down
2 changes: 1 addition & 1 deletion problems/0015.三数之和.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ public:

动画效果如下:

![15.三数之和](https://code-thinking.cdn.bcebos.com/gifs/15.%E4%B8%89%E6%95%B0%E4%B9%8B%E5%92%8C.gif)
![15.三数之和](images/0015.三数之和-01.gif)

拿这个nums数组来举例,首先将数组排序,然后有一层for循环,i从下标0的地方开始,同时定一个下标left 定义在i+1的位置上,定义下标right 在数组结尾的位置上。

Expand Down
4 changes: 2 additions & 2 deletions problems/0017.电话号码的字母组合.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。

![17.电话号码的字母组合](https://code-thinking-1253855093.file.myqcloud.com/pics/2020102916424043.png)
![17.电话号码的字母组合](images/0017.电话号码的字母组合-01.png)

示例:
* 输入:"23"
Expand Down Expand Up @@ -66,7 +66,7 @@ const string letterMap[10] = {

例如:输入:"23",抽象为树形结构,如图所示:

![17. 电话号码的字母组合](https://code-thinking-1253855093.file.myqcloud.com/pics/20201123200304469.png)
![17. 电话号码的字母组合](images/0017.电话号码的字母组合-02.png)

图中可以看出遍历的深度,就是输入"23"的长度,而叶子节点就是我们要收集的结果,输出["ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf"]。

Expand Down
10 changes: 5 additions & 5 deletions problems/0019.删除链表的倒数第N个节点.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
示例 1:


![19.删除链表的倒数第N个节点](https://code-thinking-1253855093.file.myqcloud.com/pics/20210510085957392.png)
![19.删除链表的倒数第N个节点](images/0019.删除链表的倒数第N个节点-01.png)

输入:head = [1,2,3,4,5], n = 2
输出:[1,2,3,5]
Expand Down Expand Up @@ -51,16 +51,16 @@

* 定义fast指针和slow指针,初始值为虚拟头结点,如图:

<img src='https://code-thinking.cdn.bcebos.com/pics/19.%E5%88%A0%E9%99%A4%E9%93%BE%E8%A1%A8%E7%9A%84%E5%80%92%E6%95%B0%E7%AC%ACN%E4%B8%AA%E8%8A%82%E7%82%B9.png' width=600> </img></div>
<img src='images/0019.删除链表的倒数第N个节点-02.png' width=600> </img></div>

* fast首先走n + 1步 ,为什么是n+1呢,因为只有这样同时移动的时候slow才能指向删除节点的上一个节点(方便做删除操作),如图:
<img src='https://code-thinking.cdn.bcebos.com/pics/19.%E5%88%A0%E9%99%A4%E9%93%BE%E8%A1%A8%E7%9A%84%E5%80%92%E6%95%B0%E7%AC%ACN%E4%B8%AA%E8%8A%82%E7%82%B91.png' width=600> </img></div>
<img src='images/0019.删除链表的倒数第N个节点-03.png' width=600> </img></div>

* fast和slow同时移动,直到fast指向末尾,如题:
<img src='https://code-thinking.cdn.bcebos.com/pics/19.%E5%88%A0%E9%99%A4%E9%93%BE%E8%A1%A8%E7%9A%84%E5%80%92%E6%95%B0%E7%AC%ACN%E4%B8%AA%E8%8A%82%E7%82%B92.png' width=600> </img></div>
<img src='images/0019.删除链表的倒数第N个节点-04.png' width=600> </img></div>
//图片中有错别词:应该将“只到”改为“直到”
* 删除slow指向的下一个节点,如图:
<img src='https://code-thinking.cdn.bcebos.com/pics/19.%E5%88%A0%E9%99%A4%E9%93%BE%E8%A1%A8%E7%9A%84%E5%80%92%E6%95%B0%E7%AC%ACN%E4%B8%AA%E8%8A%82%E7%82%B93.png' width=600> </img></div>
<img src='images/0019.删除链表的倒数第N个节点-05.png' width=600> </img></div>

此时不难写出如下C++代码:

Expand Down
8 changes: 4 additions & 4 deletions problems/0020.有效的括号.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,21 +83,21 @@ cd a/b/c/../../


1. 第一种情况,字符串里左方向的括号多余了 ,所以不匹配。
![括号匹配1](https://code-thinking-1253855093.file.myqcloud.com/pics/2020080915505387.png)
![括号匹配1](images/0020.有效的括号-01.png)

2. 第二种情况,括号没有多余,但是 括号的类型没有匹配上。
![括号匹配2](https://code-thinking-1253855093.file.myqcloud.com/pics/20200809155107397.png)
![括号匹配2](images/0020.有效的括号-02.png)

3. 第三种情况,字符串里右方向的括号多余了,所以不匹配。
![括号匹配3](https://code-thinking-1253855093.file.myqcloud.com/pics/20200809155115779.png)
![括号匹配3](images/0020.有效的括号-03.png)



我们的代码只要覆盖了这三种不匹配的情况,就不会出问题,可以看出 动手之前分析好题目的重要性。

动画如下:

![20.有效括号](https://code-thinking.cdn.bcebos.com/gifs/20.有效括号.gif)
![20.有效括号](images/0020.有效的括号-04.gif)


第一种情况:已经遍历完了字符串,但是栈不为空,说明有相应的左括号没有右括号来匹配,所以return false
Expand Down
10 changes: 5 additions & 5 deletions problems/0024.两两交换链表中的节点.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。


<img src='https://code-thinking.cdn.bcebos.com/pics/24.%E4%B8%A4%E4%B8%A4%E4%BA%A4%E6%8D%A2%E9%93%BE%E8%A1%A8%E4%B8%AD%E7%9A%84%E8%8A%82%E7%82%B9-%E9%A2%98%E6%84%8F.jpg' width=600 alt='24.两两交换链表中的节点-题意'> </img></div>
<img src='images/0024.两两交换链表中的节点-01.jpg' width=600 alt='24.两两交换链表中的节点-题意'> </img></div>

## 算法公开课

Expand All @@ -33,16 +33,16 @@

初始时,cur指向虚拟头结点,然后进行如下三步:

![24.两两交换链表中的节点1](https://code-thinking.cdn.bcebos.com/pics/24.%E4%B8%A4%E4%B8%A4%E4%BA%A4%E6%8D%A2%E9%93%BE%E8%A1%A8%E4%B8%AD%E7%9A%84%E8%8A%82%E7%82%B91.png)
![24.两两交换链表中的节点1](images/0024.两两交换链表中的节点-02.png)

操作之后,链表如下:

![24.两两交换链表中的节点2](https://code-thinking.cdn.bcebos.com/pics/24.%E4%B8%A4%E4%B8%A4%E4%BA%A4%E6%8D%A2%E9%93%BE%E8%A1%A8%E4%B8%AD%E7%9A%84%E8%8A%82%E7%82%B92.png)
![24.两两交换链表中的节点2](images/0024.两两交换链表中的节点-03.png)

看这个可能就更直观一些了:


![24.两两交换链表中的节点3](https://code-thinking.cdn.bcebos.com/pics/24.%E4%B8%A4%E4%B8%A4%E4%BA%A4%E6%8D%A2%E9%93%BE%E8%A1%A8%E4%B8%AD%E7%9A%84%E8%8A%82%E7%82%B93.png)
![24.两两交换链表中的节点3](images/0024.两两交换链表中的节点-04.png)

对应的C++代码实现如下: (注释中详细和如上图中的三步做对应)

Expand Down Expand Up @@ -83,7 +83,7 @@ public:

心想应该没有更好的方法了吧,也就 $O(n)$ 的时间复杂度,重复提交几次,这样了:

![24.两两交换链表中的节点](https://code-thinking.cdn.bcebos.com/pics/24.%E4%B8%A4%E4%B8%A4%E4%BA%A4%E6%8D%A2%E9%93%BE%E8%A1%A8%E4%B8%AD%E7%9A%84%E8%8A%82%E7%82%B9.png)
![24.两两交换链表中的节点](images/0024.两两交换链表中的节点-05.png)

力扣上的统计如果两份代码是 100ms 和 300ms的耗时,其实是需要注意的。

Expand Down
4 changes: 2 additions & 2 deletions problems/0027.移除元素.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@

删除过程如下:

![27.移除元素-暴力解法](https://code-thinking.cdn.bcebos.com/gifs/27.%E7%A7%BB%E9%99%A4%E5%85%83%E7%B4%A0-%E6%9A%B4%E5%8A%9B%E8%A7%A3%E6%B3%95.gif)
![27.移除元素-暴力解法](images/0027.移除元素-01.gif)

很明显暴力解法的时间复杂度是O(n^2),这道题目暴力解法在leetcode上是可以过的。

Expand Down Expand Up @@ -89,7 +89,7 @@ public:

删除过程如下:

![27.移除元素-双指针法](https://code-thinking.cdn.bcebos.com/gifs/27.%E7%A7%BB%E9%99%A4%E5%85%83%E7%B4%A0-%E5%8F%8C%E6%8C%87%E9%92%88%E6%B3%95.gif)
![27.移除元素-双指针法](images/0027.移除元素-02.gif)

很多同学不了解

Expand Down
20 changes: 10 additions & 10 deletions problems/0028.实现strStr.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ next数组就是一个前缀表(prefix table)。

如动画所示:

![KMP详解1](https://code-thinking.cdn.bcebos.com/gifs/KMP%E7%B2%BE%E8%AE%B21.gif)
![KMP详解1](images/0028.实现strStr-01.gif)

动画里,我特意把 子串`aa` 标记上了,这是有原因的,大家先注意一下,后面还会说到。

Expand Down Expand Up @@ -149,11 +149,11 @@ next数组就是一个前缀表(prefix table)。
这就是前缀表,那为啥就能告诉我们 上次匹配的位置,并跳过去呢?

回顾一下,刚刚匹配的过程在下标5的地方遇到不匹配,模式串是指向f,如图:
<img src='https://code-thinking.cdn.bcebos.com/pics/KMP%E7%B2%BE%E8%AE%B21.png' width=600 alt='KMP精讲1'> </img></div>
<img src='images/0028.实现strStr-02.png' width=600 alt='KMP精讲1'> </img></div>


然后就找到了下标2,指向b,继续匹配:如图:
<img src='https://code-thinking.cdn.bcebos.com/pics/KMP%E7%B2%BE%E8%AE%B22.png' width=600 alt='KMP精讲2'> </img></div>
<img src='images/0028.实现strStr-03.png' width=600 alt='KMP精讲2'> </img></div>

以下这句话,对于理解为什么使用前缀表可以告诉我们匹配失败之后跳到哪里重新匹配 非常重要!

Expand All @@ -169,15 +169,15 @@ next数组就是一个前缀表(prefix table)。

如图:

<img src='https://code-thinking.cdn.bcebos.com/pics/KMP%E7%B2%BE%E8%AE%B25.png' width=600 alt='KMP精讲5'> </img></div>
<img src='images/0028.实现strStr-04.png' width=600 alt='KMP精讲5'> </img></div>

长度为前1个字符的子串`a`,最长相同前后缀的长度为0。(注意字符串的**前缀是指不包含最后一个字符的所有以第一个字符开头的连续子串**;**后缀是指不包含第一个字符的所有以最后一个字符结尾的连续子串**。)

<img src='https://code-thinking.cdn.bcebos.com/pics/KMP%E7%B2%BE%E8%AE%B26.png' width=600 alt='KMP精讲6'> </img></div>
<img src='images/0028.实现strStr-05.png' width=600 alt='KMP精讲6'> </img></div>

长度为前2个字符的子串`aa`,最长相同前后缀的长度为1。

<img src='https://code-thinking.cdn.bcebos.com/pics/KMP%E7%B2%BE%E8%AE%B27.png' width=600 alt='KMP精讲7'> </img></div>
<img src='images/0028.实现strStr-06.png' width=600 alt='KMP精讲7'> </img></div>

长度为前3个字符的子串`aab`,最长相同前后缀的长度为0。

Expand All @@ -187,13 +187,13 @@ next数组就是一个前缀表(prefix table)。
长度为前6个字符的子串`aabaaf`,最长相同前后缀的长度为0。

那么把求得的最长相同前后缀的长度就是对应前缀表的元素,如图:
<img src='https://code-thinking.cdn.bcebos.com/pics/KMP%E7%B2%BE%E8%AE%B28.png' width=600 alt='KMP精讲8'> </img></div>
<img src='images/0028.实现strStr-07.png' width=600 alt='KMP精讲8'> </img></div>

可以看出模式串与前缀表对应位置的数字表示的就是:**下标i之前(包括i)的字符串中,有多大长度的相同前缀后缀。**

再来看一下如何利用 前缀表找到 当字符不匹配的时候应该指针应该移动的位置。如动画所示:

![KMP精讲2](https://code-thinking.cdn.bcebos.com/gifs/KMP%E7%B2%BE%E8%AE%B22.gif)
![KMP精讲2](images/0028.实现strStr-08.gif)

找到的不匹配的位置, 那么此时我们要看它的前一个字符的前缀表的数值是多少。

Expand Down Expand Up @@ -227,7 +227,7 @@ next数组就可以是前缀表,但是很多实现都是把前缀表统一减

匹配过程动画如下:

![KMP精讲4](https://code-thinking.cdn.bcebos.com/gifs/KMP%E7%B2%BE%E8%AE%B24.gif)
![KMP精讲4](images/0028.实现strStr-09.gif)

### 时间复杂度分析

Expand Down Expand Up @@ -334,7 +334,7 @@ void getNext(int* next, const string& s){

代码构造next数组的逻辑流程动画如下:

![KMP精讲3](https://code-thinking.cdn.bcebos.com/gifs/KMP%E7%B2%BE%E8%AE%B23.gif)
![KMP精讲3](images/0028.实现strStr-10.gif)

得到了next数组之后,就要用这个来做匹配了。

Expand Down
2 changes: 1 addition & 1 deletion problems/0031.下一个排列.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@

以求1243为例,流程如图:

<img src='https://code-thinking.cdn.bcebos.com/pics/31.下一个排列.png' width=600> </img></div>
<img src='images/0031.下一个排列-01.png' width=600> </img></div>

对应的C++代码如下:

Expand Down
10 changes: 5 additions & 5 deletions problems/0035.搜索插入位置.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@

这道题目,要在数组中插入目标值,无非是这四种情况。

![35_搜索插入位置3](https://code-thinking-1253855093.file.myqcloud.com/pics/20201216232148471.png)
![35_搜索插入位置3](images/0035.搜索插入位置-01.png)

* 目标值在数组所有元素之前
* 目标值等于数组中某一个元素
Expand Down Expand Up @@ -84,14 +84,14 @@ public:

效率如下:

![35_搜索插入位置](https://code-thinking-1253855093.file.myqcloud.com/pics/20201216232127268.png)
![35_搜索插入位置](images/0035.搜索插入位置-02.png)

### 二分法

既然暴力解法的时间复杂度是O(n),就要尝试一下使用二分查找法。


![35_搜索插入位置4](https://code-thinking-1253855093.file.myqcloud.com/pics/202012162326354.png)
![35_搜索插入位置4](images/0035.搜索插入位置-03.png)

大家注意这道题目的前提是数组是有序数组,这也是使用二分查找的基础条件。

Expand All @@ -101,7 +101,7 @@ public:

大体讲解一下二分法的思路,这里来举一个例子,例如在这个数组中,使用二分法寻找元素为5的位置,并返回其下标。

![35_搜索插入位置5](https://code-thinking-1253855093.file.myqcloud.com/pics/20201216232659199.png)
![35_搜索插入位置5](images/0035.搜索插入位置-04.png)

二分查找涉及的很多的边界条件,逻辑比较简单,就是写不好。

Expand Down Expand Up @@ -152,7 +152,7 @@ public:
* 空间复杂度:O(1)

效率如下:
![35_搜索插入位置2](https://code-thinking-1253855093.file.myqcloud.com/pics/2020121623272877.png)
![35_搜索插入位置2](images/0035.搜索插入位置-05.png)

### 二分法第二种写法

Expand Down
8 changes: 4 additions & 4 deletions problems/0037.解数独.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,11 @@
数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。
空白格用 '.' 表示。

![解数独](https://code-thinking-1253855093.file.myqcloud.com/pics/202011171912586.png)
![解数独](images/0037.解数独-01.png)

一个数独。

![解数独](https://code-thinking-1253855093.file.myqcloud.com/pics/20201117191340669.png)
![解数独](images/0037.解数独-02.png)

答案被标成红色。

Expand Down Expand Up @@ -54,7 +54,7 @@

因为这个树形结构太大了,我抽取一部分,如图所示:

![37.解数独](https://code-thinking-1253855093.file.myqcloud.com/pics/2020111720451790-20230310131816104.png)
![37.解数独](images/0037.解数独-03.png)


### 回溯三部曲
Expand Down Expand Up @@ -85,7 +85,7 @@ bool backtracking(vector<vector<char>>& board)

* 递归单层搜索逻辑

![37.解数独](https://code-thinking-1253855093.file.myqcloud.com/pics/2020111720451790-20230310131822254.png)
![37.解数独](images/0037.解数独-04.png)

在树形图中可以看出我们需要的是一个二维的递归 (一行一列)

Expand Down
8 changes: 4 additions & 4 deletions problems/0039.组合总和.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ candidates 中的数字可以无限制重复被选取。

本题搜索的过程抽象成树形结构如下:

![39.组合总和](https://code-thinking-1253855093.file.myqcloud.com/pics/20201223170730367.png)
![39.组合总和](images/0039.组合总和-01.png)
注意图中叶子节点的返回条件,因为本题没有组合数量要求,仅仅是总和的限制,所以递归没有层数的限制,只要选取的元素总和超过target,就返回!

而在[77.组合](https://programmercarl.com/0077.组合.html)和[216.组合总和III](https://programmercarl.com/0216.组合总和III.html) 中都可以知道要递归K层,因为要取k个元素的组合。
Expand Down Expand Up @@ -87,7 +87,7 @@ void backtracking(vector<int>& candidates, int target, int sum, int startIndex)

在如下树形结构中:

![39.组合总和](https://code-thinking-1253855093.file.myqcloud.com/pics/20201223170730367-20230310135337214.png)
![39.组合总和](images/0039.组合总和-02.png)

从叶子节点可以清晰看到,终止只有两种情况,sum大于target和sum等于target。

Expand Down Expand Up @@ -160,7 +160,7 @@ public:

在这个树形结构中:

![39.组合总和](https://code-thinking-1253855093.file.myqcloud.com/pics/20201223170730367-20230310135342472.png)
![39.组合总和](images/0039.组合总和-03.png)

以及上面的版本一的代码大家可以看到,对于sum已经大于target的情况,其实是依然进入了下一层递归,只是下一层递归结束判断的时候,会判断sum > target的话就返回。

Expand All @@ -173,7 +173,7 @@ public:
如图:


![39.组合总和1](https://code-thinking-1253855093.file.myqcloud.com/pics/20201223170809182.png)
![39.组合总和1](images/0039.组合总和-04.png)

for循环剪枝代码如下:

Expand Down
Loading