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

Commit b059e00

Browse files
Merge branch 'master' of github.com:youngyangyang04/leetcode-master
2 parents 1c6ad04 + 2e72b60 commit b059e00

File tree

129 files changed

+1256
-595
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

129 files changed

+1256
-595
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@
123123

124124
* 算法性能分析
125125
* [关于时间复杂度,你不知道的都在这里!](./problems/前序/关于时间复杂度,你不知道的都在这里!.md)
126-
* [O(n)的算法居然超时了,此时的n究竟是多大?](./problems/前序/On的算法居然超时了,此时的n究竟是多大?.md)
126+
* [$O(n)$的算法居然超时了,此时的n究竟是多大?](./problems/前序/On的算法居然超时了,此时的n究竟是多大?.md)
127127
* [通过一道面试题目,讲一讲递归算法的时间复杂度!](./problems/前序/通过一道面试题目,讲一讲递归算法的时间复杂度!.md)
128128
* [本周小结!(算法性能分析系列一)](./problems/周总结/20201210复杂度分析周末总结.md)
129129
* [关于空间复杂度,可能有几个疑问?](./problems/前序/关于空间复杂度,可能有几个疑问?.md)

problems/0001.两数之和.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424

2525
## 思路
2626

27-
很明显暴力的解法是两层for循环查找,时间复杂度是O(n^2)。
27+
很明显暴力的解法是两层for循环查找,时间复杂度是$O(n^2)$
2828

2929
建议大家做这道题目之前,先做一下这两道
3030
* [242. 有效的字母异位词](https://www.programmercarl.com/0242.有效的字母异位词.html)
@@ -35,17 +35,17 @@
3535
本题呢,则要使用map,那么来看一下使用数组和set来做哈希法的局限。
3636

3737
* 数组的大小是受限制的,而且如果元素很少,而哈希值太大会造成内存空间的浪费。
38-
* set是一个集合,里面放的元素只能是一个key,而两数之和这道题目,不仅要判断y是否存在而且还要记录y的下表位置,因为要返回x 和 y的下表。所以set 也不能用。
38+
* set是一个集合,里面放的元素只能是一个key,而两数之和这道题目,不仅要判断y是否存在而且还要记录y的下标位置,因为要返回x 和 y的下标。所以set 也不能用。
3939

40-
此时就要选择另一种数据结构:map ,map是一种key value的存储结构,可以用key保存数值,用value在保存数值所在的下表
40+
此时就要选择另一种数据结构:map ,map是一种key value的存储结构,可以用key保存数值,用value在保存数值所在的下标
4141

4242
C++中map,有三种类型:
4343

4444
|映射 |底层实现 | 是否有序 |数值是否可以重复 | 能否更改数值|查询效率 |增删效率|
4545
|---|---| --- |---| --- | --- | ---|
46-
|std::map |红黑树 |key有序 |key不可重复 |key不可修改 | O(logn)|O(logn) |
47-
|std::multimap | 红黑树|key有序 | key可重复 | key不可修改|O(logn) |O(logn) |
48-
|std::unordered_map |哈希表 | key无序 |key不可重复 |key不可修改 |O(1) | O(1)|
46+
|std::map |红黑树 |key有序 |key不可重复 |key不可修改 | $O(\log n)$|$O(\log n)$ |
47+
|std::multimap | 红黑树|key有序 | key可重复 | key不可修改|$O(\log n)$ |$O(\log n)$ |
48+
|std::unordered_map |哈希表 | key无序 |key不可重复 |key不可修改 |$O(1)$ | $O(1)$|
4949

5050
std::unordered_map 底层实现为哈希表,std::map 和std::multimap 的底层实现是红黑树。
5151

problems/0005.最长回文子串.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838

3939
两层for循环,遍历区间起始位置和终止位置,然后判断这个区间是不是回文。
4040

41-
时间复杂度:O(n^3)
41+
时间复杂度:$O(n^3)$
4242

4343
## 动态规划
4444

@@ -205,8 +205,8 @@ public:
205205

206206
```
207207
208-
* 时间复杂度:O(n^2)
209-
* 空间复杂度:O(n^2)
208+
* 时间复杂度:$O(n^2)$
209+
* 空间复杂度:$O(n^2)$
210210
211211
## 双指针
212212
@@ -253,8 +253,8 @@ public:
253253
254254
```
255255

256-
* 时间复杂度:O(n^2)
257-
* 空间复杂度:O(1)
256+
* 时间复杂度:$O(n^2)$
257+
* 空间复杂度:$O(1)$
258258

259259

260260

problems/0015.三数之和.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939

4040
去重的过程不好处理,有很多小细节,如果在面试中很难想到位。
4141

42-
时间复杂度可以做到O(n^2),但还是比较费时的,因为不好做剪枝操作。
42+
时间复杂度可以做到$O(n^2)$,但还是比较费时的,因为不好做剪枝操作。
4343

4444
大家可以尝试使用哈希法写一写,就知道其困难的程度了。
4545

@@ -85,7 +85,7 @@ public:
8585
8686
**其实这道题目使用哈希法并不十分合适**,因为在去重的操作中有很多细节需要注意,在面试中很难直接写出没有bug的代码。
8787
88-
而且使用哈希法 在使用两层for循环的时候,能做的剪枝操作很有限,虽然时间复杂度是O(n^2),也是可以在leetcode上通过,但是程序的执行时间依然比较长 。
88+
而且使用哈希法 在使用两层for循环的时候,能做的剪枝操作很有限,虽然时间复杂度是$O(n^2)$,也是可以在leetcode上通过,但是程序的执行时间依然比较长 。
8989
9090
接下来我来介绍另一个解法:双指针法,**这道题目使用双指针法 要比哈希法高效一些**,那么来讲解一下具体实现的思路。
9191
@@ -101,7 +101,7 @@ public:
101101
102102
如果 nums[i] + nums[left] + nums[right] < 0 说明 此时 三数之和小了,left 就向右移动,才能让三数之和大一些,直到left与right相遇为止。
103103
104-
时间复杂度:O(n^2)。
104+
时间复杂度:$O(n^2)$
105105
106106
C++代码代码如下:
107107

problems/0017.电话号码的字母组合.md

Lines changed: 48 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939

4040
可以使用map或者定义一个二位数组,例如:string letterMap[10],来做映射,我这里定义一个二维数组,代码如下:
4141

42-
```
42+
```cpp
4343
const string letterMap[10] = {
4444
"", // 0
4545
"", // 1
@@ -79,7 +79,7 @@ const string letterMap[10] = {
7979

8080
代码如下:
8181

82-
```
82+
```cpp
8383
vector<string> result;
8484
string s;
8585
void backtracking(const string& digits, int index)
@@ -95,7 +95,7 @@ void backtracking(const string& digits, int index)
9595
9696
代码如下:
9797
98-
```
98+
```cpp
9999
if (index == digits.size()) {
100100
result.push_back(s);
101101
return;
@@ -281,7 +281,7 @@ class Solution {
281281

282282
## Python
283283
**回溯**
284-
```python3
284+
```python
285285
class Solution:
286286
def __init__(self):
287287
self.answers: List[str] = []
@@ -317,7 +317,7 @@ class Solution:
317317
self.answer = self.answer[:-1] # 回溯
318318
```
319319
**回溯简化**
320-
```python3
320+
```python
321321
class Solution:
322322
def __init__(self):
323323
self.answers: List[str] = []
@@ -420,7 +420,8 @@ var letterCombinations = function(digits) {
420420
};
421421
```
422422

423-
C:
423+
## C
424+
424425
```c
425426
char* path;
426427
int pathTop;
@@ -481,6 +482,47 @@ char ** letterCombinations(char * digits, int* returnSize){
481482
}
482483
```
483484
485+
## Swift
486+
487+
```swift
488+
func letterCombinations(_ digits: String) -> [String] {
489+
// 按键与字母串映射
490+
let letterMap = [
491+
"",
492+
"", "abc", "def",
493+
"ghi", "jkl", "mno",
494+
"pqrs", "tuv", "wxyz"
495+
]
496+
// 把输入的按键字符串转成Int数组
497+
let baseCode = ("0" as Character).asciiValue!
498+
let digits = digits.map { c in
499+
guard let code = c.asciiValue else { return -1 }
500+
return Int(code - baseCode)
501+
}.filter { $0 >= 0 && $0 <= 9 }
502+
guard !digits.isEmpty else { return [] }
503+
504+
var result = [String]()
505+
var s = ""
506+
func backtracking(index: Int) {
507+
// 结束条件:收集结果
508+
if index == digits.count {
509+
result.append(s)
510+
return
511+
}
512+
513+
// 遍历当前按键对应的字母串
514+
let letters = letterMap[digits[index]]
515+
for letter in letters {
516+
s.append(letter) // 处理
517+
backtracking(index: index + 1) // 递归,记得+1
518+
s.removeLast() // 回溯
519+
}
520+
}
521+
backtracking(index: 0)
522+
return result
523+
}
524+
```
525+
484526

485527
-----------------------
486528
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>

problems/0018.四数之和.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,21 +33,21 @@
3333

3434
但是有一些细节需要注意,例如: 不要判断`nums[k] > target` 就返回了,三数之和 可以通过 `nums[i] > 0` 就返回了,因为 0 已经是确定的数了,四数之和这道题目 target是任意值。(大家亲自写代码就能感受出来)
3535

36-
[15.三数之和](https://programmercarl.com/0015.三数之和.html)的双指针解法是一层for循环num[i]为确定值,然后循环内有left和right下表作为双指针,找到nums[i] + nums[left] + nums[right] == 0。
36+
[15.三数之和](https://programmercarl.com/0015.三数之和.html)的双指针解法是一层for循环num[i]为确定值,然后循环内有left和right下标作为双指针,找到nums[i] + nums[left] + nums[right] == 0。
3737

38-
四数之和的双指针解法是两层for循环nums[k] + nums[i]为确定值,依然是循环内有left和right下表作为双指针,找出nums[k] + nums[i] + nums[left] + nums[right] == target的情况,三数之和的时间复杂度是O(n^2),四数之和的时间复杂度是O(n^3) 。
38+
四数之和的双指针解法是两层for循环nums[k] + nums[i]为确定值,依然是循环内有left和right下标作为双指针,找出nums[k] + nums[i] + nums[left] + nums[right] == target的情况,三数之和的时间复杂度是$O(n^2)$,四数之和的时间复杂度是$O(n^3)$
3939

4040
那么一样的道理,五数之和、六数之和等等都采用这种解法。
4141

42-
对于[15.三数之和](https://programmercarl.com/0015.三数之和.html)双指针法就是将原本暴力O(n^3)的解法,降为O(n^2)的解法,四数之和的双指针解法就是将原本暴力O(n^4)的解法,降为O(n^3)的解法。
42+
对于[15.三数之和](https://programmercarl.com/0015.三数之和.html)双指针法就是将原本暴力$O(n^3)$的解法,降为$O(n^2)$的解法,四数之和的双指针解法就是将原本暴力$O(n^4)$的解法,降为$O(n^3)$的解法。
4343

4444
之前我们讲过哈希表的经典题目:[454.四数相加II](https://programmercarl.com/0454.四数相加II.html),相对于本题简单很多,因为本题是要求在一个集合中找出四个数相加等于target,同时四元组不能重复。
4545

4646
[454.四数相加II](https://programmercarl.com/0454.四数相加II.html)是四个独立的数组,只要找到A[i] + B[j] + C[k] + D[l] = 0就可以,不用考虑有重复的四个元素相加等于0的情况,所以相对于本题还是简单了不少!
4747

4848
我们来回顾一下,几道题目使用了双指针法。
4949

50-
双指针法将时间复杂度O(n^2)的解法优化为 O(n)的解法。也就是降一个数量级,题目如下:
50+
双指针法将时间复杂度:$O(n^2)$的解法优化为 $O(n)$的解法。也就是降一个数量级,题目如下:
5151

5252
* [27.移除元素](https://programmercarl.com/0027.移除元素.html)
5353
* [15.三数之和](https://programmercarl.com/0015.三数之和.html)

problems/0024.两两交换链表中的节点.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ public:
6262
}
6363
};
6464
```
65+
6566
* 时间复杂度:$O(n)$
6667
* 空间复杂度:$O(1)$
6768

@@ -73,7 +74,7 @@ public:
7374

7475
上面的代码我第一次提交执行用时8ms,打败6.5%的用户,差点吓到我了。
7576

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

7879
![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)
7980

@@ -85,7 +86,7 @@ public:
8586
## 其他语言版本
8687

8788
C:
88-
```
89+
```c
8990
/**
9091
* Definition for singly-linked list.
9192
* struct ListNode {

problems/0027.移除元素.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。
1313

14-
不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并**原地**修改输入数组。
14+
不要使用额外的数组空间,你必须仅使用 $O(1)$ 额外空间并**原地**修改输入数组。
1515

1616
元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
1717

@@ -58,7 +58,7 @@ public:
5858
for (int j = i + 1; j < size; j++) {
5959
nums[j - 1] = nums[j];
6060
}
61-
i--; // 因为下表i以后的数值都向前移动了一位,所以i也向前移动一位
61+
i--; // 因为下标i以后的数值都向前移动了一位,所以i也向前移动一位
6262
size--; // 此时数组的大小-1
6363
}
6464
}
@@ -184,8 +184,8 @@ func removeElement(nums []int, val int) int {
184184

185185
JavaScript:
186186
```javascript
187-
//时间复杂度O(n)
188-
//空间复杂度O(1)
187+
//时间复杂度:O(n)
188+
//空间复杂度:O(1)
189189
var removeElement = (nums, val) => {
190190
let k = 0;
191191
for(let i = 0;i < nums.length;i++){

problems/0028.实现strStr.md

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -229,9 +229,9 @@ next数组就可以是前缀表,但是很多实现都是把前缀表统一减
229229

230230
# 时间复杂度分析
231231

232-
其中n为文本串长度,m为模式串长度,因为在匹配的过程中,根据前缀表不断调整匹配的位置,可以看出匹配的过程是O(n),之前还要单独生成next数组,时间复杂度是O(m)。所以整个KMP算法的时间复杂度是O(n+m)的。
232+
其中n为文本串长度,m为模式串长度,因为在匹配的过程中,根据前缀表不断调整匹配的位置,可以看出匹配的过程是$O(n)$,之前还要单独生成next数组,时间复杂度是$O(m)$。所以整个KMP算法的时间复杂度是$O(n+m)$的。
233233

234-
暴力的解法显而易见是O(n * m),所以**KMP在字符串匹配中极大的提高的搜索的效率。**
234+
暴力的解法显而易见是$O(n × m)$,所以**KMP在字符串匹配中极大的提高的搜索的效率。**
235235

236236
为了和力扣题目28.实现strStr保持一致,方便大家理解,以下文章统称haystack为文本串, needle为模式串。
237237

@@ -894,7 +894,58 @@ var strStr = function (haystack, needle) {
894894
};
895895
```
896896

897+
Swift 版本
897898

899+
> 前缀表统一减一
900+
901+
```swift
902+
func strStr(_ haystack: String, _ needle: String) -> Int {
903+
904+
let s = Array(haystack), p = Array(needle)
905+
guard p.count != 0 else { return 0 }
906+
907+
// 2 pointer
908+
var j = -1
909+
var next = [Int](repeating: -1, count: needle.count)
910+
// KMP
911+
getNext(&next, needle: p)
912+
for i in 0 ..< s.count {
913+
while j >= 0 && s[i] != p[j + 1] {
914+
//不匹配之后寻找之前匹配的位置
915+
j = next[j]
916+
}
917+
if s[i] == p[j + 1] {
918+
//匹配,双指针同时后移
919+
j += 1
920+
}
921+
if j == (p.count - 1) {
922+
//出现匹配字符串
923+
return i - p.count + 1
924+
}
925+
}
926+
return -1
927+
}
928+
929+
//前缀表统一减一
930+
func getNext(_ next: inout [Int], needle: [Character]) {
931+
932+
var j: Int = -1
933+
next[0] = j
934+
935+
// i 从 1 开始
936+
for i in 1 ..< needle.count {
937+
while j >= 0 && needle[i] != needle[j + 1] {
938+
j = next[j]
939+
}
940+
if needle[i] == needle[j + 1] {
941+
j += 1;
942+
}
943+
next[i] = j
944+
}
945+
print(next)
946+
}
947+
948+
```
898949

899950
-----------------------
900951
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>

problems/0034.在排序数组中查找元素的第一个和最后一个位置.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
如果数组中不存在目标值 target,返回 [-1, -1]
1313

14-
进阶:你可以设计并实现时间复杂度为 O(log n) 的算法解决此问题吗?
14+
进阶:你可以设计并实现时间复杂度为 $O(\log n)$ 的算法解决此问题吗?
1515

1616

1717
示例 1:

0 commit comments

Comments
 (0)