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

Commit cfe3b6d

Browse files
Update
1 parent 6741e45 commit cfe3b6d

File tree

4 files changed

+190
-69
lines changed

4 files changed

+190
-69
lines changed

README.md

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
* [哈希表:其实需要哈希的地方都能找到map的身影](https://mp.weixin.qq.com/s/Ue8pKKU5hw_m-jPgwlHcbA)
4040
* [哈希表:这道题目我做过?](https://mp.weixin.qq.com/s/sYZIR4dFBrw_lr3eJJnteQ)
4141
* [哈希表:解决了两数之和,那么能解决三数之和么?](https://mp.weixin.qq.com/s/r5cgZFu0tv4grBAexdcd8A)
42+
* [双指针法:一样的道理,能解决四数之和](https://mp.weixin.qq.com/s/nQrcco8AZJV1pAOVjeIU_g)
4243
* 精选链表相关的面试题
4344
* 精选字符串相关的面试题
4445
* 精选栈与队列相关的面试题
@@ -65,18 +66,19 @@
6566
* [0203.移除链表元素](https://mp.weixin.qq.com/s/slM1CH5Ew9XzK93YOQYSjA)
6667
* [0707.设计链表](https://mp.weixin.qq.com/s/Cf95Lc6brKL4g2j8YyF3Mg)
6768
* [0206.翻转链表](https://mp.weixin.qq.com/s/pnvVP-0ZM7epB8y3w_Njwg)
69+
* [面试题02.07.链表相交](https://github.com/youngyangyang04/leetcode/blob/master/problems/面试题02.07.链表相交.md)
6870
* [0142.环形链表II](https://mp.weixin.qq.com/s/_QVP3IkRZWx9zIpQRgajzA)
6971

7072
* 哈希表经典题目
7173
* [0242.有效的字母异位词](https://mp.weixin.qq.com/s/vM6OszkM6L1Mx2Ralm9Dig)
7274
* [0383.赎金信](https://mp.weixin.qq.com/s/sYZIR4dFBrw_lr3eJJnteQ)
7375
* [0575.分糖果](https://github.com/youngyangyang04/leetcode/blob/master/problems/0575.分糖果.md)
74-
* [0349.两个数组的交集](https://github.com/youngyangyang04/leetcode/blob/master/problems/0349.两个数组的交集.md)
76+
* [0349.两个数组的交集](https://mp.weixin.qq.com/s/N9iqAchXreSVW7zXUS4BVA)
7577
* [0202.快乐数](https://mp.weixin.qq.com/s/G4Q2Zfpfe706gLK7HpZHpA)
7678
* [0001.两数之和](https://mp.weixin.qq.com/s/uVAtjOHSeqymV8FeQbliJQ)
7779
* [0454.四数相加II](https://mp.weixin.qq.com/s/Ue8pKKU5hw_m-jPgwlHcbA)
78-
* [0015.三数之和](https://github.com/youngyangyang04/leetcode/blob/master/problems/0015.三数之和.md)
79-
* [0018.四数之和](https://github.com/youngyangyang04/leetcode/blob/master/problems/0018.四数之和.md)
80+
* [0015.三数之和](https://mp.weixin.qq.com/s/r5cgZFu0tv4grBAexdcd8A)
81+
* [0018.四数之和](https://mp.weixin.qq.com/s/nQrcco8AZJV1pAOVjeIU_g)
8082
* [0219.存在重复元素II](https://github.com/youngyangyang04/leetcode/blob/master/problems/0219.存在重复元素II.md)
8183
* 0220.存在重复元素III
8284

@@ -89,6 +91,15 @@
8991
* [0028.实现strStr()](https://github.com/youngyangyang04/leetcode/blob/master/problems/0028.实现strStr().md)
9092
* [0459.重复的子字符串](https://github.com/youngyangyang04/leetcode/blob/master/problems/0459.重复的子字符串.md)
9193

94+
* 双指针法经典题目
95+
* [0015.三数之和](https://github.com/youngyangyang04/leetcode/blob/master/problems/0015.三数之和.md)
96+
* [0018.四数之和](https://github.com/youngyangyang04/leetcode/blob/master/problems/0018.四数之和.md)
97+
* [0026.删除排序数组中的重复项](https://github.com/youngyangyang04/leetcode/blob/master/problems/0026.删除排序数组中的重复项.md)
98+
* [0206.翻转链表](https://github.com/youngyangyang04/leetcode/blob/master/problems/0206.翻转链表.md)
99+
* [0142.环形链表II](https://github.com/youngyangyang04/leetcode/blob/master/problems/0142.环形链表II.md)
100+
* [0344.反转字符串](https://github.com/youngyangyang04/leetcode/blob/master/problems/0344.反转字符串.md)
101+
* [剑指Offer05.替换空格](https://github.com/youngyangyang04/leetcode/blob/master/problems/剑指Offer05.替换空格.md)
102+
92103
* 栈与队列经典题目
93104
* [0232.用栈实现队列](https://github.com/youngyangyang04/leetcode/blob/master/problems/0232.用栈实现队列.md)
94105
* [0225.用队列实现栈](https://github.com/youngyangyang04/leetcode/blob/master/problems/0225.用队列实现栈.md)

pics/332.重新安排行程.png

42.6 KB
Loading

problems/0027.移除元素.md

Lines changed: 52 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -4,25 +4,50 @@
44

55
https://leetcode-cn.com/problems/remove-element/
66

7+
> 移除元素想要高效的话,不是很简单!
8+
9+
# 编号:27. 移除元素
10+
11+
给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。
12+
13+
不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并**原地**修改输入数组。
14+
15+
元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
16+
17+
示例 1:
18+
给定 nums = [3,2,2,3], val = 3,
19+
函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。
20+
你不需要考虑数组中超出新长度后面的元素。
21+
22+
示例 2:
23+
给定 nums = [0,1,2,2,3,0,4,2], val = 2,
24+
函数应该返回新的长度 5, 并且 nums 中的前五个元素为 0, 1, 3, 0, 4。
25+
26+
**你不需要考虑数组中超出新长度后面的元素。**
27+
728
# 思路
829

930
有的同学可能说了,多余的元素,删掉不就得了。
1031

11-
**要清楚数组的元素在内存地址中是连续的,数组中的元素是不能删掉的,只能覆盖。**
32+
**要知道数组的元素在内存地址中是连续的,不能单独删除数组中的某个元素,只能覆盖。**
33+
34+
数组的基础知识可以看这里[程序员算法面试中,必须掌握的数组理论知识](https://mp.weixin.qq.com/s/X7R55wSENyY62le0Fiawsg)
1235

1336
# 暴力解法
1437

1538
这个题目暴力的解法就是两层for循环,一个for循环遍历数组元素 ,第二个for循环更新数组。
1639

17-
如动画所示
40+
删除过程如下
1841

19-
<video src='../video/27.移除元素-暴力解法.mp4' controls='controls' width='640' height='320' autoplay='autoplay'> Your browser does not support the video tag.</video></div>
42+
<img src='../../World/Writing/leetcode/video/27.移除元素-暴力解法.gif' width=600> </img></div>
2043

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

23-
代码如下:
46+
# 暴力解法C++代码
2447

2548
```
49+
// 时间复杂度:O(n^2)
50+
// 空间复杂度:O(1)
2651
class Solution {
2752
public:
2853
int removeElement(vector<int>& nums, int val) {
@@ -33,7 +58,7 @@ public:
3358
nums[j - 1] = nums[j];
3459
}
3560
i--; // 因为下表i以后的数值都向前移动了一位,所以i也向前移动一位
36-
size--;// 此时数组的大小-1
61+
size--; // 此时数组的大小-1
3762
}
3863
}
3964
return size;
@@ -44,87 +69,48 @@ public:
4469

4570
# 双指针法
4671

47-
双指针法(快慢指针法): **说白了就是通过一个快指针和慢指针在一个for循环下完成两个for循环的工作**
72+
双指针法(快慢指针法): **通过一个快指针和慢指针在一个for循环下完成两个for循环的工作**
4873

49-
先看一下动画理解一下
74+
删除过程如下
5075

76+
<img src='../../World/Writing/leetcode/video/27.移除元素-双指针法.gif' width=600> </img></div>
5177

52-
<video src='../video/27.移除元素.mp4' controls='controls' width='640' height='320' autoplay='autoplay'> Your browser does not support the video tag.</video></div>
78+
**双指针法(快慢指针法)在数组和链表的操作中是非常常见的,很多考察数组和链表操作的面试题,都使用双指针法。**
5379

54-
代码如下:
55-
```
56-
// 时间复杂度:O(n)
57-
// 空间复杂度:O(1)
58-
class Solution {
59-
public:
60-
int removeElement(vector<int>& nums, int val) {
61-
int slowIndex = 0; // index为 慢指针
62-
for (int fastIndex = 0; fastIndex < nums.size(); fastIndex++) { // i 为快指针
63-
if (val != nums[fastIndex]) { //将快指针对应的数值赋值给慢指针对应的数值
64-
nums[slowIndex++] = nums[fastIndex]; 注意这里是slowIndex++ 而不是slowIndex--
65-
}
66-
}
67-
return slowIndex;
68-
}
69-
};
70-
```
80+
我们来回顾一下,之前已经讲过有四道题目使用了双指针法。
7181

72-
其实**双指针法(快慢指针法)在在数组和链表的操作中是非常常见的,很多考察数组和链表操作的面试题,都使用双指针法,可以将时间复杂度O(n^2)的解法优化为 O(n)的解法,例如:**
82+
双指针法将时间复杂度O(n^2)的解法优化为 O(n)的解法。也就是降一个数量级,题目如下:
7383

74-
* [0015.三数之和](https://github.com/youngyangyang04/leetcode/blob/master/problems/0015.三数之和.md)
75-
* [0018.四数之和](https://github.com/youngyangyang04/leetcode/blob/master/problems/0018.四数之和.md)
76-
* [0026.删除排序数组中的重复项](https://github.com/youngyangyang04/leetcode/blob/master/problems/0026.删除排序数组中的重复项.md)
77-
* [0206.翻转链表](https://github.com/youngyangyang04/leetcode/blob/master/problems/0206.翻转链表.md)
78-
* [0344.反转字符串](https://github.com/youngyangyang04/leetcode/blob/master/problems/0344.反转字符串.md)
79-
* [剑指Offer05.替换空格](https://github.com/youngyangyang04/leetcode/blob/master/problems/剑指Offer05.替换空格.md)
84+
* [15.三数之和](https://mp.weixin.qq.com/s/r5cgZFu0tv4grBAexdcd8A)
85+
* [18.四数之和](https://mp.weixin.qq.com/s/nQrcco8AZJV1pAOVjeIU_g)
8086

81-
**还有链表找环,也用到双指针:**
87+
双指针来记录前后指针实现链表反转:
8288

83-
* [0142.环形链表II](https://github.com/youngyangyang04/leetcode/blob/master/problems/0142.环形链表II.md)
89+
* [206.反转链表](https://mp.weixin.qq.com/s/pnvVP-0ZM7epB8y3w_Njwg)
8490

85-
大家都可以去做一做,感受一下双指针法的内在逻辑!
91+
使用双指针来确定有环:
8692

87-
# 本题C++代码
88-
## 暴力解法
93+
* [142题.环形链表II](https://mp.weixin.qq.com/s/_QVP3IkRZWx9zIpQRgajzA)
8994

90-
时间复杂度:O(n^2)
91-
空间复杂度:O(1)
92-
```
93-
class Solution {
94-
public:
95-
int removeElement(vector<int>& nums, int val) {
96-
int size = nums.size();
97-
for (int i = 0; i < size; i++) {
98-
if (nums[i] == val) { // 发现需要移除的元素,就将数组集体向前移动一位
99-
for (int j = i + 1; j < size; j++) {
100-
nums[j - 1] = nums[j];
101-
}
102-
i--; // 因为下表i以后的数值都向前移动了一位,所以i也向前移动一位
103-
size--;// 此时数组的大小-1
104-
}
105-
}
106-
return size;
107-
108-
}
109-
};
110-
```
95+
双指针法在数组和链表中还有很多应用,后面还会介绍到。
11196

112-
## 双指针解法
113-
时间复杂度:O(n)
114-
空间复杂度:O(1)
97+
# 双指针法C++代码:
11598
```
99+
// 时间复杂度:O(n)
100+
// 空间复杂度:O(1)
116101
class Solution {
117102
public:
118103
int removeElement(vector<int>& nums, int val) {
119-
int slowIndex = 0; // index为 慢指针
120-
for (int fastIndex = 0; fastIndex < nums.size(); fastIndex++) { // i 为快指针
121-
if (val != nums[fastIndex]) { //将快指针对应的数值赋值给慢指针对应的数值
122-
nums[slowIndex++] = nums[fastIndex]; 注意这里是slowIndex++ 而不是slowIndex--
104+
int slowIndex = 0;
105+
for (int fastIndex = 0; fastIndex < nums.size(); fastIndex++) {
106+
if (val != nums[fastIndex]) {
107+
nums[slowIndex++] = nums[fastIndex];
123108
}
124109
}
125110
return slowIndex;
126111
}
127112
};
128113
```
129-
> 更过算法干货文章持续更新,可以微信搜索「代码随想录」第一时间围观,关注后,回复「Java」「C++」 「python」「简历模板」「数据结构与算法」等等,就可以获得我多年整理的学习资料。
114+
115+
> 更多算法干货文章持续更新,可以微信搜索「代码随想录」第一时间围观,关注后,回复「Java」「C++」 「python」「简历模板」「数据结构与算法」等等,就可以获得我多年整理的学习资料。
130116

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

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
2+
## 题目地址
3+
4+
5+
6+
# 第242题.有效的字母异位词
7+
8+
# 思路
9+
10+
举一个有重复机场的例子:
11+
12+
<img src='../pics/332.重新安排行程.png' width=600> </img></div>
13+
14+
为什么要举这个例子呢,就是告诉大家,出发机场和到达机场也会重复的,**如果在解题的过程中没有对集合元素处理好,就会死循环。**
15+
16+
这道题目有几个难点:
17+
18+
1. 有多种解法,字母序靠前排在前面,让很多同学望而退步,如何该记录映射关系呢 ?
19+
2. 这是一个图不是一棵树,使用深搜/回溯 终止条件是什么呢?
20+
3. 回溯的过程中,如何遍历一个城市所对应的所有城市。
21+
22+
首先这道题目是使用回溯法(也可以说深搜),那么按照我总结的回溯模板来。
23+
24+
```
25+
backtracking() {
26+
if (终止条件) {
27+
存放结果;
28+
}
29+
30+
for (枚举同一个位置的所有可能性,可以想成节点孩子的数量) {
31+
递归,处理节点;
32+
backtracking();
33+
回溯,撤销处理结果
34+
}
35+
}
36+
```
37+
38+
## 1. 有多种解法,字母序靠前排在前面,让很多同学望而退步,如何该记录映射关系呢 ?
39+
40+
一个城市映射多个城市,城市之间要靠字母序排列,一个城市映射多个城市,可以使用std::unordered_map,如果让多个城市之间再有顺序的话,就是用std::map 或者 或者std::multimap 或者 std::multiset。
41+
42+
如果对map 和 set 的实现机制不太了解,也不清楚为什么 map、multimap就是有序的同学,可以看这篇文章[关于哈希表,你该了解这些!](https://mp.weixin.qq.com/s/g8N6WmoQmsCUw3_BaWxHZA)
43+
44+
这样存放映射关系可以定义为 `unordered_map<string, multiset<string>> targets` 或者 `unordered_map<string, map<string, int>> targets`
45+
46+
含义如下:
47+
48+
`unordered_map<string, multiset<string>> targets``unordered_map<出发城市, 到达城市的集合> targets`
49+
`unordered_map<string, map<string, int>> targets``unordered_map<出发城市, map<到达城市, 航班次数>> targets`
50+
51+
这两个结构,我们选择了后者,因为如果使用`unordered_map<string, multiset<string>> targets` 遍历multiset的时候,不能删除元素,一旦删除元素,迭代器就失效了。而本地在回溯的过程中就是要不断的增删 multiset里的元素,所以 我们使用`unordered_map<string, map<string, int>> targets`
52+
53+
在遍历 `unordered_map<出发城市, map<到达城市, 航班次数>> targets`的过程中,可以使用航班次数这个字段的数字 --或者++,来标记到达城市是否使用过了,而不用对集合做删除元素或者增加元素的操作。
54+
55+
## 2. 这是一个图不是一棵树,使用深搜/回溯 终止条件是什么呢?
56+
57+
你看有多少个航班,那题目中的示例为例,输入: [["MUC", "LHR"], ["JFK", "MUC"], ["SFO", "SJC"], ["LHR", "SFO"]] ,这是有4个航班,那么只要找出一种行程,行程里的机场个数是5就可以了。
58+
59+
所以终止条件 我们回溯遍历的过程中,遇到的机场个数,如果达到了(航班数量+1),那么我们就找到了一个行程,把所有航班串在一起了。
60+
61+
## 3. 回溯的过程中,如何遍历一个城市所对应的所有城市。
62+
63+
这里刚刚说过,在选择映射函数的时候,不能选择`unordered_map<string, multiset<string>> targets`, 因为一旦有元素增删multiset的迭代器就会失效,有一些题解使用了 例如list的迭代器,使用splice来保证 迭代器不失效。
64+
65+
可以说既要找到一个对数据经行排序的容器,而且这个容易增删元素,迭代器还不能失效。
66+
67+
**再说一下为什么一定要增删元素呢,正如开篇我给出的图中所示,出发机场和到达机场是会重复的,搜索的过程没及时删除元素就会死循环。**
68+
69+
所以我选择了`unordered_map<string, map<string, int>> targets` 来基于映射条件。
70+
71+
遍历过程如下:
72+
73+
```
74+
for (pair<const string, int>& target : targets[result[result.size() - 1]]) {
75+
if (target.second > 0 ) {
76+
result.push_back(target.first);
77+
target.second--;
78+
if (backtracking(ticketNum, index + 1, result)) return true;
79+
result.pop_back();
80+
target.second++;
81+
}
82+
}
83+
```
84+
85+
可以看出 通过`unordered_map<string, map<string, int>> targets`里的int字段来判断 这个集合使用使用完了,这样避免了 增删元素。
86+
87+
此时完整代码如下:
88+
89+
# C++代码
90+
91+
```
92+
93+
class Solution {
94+
private:
95+
// unordered_map<出发城市, map<到达城市, 航班次数>> targets
96+
unordered_map<string, map<string, int>> targets;
97+
bool backtracking(int ticketNum, int index, vector<string>& result) {
98+
if (index == ticketNum + 1) {
99+
return true;
100+
}
101+
for (pair<const string, int>& target : targets[result[result.size() - 1]]) {
102+
if (target.second > 0 ) { // 使用int字段来记录到达城市是否使用过了
103+
result.push_back(target.first);
104+
target.second--;
105+
if (backtracking(ticketNum, index + 1, result)) return true;
106+
result.pop_back();
107+
target.second++;
108+
}
109+
}
110+
return false;
111+
}
112+
public:
113+
vector<string> findItinerary(vector<vector<string>>& tickets) {
114+
vector<string> result;
115+
for (const vector<string>& vec : tickets) {
116+
targets[vec[0]][vec[1]]++; // 记录映射关系
117+
}
118+
result.push_back("JFK");
119+
backtracking(tickets.size(), 1, result);
120+
return result;
121+
}
122+
};
123+
```
124+

0 commit comments

Comments
 (0)