Skip to content

Commit

Permalink
优化排版,把复杂度标记为公式
Browse files Browse the repository at this point in the history
  • Loading branch information
bqlin committed Dec 10, 2021
1 parent 973582c commit 90638af
Show file tree
Hide file tree
Showing 96 changed files with 462 additions and 449 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,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
8 changes: 4 additions & 4 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 @@ -43,9 +43,9 @@ 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
6 changes: 3 additions & 3 deletions problems/0018.四数之和.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,19 +35,19 @@

[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
6 changes: 3 additions & 3 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 @@ -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
4 changes: 2 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
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
15 changes: 8 additions & 7 deletions problems/0035.搜索插入位置.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,16 +73,16 @@ public:
};
```
* 时间复杂度:O(n)
* 空间复杂度:O(1)
* 时间复杂度:$O(n)$
* 空间复杂度:$O(1)$
效率如下:
![35_搜索插入位置](https://img-blog.csdnimg.cn/20201216232127268.png)
### 二分法
既然暴力解法的时间复杂度是O(n),就要尝试一下使用二分查找法。
既然暴力解法的时间复杂度是$O(n)$,就要尝试一下使用二分查找法。
![35_搜索插入位置4](https://img-blog.csdnimg.cn/202012162326354.png)
Expand Down Expand Up @@ -140,8 +140,9 @@ public:
}
};
```
* 时间复杂度:O(logn)
* 时间复杂度:O(1)

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

效率如下:
![35_搜索插入位置2](https://img-blog.csdnimg.cn/2020121623272877.png)
Expand Down Expand Up @@ -183,8 +184,8 @@ public:
};
```
* 时间复杂度:O(logn)
* 时间复杂度:O(1)
* 时间复杂度:$O(\log n)$
* 时间复杂度:$O(1)$
## 总结
Expand Down
9 changes: 5 additions & 4 deletions problems/0042.接雨水.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,8 +129,8 @@ public:
};
```

因为每次遍历列的时候,还要向两边寻找最高的列,所以时间复杂度为O(n^2)。
空间复杂度为O(1)。
因为每次遍历列的时候,还要向两边寻找最高的列,所以时间复杂度为$O(n^2)$
空间复杂度为$O(1)$



Expand Down Expand Up @@ -778,8 +778,9 @@ int trap(int* height, int heightSize) {
return ans;
}
```
时间复杂度 O(n)
空间复杂度 O(1)
* 时间复杂度 $O(n)$
* 空间复杂度 $O(1)$
-----------------------
Expand Down
14 changes: 8 additions & 6 deletions problems/0053.最大子序和.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,9 @@

暴力解法的思路,第一层for 就是设置起始位置,第二层for循环遍历数组寻找最大值

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

```CPP
class Solution {
public:
Expand Down Expand Up @@ -96,8 +97,9 @@ public:
}
};
```
时间复杂度:O(n)
空间复杂度:O(1)

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

当然题目没有说如果数组为空,应该返回什么,所以数组为空的话返回啥都可以了。

Expand Down Expand Up @@ -126,8 +128,8 @@ public:
};
```
时间复杂度:O(n)
空间复杂度:O(n)
* 时间复杂度:$O(n)$
* 空间复杂度:$O(n)$
## 总结
Expand Down
5 changes: 3 additions & 2 deletions problems/0053.最大子序和(动态规划).md
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,9 @@ public:
}
};
```
* 时间复杂度:O(n)
* 空间复杂度:O(n)
* 时间复杂度:$O(n)$
* 空间复杂度:$O(n)$
## 总结
Expand Down
4 changes: 2 additions & 2 deletions problems/0056.合并区间.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,8 +112,8 @@ public:
};
```

* 时间复杂度:O(nlogn) ,有一个快排
* 空间复杂度:O(1),我没有算result数组(返回值所需容器占的空间)
* 时间复杂度:$O(n\log n)$ ,有一个快排
* 空间复杂度:$O(1)$,我没有算result数组(返回值所需容器占的空间)


## 总结
Expand Down
2 changes: 1 addition & 1 deletion problems/0059.螺旋矩阵II.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

[力扣题目链接](https://leetcode-cn.com/problems/spiral-matrix-ii/)

给定一个正整数 n,生成一个包含 1 到 n^2 所有元素,且元素按顺时针顺序螺旋排列的正方形矩阵。
给定一个正整数 n,生成一个包含 1 到 $n^2$ 所有元素,且元素按顺时针顺序螺旋排列的正方形矩阵。

示例:

Expand Down
16 changes: 9 additions & 7 deletions problems/0062.不同路径.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ public:
那二叉树的节点个数就是 2^(m + n - 1) - 1。可以理解深搜的算法就是遍历了整个满二叉树(其实没有遍历整个满二叉树,只是近似而已)
所以上面深搜代码的时间复杂度为O(2^(m + n - 1) - 1),可以看出,这是指数级别的时间复杂度,是非常大的。
所以上面深搜代码的时间复杂度为$O(2^(m + n - 1) - 1)$,可以看出,这是指数级别的时间复杂度,是非常大的。
### 动态规划
Expand Down Expand Up @@ -142,8 +142,9 @@ public:
}
};
```
* 时间复杂度:O(m * n)
* 空间复杂度:O(m * n)

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

其实用一个一维数组(也可以理解是滚动数组)就可以了,但是不利于理解,可以优化点空间,建议先理解了二维,在理解一维,C++代码如下:

Expand All @@ -162,8 +163,9 @@ public:
}
};
```
* 时间复杂度:O(m * n)
* 空间复杂度:O(n)
* 时间复杂度:$O(m × n)$
* 空间复杂度:$O(n)$
### 数论方法
Expand Down Expand Up @@ -222,8 +224,8 @@ public:
};
```
时间复杂度:O(m)
空间复杂度:O(1)
* 时间复杂度:$O(m)$
* 空间复杂度:$O(1)$
**计算组合问题的代码还是有难度的,特别是处理溢出的情况!**
Expand Down
5 changes: 3 additions & 2 deletions problems/0063.不同路径II.md
Original file line number Diff line number Diff line change
Expand Up @@ -152,8 +152,9 @@ public:
}
};
```
* 时间复杂度O(n * m) n m 分别为obstacleGrid 长度和宽度
* 空间复杂度O(n * m)
* 时间复杂度:$O(n × m)$,n、m 分别为obstacleGrid 长度和宽度
* 空间复杂度:$O(n × m)$
## 总结
Expand Down
10 changes: 6 additions & 4 deletions problems/0070.爬楼梯.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,8 +123,9 @@ public:
}
};
```
* 时间复杂度:O(n)
* 空间复杂度:O(n)
* 时间复杂度:$O(n)$
* 空间复杂度:$O(n)$
当然依然也可以,优化一下空间复杂度,代码如下:
Expand All @@ -146,8 +147,9 @@ public:
}
};
```
* 时间复杂度:O(n)
* 空间复杂度:O(1)

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

后面将讲解的很多动规的题目其实都是当前状态依赖前两个,或者前三个状态,都可以做空间上的优化,**但我个人认为面试中能写出版本一就够了哈,清晰明了,如果面试官要求进一步优化空间的话,我们再去优化**

Expand Down
Loading

0 comments on commit 90638af

Please sign in to comment.