Skip to content

Commit

Permalink
Merge branch 'master' of github.com:youngyangyang04/leetcode-master
Browse files Browse the repository at this point in the history
  • Loading branch information
youngyangyang04 committed Dec 20, 2021
2 parents 1c6ad04 + 2e72b60 commit b059e00
Show file tree
Hide file tree
Showing 129 changed files with 1,256 additions and 595 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@

* 算法性能分析
* [关于时间复杂度,你不知道的都在这里!](./problems/前序/关于时间复杂度,你不知道的都在这里!.md)
* [O(n)的算法居然超时了,此时的n究竟是多大?](./problems/前序/On的算法居然超时了,此时的n究竟是多大?.md)
* [$O(n)$的算法居然超时了,此时的n究竟是多大?](./problems/前序/On的算法居然超时了,此时的n究竟是多大?.md)
* [通过一道面试题目,讲一讲递归算法的时间复杂度!](./problems/前序/通过一道面试题目,讲一讲递归算法的时间复杂度!.md)
* [本周小结!(算法性能分析系列一)](./problems/周总结/20201210复杂度分析周末总结.md)
* [关于空间复杂度,可能有几个疑问?](./problems/前序/关于空间复杂度,可能有几个疑问?.md)
Expand Down
12 changes: 6 additions & 6 deletions problems/0001.两数之和.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@

## 思路

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

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

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

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

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

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

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

Expand Down
10 changes: 5 additions & 5 deletions problems/0005.最长回文子串.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@

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

时间复杂度:O(n^3)
时间复杂度:$O(n^3)$

## 动态规划

Expand Down Expand Up @@ -205,8 +205,8 @@ public:

```
* 时间复杂度:O(n^2)
* 空间复杂度:O(n^2)
* 时间复杂度:$O(n^2)$
* 空间复杂度:$O(n^2)$
## 双指针
Expand Down Expand Up @@ -253,8 +253,8 @@ public:
```

* 时间复杂度:O(n^2)
* 空间复杂度:O(1)
* 时间复杂度:$O(n^2)$
* 空间复杂度:$O(1)$



Expand Down
6 changes: 3 additions & 3 deletions problems/0015.三数之和.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@

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

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

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

Expand Down Expand Up @@ -85,7 +85,7 @@ public:
**其实这道题目使用哈希法并不十分合适**,因为在去重的操作中有很多细节需要注意,在面试中很难直接写出没有bug的代码。
而且使用哈希法 在使用两层for循环的时候,能做的剪枝操作很有限,虽然时间复杂度是O(n^2),也是可以在leetcode上通过,但是程序的执行时间依然比较长 。
而且使用哈希法 在使用两层for循环的时候,能做的剪枝操作很有限,虽然时间复杂度是$O(n^2)$,也是可以在leetcode上通过,但是程序的执行时间依然比较长 。
接下来我来介绍另一个解法:双指针法,**这道题目使用双指针法 要比哈希法高效一些**,那么来讲解一下具体实现的思路。
Expand All @@ -101,7 +101,7 @@ public:
如果 nums[i] + nums[left] + nums[right] < 0 说明 此时 三数之和小了,left 就向右移动,才能让三数之和大一些,直到left与right相遇为止。
时间复杂度:O(n^2)。
时间复杂度:$O(n^2)$
C++代码代码如下:
Expand Down
54 changes: 48 additions & 6 deletions problems/0017.电话号码的字母组合.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@

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

```
```cpp
const string letterMap[10] = {
"", // 0
"", // 1
Expand Down Expand Up @@ -79,7 +79,7 @@ const string letterMap[10] = {

代码如下:

```
```cpp
vector<string> result;
string s;
void backtracking(const string& digits, int index)
Expand All @@ -95,7 +95,7 @@ void backtracking(const string& digits, int index)
代码如下:
```
```cpp
if (index == digits.size()) {
result.push_back(s);
return;
Expand Down Expand Up @@ -281,7 +281,7 @@ class Solution {

## Python
**回溯**
```python3
```python
class Solution:
def __init__(self):
self.answers: List[str] = []
Expand Down Expand Up @@ -317,7 +317,7 @@ class Solution:
self.answer = self.answer[:-1] # 回溯
```
**回溯简化**
```python3
```python
class Solution:
def __init__(self):
self.answers: List[str] = []
Expand Down Expand Up @@ -420,7 +420,8 @@ var letterCombinations = function(digits) {
};
```

C:
## C

```c
char* path;
int pathTop;
Expand Down Expand Up @@ -481,6 +482,47 @@ char ** letterCombinations(char * digits, int* returnSize){
}
```
## Swift
```swift
func letterCombinations(_ digits: String) -> [String] {
// 按键与字母串映射
let letterMap = [
"",
"", "abc", "def",
"ghi", "jkl", "mno",
"pqrs", "tuv", "wxyz"
]
// 把输入的按键字符串转成Int数组
let baseCode = ("0" as Character).asciiValue!
let digits = digits.map { c in
guard let code = c.asciiValue else { return -1 }
return Int(code - baseCode)
}.filter { $0 >= 0 && $0 <= 9 }
guard !digits.isEmpty else { return [] }
var result = [String]()
var s = ""
func backtracking(index: Int) {
// 结束条件:收集结果
if index == digits.count {
result.append(s)
return
}
// 遍历当前按键对应的字母串
let letters = letterMap[digits[index]]
for letter in letters {
s.append(letter) // 处理
backtracking(index: index + 1) // 递归,记得+1
s.removeLast() // 回溯
}
}
backtracking(index: 0)
return result
}
```


-----------------------
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
8 changes: 4 additions & 4 deletions problems/0018.四数之和.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,21 +33,21 @@

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

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

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

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

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

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

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

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

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

* [27.移除元素](https://programmercarl.com/0027.移除元素.html)
* [15.三数之和](https://programmercarl.com/0015.三数之和.html)
Expand Down
5 changes: 3 additions & 2 deletions problems/0024.两两交换链表中的节点.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ public:
}
};
```

* 时间复杂度:$O(n)$
* 空间复杂度:$O(1)$

Expand All @@ -73,7 +74,7 @@ public:

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

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

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

C:
```
```c
/**
* Definition for singly-linked list.
* struct ListNode {
Expand Down
8 changes: 4 additions & 4 deletions problems/0027.移除元素.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

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

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

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

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

JavaScript:
```javascript
//时间复杂度O(n)
//空间复杂度O(1)
//时间复杂度:O(n)
//空间复杂度:O(1)
var removeElement = (nums, val) => {
let k = 0;
for(let i = 0;i < nums.length;i++){
Expand Down
55 changes: 53 additions & 2 deletions problems/0028.实现strStr.md
Original file line number Diff line number Diff line change
Expand Up @@ -229,9 +229,9 @@ next数组就可以是前缀表,但是很多实现都是把前缀表统一减

# 时间复杂度分析

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

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

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

Expand Down Expand Up @@ -894,7 +894,58 @@ var strStr = function (haystack, needle) {
};
```

Swift 版本

> 前缀表统一减一
```swift
func strStr(_ haystack: String, _ needle: String) -> Int {

let s = Array(haystack), p = Array(needle)
guard p.count != 0 else { return 0 }

// 2 pointer
var j = -1
var next = [Int](repeating: -1, count: needle.count)
// KMP
getNext(&next, needle: p)
for i in 0 ..< s.count {
while j >= 0 && s[i] != p[j + 1] {
//不匹配之后寻找之前匹配的位置
j = next[j]
}
if s[i] == p[j + 1] {
//匹配,双指针同时后移
j += 1
}
if j == (p.count - 1) {
//出现匹配字符串
return i - p.count + 1
}
}
return -1
}

//前缀表统一减一
func getNext(_ next: inout [Int], needle: [Character]) {

var j: Int = -1
next[0] = j

// i 从 1 开始
for i in 1 ..< needle.count {
while j >= 0 && needle[i] != needle[j + 1] {
j = next[j]
}
if needle[i] == needle[j + 1] {
j += 1;
}
next[i] = j
}
print(next)
}

```

-----------------------
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

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

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


示例 1:
Expand Down
Loading

0 comments on commit b059e00

Please sign in to comment.