Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit d5a824d

Browse files
Update
1 parent 1d5570b commit d5a824d

8 files changed

+200
-25
lines changed

README.md

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
* [哈希表:这道题目我做过?](https://mp.weixin.qq.com/s/sYZIR4dFBrw_lr3eJJnteQ)
4141
* [哈希表:解决了两数之和,那么能解决三数之和么?](https://mp.weixin.qq.com/s/r5cgZFu0tv4grBAexdcd8A)
4242
* [双指针法:一样的道理,能解决四数之和](https://mp.weixin.qq.com/s/nQrcco8AZJV1pAOVjeIU_g)
43+
* [必须掌握的数组理论知识](https://mp.weixin.qq.com/s/X7R55wSENyY62le0Fiawsg)
4344
* [数组:每次遇到二分法,都是一看就会,一写就废](https://mp.weixin.qq.com/s/fCf5QbPDtE6SSlZ1yh_q8Q)
4445
* [数组:就移除个元素很难么?](https://mp.weixin.qq.com/s/wj0T-Xs88_FHJFwayElQlA)
4546
* [数组:滑动窗口拯救了你](https://mp.weixin.qq.com/s/UrZynlqi4QpyLlLhBPglyg)
@@ -331,22 +332,6 @@ vector<vector<int>> levelOrder(TreeNode* root) {
331332
332333
```
333334

334-
## 回溯算法
335-
336-
```
337-
backtracking() {
338-
if (终止条件) {
339-
存放结果;
340-
}
341-
342-
for (选择:选择列表(可以想成树中节点孩子的数量)) {
343-
递归,处理节点;
344-
backtracking();
345-
回溯,撤销处理结果
346-
}
347-
}
348-
349-
```
350335

351336

352337
可以直接解决如下题目:
@@ -376,6 +361,23 @@ int countNodes(TreeNode* root) {
376361
}
377362
```
378363

364+
## 回溯算法
365+
366+
```
367+
backtracking() {
368+
if (终止条件) {
369+
存放结果;
370+
}
371+
372+
for (选择:选择列表(可以想成树中节点孩子的数量)) {
373+
递归,处理节点;
374+
backtracking();
375+
回溯,撤销处理结果
376+
}
377+
}
378+
379+
```
380+
379381
(持续补充ing)
380382

381383
# LeetCode 最强题解:
@@ -435,6 +437,7 @@ int countNodes(TreeNode* root) {
435437
|[0237.删除链表中的节点](https://github.com/youngyangyang04/leetcode/blob/master/problems/0237.删除链表中的节点.md) |链表 |简单| **原链表移除** **添加虚拟节点** 递归|
436438
|[0239.滑动窗口最大值](https://github.com/youngyangyang04/leetcode/blob/master/problems/0239.滑动窗口最大值.md) |滑动窗口/队列 |困难| **单调队列**|
437439
|[0242.有效的字母异位词](https://github.com/youngyangyang04/leetcode/blob/master/problems/0242.有效的字母异位词.md) |哈希表 |简单| **哈希**|
440+
|[0257.二叉树的所有路径](https://github.com/youngyangyang04/leetcode/blob/master/problems/0257.二叉树的所有路径.md) ||简单| **递归/回溯**|
438441
|[0332.重新安排行程](https://github.com/youngyangyang04/leetcode/blob/master/problems/0332.重新安排行程.md) |深度优先搜索/回溯 |中等| **深度优先搜索/回溯算法**|
439442
|[0344.反转字符串](https://github.com/youngyangyang04/leetcode/blob/master/problems/0344.反转字符串.md) |字符串 |简单| **双指针**|
440443
|[0347.前K个高频元素](https://github.com/youngyangyang04/leetcode/blob/master/problems/0347.前K个高频元素.md) |哈希/堆/优先级队列 |中等| **哈希/优先级队列**|
51.8 KB
Loading

problems/0028.实现strStr().md

Lines changed: 167 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,178 @@ https://leetcode-cn.com/problems/implement-strstr/
44

55
## 思路
66

7-
KMP 经典算法
8-
KMP的经典思想是:当出现字符串不匹配时,可以记录一部分之前已经匹配的文本内容,利用这些信息避免从头再去做匹配。
7+
本题是KMP 经典题目。
8+
9+
KMP的经典思想就是:**当出现字符串不匹配时,可以记录一部分之前已经匹配的文本内容,利用这些信息避免从头再去做匹配。**
10+
11+
如果对KMP理论基础还不够了解的同学请看[]()
12+
13+
定义一个函数getNext来构建next数组, 函数参数为指向next数组的指针,和一个字符串。 然后在函数里完成对next数组的构建。
14+
15+
16+
这其实就是计算模式串s,前缀表的过程。 主要有如下三步:
17+
18+
1. 初始化
19+
2. 处理前后缀不相同的情况
20+
3. 处理前后缀相同的情况
21+
22+
1. 初始化:
23+
24+
定义两个指针i和j,j指向前缀终止位置(严格来说是终止位置减一的位置),i指向后缀终止位置(与j同理)。
25+
26+
然后还要对next数组进行初始化赋值,如下:
27+
28+
```
29+
        int j = -1;
30+
        next[0] = j;
31+
```
32+
33+
j 为什么要初始化为 -1呢,因为之前说过 前缀表要统一减一的操作,所以j初始化为-1。
34+
35+
next[i] 表示 i(包括i)之前最长相等的前后缀长度(其实就是j)
36+
37+
所以初始化next[0] = j 。
38+
39+
40+
2. 处理前后缀不相同的情况
41+
42+
43+
因为j初始化为-1,那么i就从1开始,进行s[i] 与 s[j+1]的比较。
44+
45+
所以遍历模式串s的循环下表i 要从 1开始,代码如下:
46+
47+
```
48+
        for(int i = 1; i < s.size(); i++) {
49+
```
50+
51+
如果 s[i] 与 s[j+1]不相同,也就是遇到 前后缀末尾不相同的情况,就要向前回溯。
52+
53+
怎么回溯呢?
54+
55+
next[j]就是记录着j(包括j)之前的子串的相同前后缀的长度。
56+
57+
那么 s[i] 与 s[j+1] 不相同,就要找 j+1前一个元素在next数组里的值(就是next[j])。
58+
59+
所以,处理前后缀不相同的情况代码如下:
60+
61+
```
62+
while (j >= 0 && s[i] != s[j + 1]) { // 前后缀不相同了
63+
    j = next[j]; // 向前回溯
64+
}
65+
```
66+
67+
3. 处理前后缀相同的情况
68+
69+
如果s[i] 与 s[j + 1] 相同,那么就同时向后移动i 和j 说明找到了相同的前后缀,同时还要将j(前缀的长度)赋给next[i], 因为next[i]要记录相同前后缀的长度。
70+
71+
代码如下:
72+
73+
```
74+
if (s[i] == s[j + 1]) { // 找到相同的前后缀
75+
    j++;
76+
}
77+
next[i] = j;
78+
```
79+
80+
最后整体构建next数组的函数代码如下:
81+
82+
```
83+
    void getNext(int* next, const string& s){
84+
        int j = -1;
85+
        next[0] = j;
86+
        for(int i = 1; i < s.size(); i++) { // 注意i从1开始
87+
            while (j >= 0 && s[i] != s[j + 1]) { // 前后缀不相同了
88+
                j = next[j]; // 向前回溯
89+
            }
90+
            if (s[i] == s[j + 1]) { // 找到相同的前后缀
91+
                j++;
92+
            }
93+
            next[i] = j; // 将j(前缀的长度)赋给next[i]
94+
        }
95+
    }
96+
```
97+
98+
99+
代码构造next数组的逻辑流程如下:
100+
101+
<img src='../../media/video/KMP精讲3.gif' width=600> </img></div>
102+
103+
104+
得到了next数组之后,就要用这个来做匹配了。
105+
106+
在文本串s里 找是否出现过模式串t。
107+
108+
定义两个下表j 指向模式串起始位置,i指向文本串其实位置。
109+
110+
那么j初始值依然为-1,为什么呢? **依然因为next数组里记录的起始位置为-1。**
111+
112+
i就从0开始,遍历文本串,代码如下:
113+
114+
```
115+
for (int i = 0; i < s.size(); i++) 
116+
```
117+
118+
接下来就是 s[i] 与 t[j + 1] (因为j从-1开始的) 经行比较。
119+
120+
如果 s[i] 与 t[j + 1] 不相同,j就要从next数组里寻找下一个匹配的位置。
121+
122+
代码如下:
123+
124+
```
125+
while(j >= 0 && s[i] != t[j + 1]) {
126+
    j = next[j];
127+
}
128+
```
129+
130+
如果 s[i] 与 t[j + 1] 相同,那么i 和 j 同时向后移动, 代码如下:
131+
132+
```
133+
if (s[i] == t[j + 1]) {
134+
    j++; // i的增加在for循环里
135+
}
136+
```
137+
138+
如何判断在文本串s里出现了模式串t呢,如果j指向了模式串t的末尾,那么就说明模式串t完全匹配文本串s里的某个子串了。
139+
140+
本题要在文本串字符串中找出模式串出现的第一个位置 (从0开始),所以返回当前在文本串匹配模式串的位置i 减去 模式串的长度,就是文本串字符串中出现模式串的第一个位置。
141+
142+
代码如下:
143+
144+
```
145+
if (j == (t.size() - 1) ) {
146+
    return (i - t.size() + 1);
147+
}
148+
```
149+
150+
那么使用next数组,用模式串匹配文本串的整体代码如下:
151+
152+
```
153+
        int j = -1; // 因为next数组里记录的起始位置为-1
154+
        for (int i = 0; i < s.size(); i++) { // 注意i就从0开始
155+
            while(j >= 0 && s[i] != t[j + 1]) { // 不匹配
156+
                j = next[j]; // j 寻找之前匹配的位置
157+
            }
158+
            if (s[i] == t[j + 1]) { // 匹配,j和i同时向后移动
159+
                j++; // i的增加在for循环里
160+
            }
161+
            if (j == (t.size() - 1) ) { // 文本串s里出现了模式串t
162+
                return (i - t.size() + 1);
163+
            }
164+
        }
165+
```
166+
167+
此时所有逻辑的代码都已经写出来了,本题整体代码如下:
168+
169+
170+
171+
9172

10173
## C++代码
11174

12175
```
13176
class Solution {
14177
public:
15-
void preKmp(int* next, const string& s){
178+
void getNext(int* next, const string& s){
16179
next[0] = -1;
17180
int j = -1;
18181
for(int i = 1; i < s.size(); i++){
@@ -30,7 +193,7 @@ public:
30193
return 0;
31194
}
32195
int next[needle.size()];
33-
preKmp(next, needle);
196+
getNext(next, needle);
34197
35198
int j = -1;
36199
for (int i = 0; i < haystack.size(); i++) {

problems/0257.二叉树的所有路径.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,7 @@ public:
213213

214214
注意在函数定义的时候`void traversal(TreeNode* cur, string path, vector<string>& result)` ,定义的是`string path`,说明每次都是复制赋值。
215215

216-
那么在如上代码中,貌似没有看到回溯的逻辑,其实不然,回溯就隐藏在`traversal(cur->left, path + "->", result);`中的 `path + "->"`。 每次函数调用完,path依然是没有+ 上"->" 的,这就是回溯了。
216+
那么在如上代码中,**貌似没有看到回溯的逻辑,其实不然,回溯就隐藏在`traversal(cur->left, path + "->", result);`中的 `path + "->"`** 每次函数调用完,path依然是没有+ 上"->" 的,这就是回溯了。
217217

218218
**综合以上,第二种写法更简洁,但是把很多重要的点隐藏在了代码细节里,第一种写法虽然代码多一些,但是每一个处理逻辑都完整的展现了出来。**
219219

@@ -222,3 +222,4 @@ public:
222222

223223

224224

225+
> 更多算法干货文章持续更新,可以微信搜索「代码随想录」第一时间围观,关注后,回复「Java」「C++」 「python」「简历模板」「数据结构与算法」等等,就可以获得我多年整理的学习资料。

problems/0332.重新安排行程.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,3 +123,4 @@ public:
123123
124124
```
125125

126+
> 更多算法干货文章持续更新,可以微信搜索「代码随想录」第一时间围观,关注后,回复「Java」「C++」 「python」「简历模板」「数据结构与算法」等等,就可以获得我多年整理的学习资料。

problems/0344.反转字符串.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,5 +122,5 @@ public:
122122
}
123123
};
124124
```
125-
> 更过算法干货文章持续更新,可以微信搜索「代码随想录」第一时间围观,关注后,回复「Java」「C++」 「python」「简历模板」「数据结构与算法」等等,就可以获得我多年整理的学习资料。
125+
> 更多算法干货文章持续更新,可以微信搜索「代码随想录」第一时间围观,关注后,回复「Java」「C++」 「python」「简历模板」「数据结构与算法」等等,就可以获得我多年整理的学习资料。
126126

problems/剑指Offer58-II.左旋转字符串.md

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,9 @@ https://leetcode-cn.com/problems/zuo-xuan-zhuan-zi-fu-chuan-lcof/
4040

4141
例如 :示例1中 输入:字符串abcdefg,n=2
4242

43-
1. 反转区间为前n的子串 : bacdefg
44-
2. 反转区间为n到末尾的子串:bagfedc
45-
3. 反转整个字符串:cdefgab
43+
如图:
44+
45+
<img src='../pics/剑指Offer58-II.左旋转字符串.png' width=600> </img></div>
4646

4747
最终得到左旋2个单元的字符串:cdefgab
4848

@@ -77,4 +77,11 @@ public:
7777

7878
好了,反转字符串一共就介绍到这里,相信大家此时对反转字符串的常见操作已经很了解了。
7979

80+
# 题外话
81+
82+
一些同学热衷于使用substr,来做这道题。
83+
其实使用substr 和 反转 时间复杂度是一样的 ,都是O(n),但是使用substr申请了额外空间,所以空间复杂度是O(n),而反转方法的空间复杂度是O(1)。
84+
85+
**如果想让这套题目有意义,就不要申请额外空间。**
86+
8087
> 更多算法干货文章持续更新,可以微信搜索「代码随想录」第一时间围观,关注后,回复「Java」「C++」 「python」「简历模板」「数据结构与算法」等等,就可以获得我多年整理的学习资料。

video/KMP详解1.gif

687 KB
Loading

0 commit comments

Comments
 (0)