mirror of
https://github.com/youngyangyang04/leetcode-master.git
synced 2025-07-08 16:54:50 +08:00
Merge remote-tracking branch 'origin/master'
This commit is contained in:
27
README.md
27
README.md
@ -12,10 +12,10 @@
|
||||
</a>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ"><img src="https://img.shields.io/badge/知识星球-代码随想录-blue" alt=""></a>
|
||||
<a href="https://mp.weixin.qq.com/s/RsdcQ9umo09R6cfnwXZlrQ"><img src="https://img.shields.io/badge/PDF下载-代码随想录-blueviolet" alt=""></a>
|
||||
<a href="https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw"><img src="https://img.shields.io/badge/刷题-微信群-green" alt=""></a>
|
||||
<a href="https://img-blog.csdnimg.cn/20201210231711160.png"><img src="https://img.shields.io/badge/公众号-代码随想录-brightgreen" alt=""></a>
|
||||
<a href="https://space.bilibili.com/525438321"><img src="https://img.shields.io/badge/B站-代码随想录-orange" alt=""></a>
|
||||
<a href="https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ"><img src="https://img.shields.io/badge/知识星球-代码随想录-blue" alt=""></a>
|
||||
</p>
|
||||
|
||||
# LeetCode 刷题攻略
|
||||
@ -112,14 +112,24 @@
|
||||
|
||||
(持续更新中.....)
|
||||
|
||||
## 备战秋招
|
||||
|
||||
1. [选择方向的时候,我也迷茫了](https://mp.weixin.qq.com/s/ZCzFiAHZHLqHPLJQXNm75g)
|
||||
2. [刷题就用库函数了,怎么了?](https://mp.weixin.qq.com/s/6K3_OSaudnHGq2Ey8vqYfg)
|
||||
3. [关于实习,大家可能有点迷茫!](https://mp.weixin.qq.com/s/xcxzi7c78kQGjvZ8hh7taA)
|
||||
4. [马上秋招了,慌得很!](https://mp.weixin.qq.com/s/7q7W8Cb2-a5U5atZdOnOFA)
|
||||
5. [Carl看了上百份简历,总结了这些!](https://mp.weixin.qq.com/s/sJa87MZD28piCOVMFkIbwQ)
|
||||
6. [面试中遇到了发散性问题.....](https://mp.weixin.qq.com/s/SSonDxi2pjkSVwHNzZswng)
|
||||
|
||||
## 数组
|
||||
|
||||
1. [数组过于简单,但你该了解这些!](./problems/数组理论基础.md)
|
||||
2. [数组:每次遇到二分法,都是一看就会,一写就废](./problems/0704.二分查找.md)
|
||||
3. [数组:就移除个元素很难么?](./problems/0027.移除元素.md)
|
||||
4. [数组:滑动窗口拯救了你](./problems/0209.长度最小的子数组.md)
|
||||
5. [数组:这个循环可以转懵很多人!](./problems/0059.螺旋矩阵II.md)
|
||||
6. [数组:总结篇](./problems/数组总结篇.md)
|
||||
4. [数组:有序数组的平方,还有序么?](./problems/0977.有序数组的平方.md)
|
||||
5. [数组:滑动窗口拯救了你](./problems/0209.长度最小的子数组.md)
|
||||
6. [数组:这个循环可以转懵很多人!](./problems/0059.螺旋矩阵II.md)
|
||||
7. [数组:总结篇](./problems/数组总结篇.md)
|
||||
|
||||
## 链表
|
||||
|
||||
@ -292,6 +302,7 @@
|
||||
|
||||
动态规划专题已经开始啦,来不及解释了,小伙伴们上车别掉队!
|
||||
|
||||
<img src='https://code-thinking.cdn.bcebos.com/pics/动态规划-总结大纲1.jpg' width=500> </img></div>
|
||||
1. [关于动态规划,你该了解这些!](./problems/动态规划理论基础.md)
|
||||
2. [动态规划:斐波那契数](./problems/0509.斐波那契数.md)
|
||||
3. [动态规划:爬楼梯](./problems/0070.爬楼梯.md)
|
||||
@ -366,6 +377,7 @@
|
||||
52. [为了绝杀编辑距离,Carl做了三步铺垫,你都知道么?](./problems/为了绝杀编辑距离,卡尔做了三步铺垫.md)
|
||||
53. [动态规划:回文子串](./problems/0647.回文子串.md)
|
||||
54. [动态规划:最长回文子序列](./problems/0516.最长回文子序列.md)
|
||||
55. [动态规划总结篇](./problems/动态规划总结篇.md)
|
||||
|
||||
|
||||
(持续更新中....)
|
||||
@ -390,12 +402,7 @@
|
||||
|
||||
[各类基础算法模板](https://github.com/youngyangyang04/leetcode/blob/master/problems/算法模板.md)
|
||||
|
||||
# 备战秋招
|
||||
|
||||
1. [技术比较弱,也对技术不感兴趣,如何选择方向?](https://mp.weixin.qq.com/s/ZCzFiAHZHLqHPLJQXNm75g)
|
||||
2. [刷题就用库函数了,怎么了?](https://mp.weixin.qq.com/s/6K3_OSaudnHGq2Ey8vqYfg)
|
||||
3. [关于实习,大家可能有点迷茫!](https://mp.weixin.qq.com/s/xcxzi7c78kQGjvZ8hh7taA)
|
||||
4. [马上秋招了,慌得很!](https://mp.weixin.qq.com/s/7q7W8Cb2-a5U5atZdOnOFA)
|
||||
|
||||
# B站算法视频讲解
|
||||
|
||||
|
@ -29,17 +29,17 @@ https://leetcode-cn.com/problems/two-sum/
|
||||
很明显暴力的解法是两层for循环查找,时间复杂度是O(n^2)。
|
||||
|
||||
建议大家做这道题目之前,先做一下这两道
|
||||
* [242. 有效的字母异位词](https://mp.weixin.qq.com/s/vM6OszkM6L1Mx2Ralm9Dig)
|
||||
* [349. 两个数组的交集](https://mp.weixin.qq.com/s/N9iqAchXreSVW7zXUS4BVA)
|
||||
* [242. 有效的字母异位词](https://mp.weixin.qq.com/s/ffS8jaVFNUWyfn_8T31IdA)
|
||||
* [349. 两个数组的交集](https://mp.weixin.qq.com/s/aMSA5zrp3jJcLjuSB0Es2Q)
|
||||
|
||||
[242. 有效的字母异位词](https://mp.weixin.qq.com/s/vM6OszkM6L1Mx2Ralm9Dig) 这道题目是用数组作为哈希表来解决哈希问题,[349. 两个数组的交集](https://mp.weixin.qq.com/s/N9iqAchXreSVW7zXUS4BVA)这道题目是通过set作为哈希表来解决哈希问题。
|
||||
[242. 有效的字母异位词](https://mp.weixin.qq.com/s/ffS8jaVFNUWyfn_8T31IdA) 这道题目是用数组作为哈希表来解决哈希问题,[349. 两个数组的交集](https://mp.weixin.qq.com/s/aMSA5zrp3jJcLjuSB0Es2Q)这道题目是通过set作为哈希表来解决哈希问题。
|
||||
|
||||
本题呢,则要使用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,有三种类型:
|
||||
|
||||
@ -51,13 +51,12 @@ C++中map,有三种类型:
|
||||
|
||||
std::unordered_map 底层实现为哈希表,std::map 和std::multimap 的底层实现是红黑树。
|
||||
|
||||
同理,std::map 和std::multimap 的key也是有序的(这个问题也经常作为面试题,考察对语言容器底层的理解)。 更多哈希表的理论知识请看[关于哈希表,你该了解这些!](https://mp.weixin.qq.com/s/g8N6WmoQmsCUw3_BaWxHZA)。
|
||||
同理,std::map 和std::multimap 的key也是有序的(这个问题也经常作为面试题,考察对语言容器底层的理解)。 更多哈希表的理论知识请看[关于哈希表,你该了解这些!](https://mp.weixin.qq.com/s/RSUANESA_tkhKhYe3ZR8Jg)。
|
||||
|
||||
**这道题目中并不需要key有序,选择std::unordered_map 效率更高!**
|
||||
|
||||
解题思路动画如下:
|
||||
|
||||
|
||||

|
||||
|
||||
|
||||
@ -136,6 +135,21 @@ func twoSum(nums []int, target int) []int {
|
||||
}
|
||||
```
|
||||
|
||||
```go
|
||||
// 使用map方式解题,降低时间复杂度
|
||||
func twoSum(nums []int, target int) []int {
|
||||
m := make(map[int]int)
|
||||
for index, val := range nums {
|
||||
if preIndex, ok := m[target-val]; ok {
|
||||
return []int{preIndex, index}
|
||||
} else {
|
||||
m[val] = index
|
||||
}
|
||||
}
|
||||
return []int{}
|
||||
}
|
||||
```
|
||||
|
||||
Rust
|
||||
|
||||
```rust
|
||||
|
@ -105,8 +105,7 @@ public:
|
||||
|
||||
时间复杂度:O(n^2)。
|
||||
|
||||
|
||||
## 双指针法C++代码
|
||||
C++代码代码如下:
|
||||
|
||||
```C++
|
||||
class Solution {
|
||||
@ -163,13 +162,14 @@ public:
|
||||
|
||||
# 思考题
|
||||
|
||||
既然三数之和可以使用双指针法,我们之前讲过的[两数之和](https://mp.weixin.qq.com/s/uVAtjOHSeqymV8FeQbliJQ),可不可以使用双指针法呢?
|
||||
|
||||
既然三数之和可以使用双指针法,我们之前讲过的[1.两数之和](https://mp.weixin.qq.com/s/vaMsLnH-f7_9nEK4Cuu3KQ),可不可以使用双指针法呢?
|
||||
|
||||
如果不能,题意如何更改就可以使用双指针法呢? **大家留言说出自己的想法吧!**
|
||||
|
||||
两数之和 就不能使用双指针法,因为[两数之和](https://mp.weixin.qq.com/s/uVAtjOHSeqymV8FeQbliJQ)要求返回的是索引下表, 而双指针法一定要排序,一旦排序之后原数组的索引就被改变了。
|
||||
两数之和 就不能使用双指针法,因为[1.两数之和](https://mp.weixin.qq.com/s/vaMsLnH-f7_9nEK4Cuu3KQ)要求返回的是索引下表, 而双指针法一定要排序,一旦排序之后原数组的索引就被改变了。
|
||||
|
||||
如果[两数之和](https://mp.weixin.qq.com/s/uVAtjOHSeqymV8FeQbliJQ)要求返回的是数值的话,就可以使用双指针法了。
|
||||
如果[1.两数之和](https://mp.weixin.qq.com/s/vaMsLnH-f7_9nEK4Cuu3KQ)要求返回的是数值的话,就可以使用双指针法了。
|
||||
|
||||
|
||||
|
||||
@ -336,6 +336,23 @@ var threeSum = function(nums) {
|
||||
```
|
||||
|
||||
|
||||
ruby:
|
||||
```ruby
|
||||
def is_valid(strs)
|
||||
symbol_map = {')' => '(', '}' => '{', ']' => '['}
|
||||
stack = []
|
||||
strs.size.times {|i|
|
||||
c = strs[i]
|
||||
if symbol_map.has_key?(c)
|
||||
top_e = stack.shift
|
||||
return false if symbol_map[c] != top_e
|
||||
else
|
||||
stack.unshift(c)
|
||||
end
|
||||
}
|
||||
stack.empty?
|
||||
end
|
||||
```
|
||||
|
||||
-----------------------
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
|
@ -137,7 +137,7 @@ for (int i = 0; i < letters.size(); i++) {
|
||||
关键地方都讲完了,按照[关于回溯算法,你该了解这些!](https://mp.weixin.qq.com/s/gjSgJbNbd1eAA5WkA-HeWw)中的回溯法模板,不难写出如下C++代码:
|
||||
|
||||
|
||||
```
|
||||
```c++
|
||||
// 版本一
|
||||
class Solution {
|
||||
private:
|
||||
@ -183,7 +183,7 @@ public:
|
||||
|
||||
一些写法,是把回溯的过程放在递归函数里了,例如如下代码,我可以写成这样:(注意注释中不一样的地方)
|
||||
|
||||
```
|
||||
```c++
|
||||
// 版本二
|
||||
class Solution {
|
||||
private:
|
||||
@ -319,7 +319,7 @@ class Solution:
|
||||
|
||||
python3:
|
||||
|
||||
```python3
|
||||
```py
|
||||
class Solution:
|
||||
def letterCombinations(self, digits: str) -> List[str]:
|
||||
self.s = ""
|
||||
@ -342,6 +342,33 @@ class Solution:
|
||||
|
||||
Go:
|
||||
|
||||
javaScript:
|
||||
|
||||
```js
|
||||
var letterCombinations = function(digits) {
|
||||
const k = digits.length;
|
||||
const map = ["","","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"];
|
||||
if(!k) return [];
|
||||
if(k === 1) return map[digits].split("");
|
||||
|
||||
const res = [], path = [];
|
||||
backtracking(digits, k, 0);
|
||||
return res;
|
||||
|
||||
function backtracking(n, k, a) {
|
||||
if(path.length === k) {
|
||||
res.push(path.join(""));
|
||||
return;
|
||||
}
|
||||
for(const v of map[n[a]]) {
|
||||
path.push(v);
|
||||
backtracking(n, k, a + 1);
|
||||
path.pop();
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
@ -31,38 +31,39 @@ https://leetcode-cn.com/problems/4sum/
|
||||
|
||||
# 思路
|
||||
|
||||
四数之和,和[三数之和](https://mp.weixin.qq.com/s/r5cgZFu0tv4grBAexdcd8A)是一个思路,都是使用双指针法, 基本解法就是在[三数之和](https://mp.weixin.qq.com/s/r5cgZFu0tv4grBAexdcd8A) 的基础上再套一层for循环。
|
||||
四数之和,和[15.三数之和](https://mp.weixin.qq.com/s/QfTNEByq1YlNSXRKEumwHg)是一个思路,都是使用双指针法, 基本解法就是在[15.三数之和](https://mp.weixin.qq.com/s/QfTNEByq1YlNSXRKEumwHg) 的基础上再套一层for循环。
|
||||
|
||||
但是有一些细节需要注意,例如: 不要判断`nums[k] > target` 就返回了,三数之和 可以通过 `nums[i] > 0` 就返回了,因为 0 已经是确定的数了,四数之和这道题目 target是任意值。(大家亲自写代码就能感受出来)
|
||||
|
||||
[三数之和](https://mp.weixin.qq.com/s/r5cgZFu0tv4grBAexdcd8A)的双指针解法是一层for循环num[i]为确定值,然后循环内有left和right下表作为双指针,找到nums[i] + nums[left] + nums[right] == 0。
|
||||
[15.三数之和](https://mp.weixin.qq.com/s/QfTNEByq1YlNSXRKEumwHg)的双指针解法是一层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) 。
|
||||
|
||||
那么一样的道理,五数之和、六数之和等等都采用这种解法。
|
||||
|
||||
对于[三数之和](https://mp.weixin.qq.com/s/r5cgZFu0tv4grBAexdcd8A)双指针法就是将原本暴力O(n^3)的解法,降为O(n^2)的解法,四数之和的双指针解法就是将原本暴力O(n^4)的解法,降为O(n^3)的解法。
|
||||
对于[15.三数之和](https://mp.weixin.qq.com/s/QfTNEByq1YlNSXRKEumwHg)双指针法就是将原本暴力O(n^3)的解法,降为O(n^2)的解法,四数之和的双指针解法就是将原本暴力O(n^4)的解法,降为O(n^3)的解法。
|
||||
|
||||
之前我们讲过哈希表的经典题目:[四数相加II](https://mp.weixin.qq.com/s/Ue8pKKU5hw_m-jPgwlHcbA),相对于本题简单很多,因为本题是要求在一个集合中找出四个数相加等于target,同时四元组不能重复。
|
||||
之前我们讲过哈希表的经典题目:[454.四数相加II](https://mp.weixin.qq.com/s/12g_w6RzHuEpFts1pT6BWw),相对于本题简单很多,因为本题是要求在一个集合中找出四个数相加等于target,同时四元组不能重复。
|
||||
|
||||
而[四数相加II](https://mp.weixin.qq.com/s/Ue8pKKU5hw_m-jPgwlHcbA)是四个独立的数组,只要找到A[i] + B[j] + C[k] + D[l] = 0就可以,不用考虑有重复的四个元素相加等于0的情况,所以相对于本题还是简单了不少!
|
||||
而[454.四数相加II](https://mp.weixin.qq.com/s/12g_w6RzHuEpFts1pT6BWw)是四个独立的数组,只要找到A[i] + B[j] + C[k] + D[l] = 0就可以,不用考虑有重复的四个元素相加等于0的情况,所以相对于本题还是简单了不少!
|
||||
|
||||
我们来回顾一下,几道题目使用了双指针法。
|
||||
|
||||
双指针法将时间复杂度O(n^2)的解法优化为 O(n)的解法。也就是降一个数量级,题目如下:
|
||||
* [0027.移除元素](https://mp.weixin.qq.com/s/wj0T-Xs88_FHJFwayElQlA)
|
||||
* [15.三数之和](https://mp.weixin.qq.com/s/r5cgZFu0tv4grBAexdcd8A)
|
||||
|
||||
* [27.移除元素](https://mp.weixin.qq.com/s/RMkulE4NIb6XsSX83ra-Ww)
|
||||
* [15.三数之和](https://mp.weixin.qq.com/s/QfTNEByq1YlNSXRKEumwHg)
|
||||
* [18.四数之和](https://mp.weixin.qq.com/s/nQrcco8AZJV1pAOVjeIU_g)
|
||||
|
||||
双指针来记录前后指针实现链表反转:
|
||||
|
||||
* [206.反转链表](https://mp.weixin.qq.com/s/pnvVP-0ZM7epB8y3w_Njwg)
|
||||
操作链表:
|
||||
|
||||
使用双指针来确定有环:
|
||||
* [206.反转链表](https://mp.weixin.qq.com/s/ckEvIVGcNLfrz6OLOMoT0A)
|
||||
* [19.删除链表的倒数第N个节点](https://mp.weixin.qq.com/s/gxu65X1343xW_sBrkTz0Eg)
|
||||
* [面试题 02.07. 链表相交](https://mp.weixin.qq.com/s/BhfFfaGvt9Zs7UmH4YehZw)
|
||||
* [142题.环形链表II](https://mp.weixin.qq.com/s/gt_VH3hQTqNxyWcl1ECSbQ)
|
||||
|
||||
* [142题.环形链表II](https://mp.weixin.qq.com/s/_QVP3IkRZWx9zIpQRgajzA)
|
||||
|
||||
双指针法在数组和链表中还有很多应用,后面还会介绍到。
|
||||
双指针法在字符串题目中还有很多应用,后面还会介绍到。
|
||||
|
||||
C++代码
|
||||
|
||||
|
@ -112,6 +112,30 @@ class Solution {
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Python:
|
||||
```python
|
||||
# Definition for singly-linked list.
|
||||
# class ListNode:
|
||||
# def __init__(self, val=0, next=None):
|
||||
# self.val = val
|
||||
# self.next = next
|
||||
class Solution:
|
||||
def removeNthFromEnd(self, head: ListNode, n: int) -> ListNode:
|
||||
head_dummy = ListNode()
|
||||
head_dummy.next = head
|
||||
|
||||
slow, fast = head_dummy, head_dummy
|
||||
while(n!=0): #fast先往前走n步
|
||||
fast = fast.next
|
||||
n -= 1
|
||||
while(fast.next!=None):
|
||||
slow = slow.next
|
||||
fast = fast.next
|
||||
#fast 走到结尾后,slow的下一个节点为倒数第N个节点
|
||||
slow.next = slow.next.next #删除
|
||||
return head_dummy.next
|
||||
```
|
||||
Go:
|
||||
```Go
|
||||
/**
|
||||
|
@ -129,6 +129,23 @@ class Solution {
|
||||
```
|
||||
|
||||
Python:
|
||||
```python
|
||||
class Solution:
|
||||
def swapPairs(self, head: ListNode) -> ListNode:
|
||||
dummy = ListNode(0) #设置一个虚拟头结点
|
||||
dummy.next = head
|
||||
cur = dummy
|
||||
while cur.next and cur.next.next:
|
||||
tmp = cur.next #记录临时节点
|
||||
tmp1 = cur.next.next.next #记录临时节点
|
||||
|
||||
cur.next = cur.next.next #步骤一
|
||||
cur.next.next = tmp #步骤二
|
||||
cur.next.next.next = tmp1 #步骤三
|
||||
|
||||
cur = cur.next.next #cur移动两位,准备下一轮交换
|
||||
return dummy.next
|
||||
```
|
||||
|
||||
Go:
|
||||
```go
|
||||
|
@ -186,6 +186,36 @@ var removeElement = (nums, val) => {
|
||||
};
|
||||
```
|
||||
|
||||
Ruby:
|
||||
```ruby
|
||||
def remove_element(nums, val)
|
||||
i = 0
|
||||
nums.each_index do |j|
|
||||
if nums[j] != val
|
||||
nums[i] = nums[j]
|
||||
i+=1
|
||||
end
|
||||
end
|
||||
i
|
||||
end
|
||||
```
|
||||
Rust:
|
||||
```rust
|
||||
pub fn remove_element(nums: &mut Vec<i32>, val: i32) -> &mut Vec<i32> {
|
||||
let mut start: usize = 0;
|
||||
while start < nums.len() {
|
||||
if nums[start] == val {
|
||||
nums.remove(start);
|
||||
}
|
||||
start += 1;
|
||||
}
|
||||
nums
|
||||
}
|
||||
fn main() {
|
||||
let mut nums = vec![5,1,3,5,2,3,4,1];
|
||||
println!("{:?}",remove_element(&mut nums, 5));
|
||||
}
|
||||
```
|
||||
-----------------------
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
|
@ -61,11 +61,6 @@ KMP的经典思想就是:**当出现字符串不匹配时,可以记录一部
|
||||
|
||||
读完本篇可以顺便,把leetcode上28.实现strStr()题目做了。
|
||||
|
||||
如果文字实在看不下去,就看我在B站上的视频吧,如下:
|
||||
|
||||
* [帮你把KMP算法学个通透!(理论篇)B站](https://www.bilibili.com/video/BV1PD4y1o7nd/)
|
||||
* [帮你把KMP算法学个通透!(求next数组代码篇)B站](https://www.bilibili.com/video/BV1M5411j7Xx/)
|
||||
|
||||
|
||||
# 什么是KMP
|
||||
|
||||
@ -726,10 +721,98 @@ class Solution:
|
||||
|
||||
Go:
|
||||
|
||||
```go
|
||||
// 方法一:前缀表使用减1实现
|
||||
|
||||
// getNext 构造前缀表next
|
||||
// params:
|
||||
// next 前缀表数组
|
||||
// s 模式串
|
||||
func getNext(next []int, s string) {
|
||||
j := -1 // j表示 最长相等前后缀长度
|
||||
next[0] = j
|
||||
|
||||
for i := 1; i < len(s); i++ {
|
||||
for j >= 0 && s[i] != s[j+1] {
|
||||
j = next[j] // 回退前一位
|
||||
}
|
||||
if s[i] == s[j+1] {
|
||||
j++
|
||||
}
|
||||
next[i] = j // next[i]是i(包括i)之前的最长相等前后缀长度
|
||||
}
|
||||
}
|
||||
func strStr(haystack string, needle string) int {
|
||||
if len(needle) == 0 {
|
||||
return 0
|
||||
}
|
||||
next := make([]int, len(needle))
|
||||
getNext(next, needle)
|
||||
j := -1 // 模式串的起始位置 next为-1 因此也为-1
|
||||
for i := 0; i < len(haystack); i++ {
|
||||
for j >= 0 && haystack[i] != needle[j+1] {
|
||||
j = next[j] // 寻找下一个匹配点
|
||||
}
|
||||
if haystack[i] == needle[j+1] {
|
||||
j++
|
||||
}
|
||||
if j == len(needle)-1 { // j指向了模式串的末尾
|
||||
return i - len(needle) + 1
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
```
|
||||
|
||||
```go
|
||||
// 方法二: 前缀表无减一或者右移
|
||||
|
||||
// getNext 构造前缀表next
|
||||
// params:
|
||||
// next 前缀表数组
|
||||
// s 模式串
|
||||
func getNext(next []int, s string) {
|
||||
j := 0
|
||||
next[0] = j
|
||||
for i := 1; i < len(s); i++ {
|
||||
for j > 0 && s[i] != s[j] {
|
||||
j = next[j-1]
|
||||
}
|
||||
if s[i] == s[j] {
|
||||
j++
|
||||
}
|
||||
next[i] = j
|
||||
}
|
||||
}
|
||||
func strStr(haystack string, needle string) int {
|
||||
n := len(needle)
|
||||
if n == 0 {
|
||||
return 0
|
||||
}
|
||||
j := 0
|
||||
next := make([]int, n)
|
||||
getNext(next, needle)
|
||||
for i := 0; i < len(haystack); i++ {
|
||||
for j > 0 && haystack[i] != needle[j] {
|
||||
j = next[j-1] // 回退到j的前一位
|
||||
}
|
||||
if haystack[i] == needle[j] {
|
||||
j++
|
||||
}
|
||||
if j == n {
|
||||
return i - n + 1
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
-----------------------
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
@ -287,7 +287,39 @@ class Solution {
|
||||
```
|
||||
|
||||
Python:
|
||||
|
||||
```python3
|
||||
class Solution:
|
||||
def solveSudoku(self, board: List[List[str]]) -> None:
|
||||
"""
|
||||
Do not return anything, modify board in-place instead.
|
||||
"""
|
||||
def backtrack(board):
|
||||
for i in range(len(board)): #遍历行
|
||||
for j in range(len(board[0])): #遍历列
|
||||
if board[i][j] != ".": continue
|
||||
for k in range(1,10): #(i, j) 这个位置放k是否合适
|
||||
if isValid(i,j,k,board):
|
||||
board[i][j] = str(k) #放置k
|
||||
if backtrack(board): return True #如果找到合适一组立刻返回
|
||||
board[i][j] = "." #回溯,撤销k
|
||||
return False #9个数都试完了,都不行,那么就返回false
|
||||
return True #遍历完没有返回false,说明找到了合适棋盘位置了
|
||||
def isValid(row,col,val,board):
|
||||
for i in range(9): #判断行里是否重复
|
||||
if board[row][i] == str(val):
|
||||
return False
|
||||
for j in range(9): #判断列里是否重复
|
||||
if board[j][col] == str(val):
|
||||
return False
|
||||
startRow = (row // 3) * 3
|
||||
startcol = (col // 3) * 3
|
||||
for i in range(startRow,startRow + 3): #判断9方格里是否重复
|
||||
for j in range(startcol,startcol + 3):
|
||||
if board[i][j] == str(val):
|
||||
return False
|
||||
return True
|
||||
backtrack(board)
|
||||
```
|
||||
|
||||
Go:
|
||||
|
||||
|
@ -237,34 +237,28 @@ public:
|
||||
|
||||
Java:
|
||||
```Java
|
||||
// 剪枝优化
|
||||
class Solution {
|
||||
List<List<Integer>> lists = new ArrayList<>();
|
||||
Deque<Integer> deque = new LinkedList<>();
|
||||
|
||||
public List<List<Integer>> combinationSum3(int k, int n) {
|
||||
int[] arr = new int[]{1, 2, 3, 4, 5, 6, 7, 8, 9};
|
||||
backTracking(arr, n, k, 0);
|
||||
return lists;
|
||||
public List<List<Integer>> combinationSum(int[] candidates, int target) {
|
||||
List<List<Integer>> res = new ArrayList<>();
|
||||
Arrays.sort(candidates); // 先进行排序
|
||||
backtracking(res, new ArrayList<>(), candidates, target, 0, 0);
|
||||
return res;
|
||||
}
|
||||
|
||||
public void backTracking(int[] arr, int n, int k, int startIndex) {
|
||||
//如果 n 小于0,没必要继续本次递归,已经不符合要求了
|
||||
if (n < 0) {
|
||||
public void backtracking(List<List<Integer>> res, List<Integer> path, int[] candidates, int target, int sum, int idx) {
|
||||
// 找到了数字和为 target 的组合
|
||||
if (sum == target) {
|
||||
res.add(new ArrayList<>(path));
|
||||
return;
|
||||
}
|
||||
if (deque.size() == k) {
|
||||
if (n == 0) {
|
||||
lists.add(new ArrayList(deque));
|
||||
}
|
||||
return;
|
||||
}
|
||||
for (int i = startIndex; i < arr.length - (k - deque.size()) + 1; i++) {
|
||||
deque.push(arr[i]);
|
||||
//减去当前元素
|
||||
n -= arr[i];
|
||||
backTracking(arr, n, k, i + 1);
|
||||
//恢复n
|
||||
n += deque.pop();
|
||||
|
||||
for (int i = idx; i < candidates.length; i++) {
|
||||
// 如果 sum + candidates[i] > target 就终止遍历
|
||||
if (sum + candidates[i] > target) break;
|
||||
path.add(candidates[i]);
|
||||
backtracking(res, path, candidates, target, sum + candidates[i], i);
|
||||
path.remove(path.size() - 1); // 回溯,移除路径 path 最后一个元素
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -292,30 +286,30 @@ class Solution:
|
||||
```
|
||||
Go:
|
||||
|
||||
JavaScript
|
||||
JavaScript:
|
||||
|
||||
```js
|
||||
var strStr = function (haystack, needle) {
|
||||
if (needle === '') {
|
||||
return 0;
|
||||
}
|
||||
|
||||
let hayslen = haystack.length;
|
||||
let needlen = needle.length;
|
||||
|
||||
if (haystack === '' || hayslen < needlen) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (let i = 0; i <= hayslen - needlen; i++) {
|
||||
if (haystack[i] === needle[0]) {
|
||||
if (haystack.substr(i, needlen) === needle) {
|
||||
return i;
|
||||
}
|
||||
var combinationSum = function(candidates, target) {
|
||||
const res = [], path = [];
|
||||
candidates.sort(); // 排序
|
||||
backtracking(0, 0);
|
||||
return res;
|
||||
function backtracking(j, sum) {
|
||||
if (sum > target) return;
|
||||
if (sum === target) {
|
||||
res.push(Array.from(path));
|
||||
return;
|
||||
}
|
||||
for(let i = j; i < candidates.length; i++ ) {
|
||||
const n = candidates[i];
|
||||
if(n > target - sum) continue;
|
||||
path.push(n);
|
||||
sum += n;
|
||||
backtracking(i, sum);
|
||||
path.pop();
|
||||
sum -= n;
|
||||
}
|
||||
}
|
||||
if (i === hayslen - needlen) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
|
@ -293,7 +293,7 @@ class Solution {
|
||||
}
|
||||
```
|
||||
Python:
|
||||
```python3
|
||||
```py
|
||||
class Solution:
|
||||
def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]:
|
||||
res = []
|
||||
@ -314,7 +314,39 @@ class Solution:
|
||||
```
|
||||
Go:
|
||||
|
||||
javaScript:
|
||||
|
||||
```js
|
||||
/**
|
||||
* @param {number[]} candidates
|
||||
* @param {number} target
|
||||
* @return {number[][]}
|
||||
*/
|
||||
var combinationSum2 = function(candidates, target) {
|
||||
const res = []; path = [], len = candidates.length;
|
||||
candidates.sort();
|
||||
backtracking(0, 0);
|
||||
return res;
|
||||
function backtracking(sum, i) {
|
||||
if (sum > target) return;
|
||||
if (sum === target) {
|
||||
res.push(Array.from(path));
|
||||
return;
|
||||
}
|
||||
let f = -1;
|
||||
for(let j = i; j < len; j++) {
|
||||
const n = candidates[j];
|
||||
if(n > target - sum || n === f) continue;
|
||||
path.push(n);
|
||||
sum += n;
|
||||
f = n;
|
||||
backtracking(sum, j + 1);
|
||||
path.pop();
|
||||
sum -= n;
|
||||
}
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
-----------------------
|
||||
|
@ -208,6 +208,26 @@ func jump(nums []int) int {
|
||||
}
|
||||
return dp[len(nums)-1]
|
||||
}
|
||||
```
|
||||
|
||||
Javascript:
|
||||
```Javascript
|
||||
var jump = function(nums) {
|
||||
let curIndex = 0
|
||||
let nextIndex = 0
|
||||
let steps = 0
|
||||
for(let i = 0; i < nums.length - 1; i++) {
|
||||
nextIndex = Math.max(nums[i] + i, nextIndex)
|
||||
if(i === curIndex) {
|
||||
curIndex = nextIndex
|
||||
steps++
|
||||
}
|
||||
}
|
||||
|
||||
return steps
|
||||
};
|
||||
```
|
||||
|
||||
/*
|
||||
dp[i]表示从起点到当前位置的最小跳跃次数
|
||||
dp[i]=min(dp[j]+1,dp[i]) 表示从j位置用一步跳跃到当前位置,这个j位置可能有很多个,却最小一个就可以
|
||||
|
@ -182,7 +182,45 @@ class Solution {
|
||||
```
|
||||
|
||||
Python:
|
||||
```python3
|
||||
class Solution:
|
||||
def permute(self, nums: List[int]) -> List[List[int]]:
|
||||
res = [] #存放符合条件结果的集合
|
||||
path = [] #用来存放符合条件的结果
|
||||
used = [] #用来存放已经用过的数字
|
||||
def backtrack(nums,used):
|
||||
if len(path) == len(nums):
|
||||
return res.append(path[:]) #此时说明找到了一组
|
||||
for i in range(0,len(nums)):
|
||||
if nums[i] in used:
|
||||
continue #used里已经收录的元素,直接跳过
|
||||
path.append(nums[i])
|
||||
used.append(nums[i])
|
||||
backtrack(nums,used)
|
||||
used.pop()
|
||||
path.pop()
|
||||
backtrack(nums,used)
|
||||
return res
|
||||
```
|
||||
|
||||
Python(优化,不用used数组):
|
||||
```python3
|
||||
class Solution:
|
||||
def permute(self, nums: List[int]) -> List[List[int]]:
|
||||
res = [] #存放符合条件结果的集合
|
||||
path = [] #用来存放符合条件的结果
|
||||
def backtrack(nums):
|
||||
if len(path) == len(nums):
|
||||
return res.append(path[:]) #此时说明找到了一组
|
||||
for i in range(0,len(nums)):
|
||||
if nums[i] in path: #path里已经收录的元素,直接跳过
|
||||
continue
|
||||
path.append(nums[i])
|
||||
backtrack(nums) #递归
|
||||
path.pop() #回溯
|
||||
backtrack(nums)
|
||||
return res
|
||||
```
|
||||
|
||||
Go:
|
||||
```Go
|
||||
|
@ -141,7 +141,20 @@ func canJUmp(nums []int) bool {
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
Javascript:
|
||||
```Javascript
|
||||
var canJump = function(nums) {
|
||||
if(nums.length === 1) return true
|
||||
let cover = 0
|
||||
for(let i = 0; i <= cover; i++) {
|
||||
cover = Math.max(cover, i + nums[i])
|
||||
if(cover >= nums.length - 1) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
-----------------------
|
||||
|
@ -141,16 +141,7 @@ Java:
|
||||
class Solution {
|
||||
public int[][] merge(int[][] intervals) {
|
||||
List<int[]> res = new LinkedList<>();
|
||||
Arrays.sort(intervals, new Comparator<int[]>() {
|
||||
@Override
|
||||
public int compare(int[] o1, int[] o2) {
|
||||
if (o1[0] != o2[0]) {
|
||||
return Integer.compare(o1[0],o2[0]);
|
||||
} else {
|
||||
return Integer.compare(o1[1],o2[1]);
|
||||
}
|
||||
}
|
||||
});
|
||||
Arrays.sort(intervals, (o1, o2) -> Integer.compare(o1[0], o2[0]));
|
||||
|
||||
int start = intervals[0][0];
|
||||
for (int i = 1; i < intervals.length; i++) {
|
||||
|
@ -8,6 +8,8 @@
|
||||
|
||||
## 72. 编辑距离
|
||||
|
||||
https://leetcode-cn.com/problems/edit-distance/
|
||||
|
||||
给你两个单词 word1 和 word2,请你计算出将 word1 转换成 word2 所使用的最少操作数 。
|
||||
|
||||
你可以对一个单词进行如下三种操作:
|
||||
@ -16,23 +18,23 @@
|
||||
* 删除一个字符
|
||||
* 替换一个字符
|
||||
|
||||
示例 1:
|
||||
输入:word1 = "horse", word2 = "ros"
|
||||
输出:3
|
||||
解释:
|
||||
horse -> rorse (将 'h' 替换为 'r')
|
||||
rorse -> rose (删除 'r')
|
||||
rose -> ros (删除 'e')
|
||||
示例 1:
|
||||
输入:word1 = "horse", word2 = "ros"
|
||||
输出:3
|
||||
解释:
|
||||
horse -> rorse (将 'h' 替换为 'r')
|
||||
rorse -> rose (删除 'r')
|
||||
rose -> ros (删除 'e')
|
||||
|
||||
示例 2:
|
||||
输入:word1 = "intention", word2 = "execution"
|
||||
输出:5
|
||||
解释:
|
||||
intention -> inention (删除 't')
|
||||
inention -> enention (将 'i' 替换为 'e')
|
||||
enention -> exention (将 'n' 替换为 'x')
|
||||
exention -> exection (将 'n' 替换为 'c')
|
||||
exection -> execution (插入 'u')
|
||||
示例 2:
|
||||
输入:word1 = "intention", word2 = "execution"
|
||||
输出:5
|
||||
解释:
|
||||
intention -> inention (删除 't')
|
||||
inention -> enention (将 'i' 替换为 'e')
|
||||
enention -> exention (将 'n' 替换为 'x')
|
||||
exention -> exection (将 'n' 替换为 'c')
|
||||
exection -> execution (插入 'u')
|
||||
|
||||
|
||||
提示:
|
||||
|
@ -147,7 +147,7 @@ public:
|
||||
|
||||
|
||||
Java:
|
||||
```
|
||||
```java
|
||||
class Solution {
|
||||
List<List<Integer>> result = new ArrayList<>();
|
||||
LinkedList<Integer> path = new LinkedList<>();
|
||||
@ -220,6 +220,28 @@ func backtrack(n,k,start int,track []int){
|
||||
}
|
||||
```
|
||||
|
||||
javaScript:
|
||||
|
||||
```js
|
||||
var combine = function(n, k) {
|
||||
const res = [], path = [];
|
||||
backtracking(n, k, 1);
|
||||
return res;
|
||||
function backtracking (n, k, i){
|
||||
const len = path.length;
|
||||
if(len === k) {
|
||||
res.push(Array.from(path));
|
||||
return;
|
||||
}
|
||||
for(let a = i; a <= n + len - k + 1; a++) {
|
||||
path.push(a);
|
||||
backtracking(n, k, a + 1);
|
||||
path.pop();
|
||||
}
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -205,7 +205,20 @@ class Solution {
|
||||
```
|
||||
|
||||
Python:
|
||||
|
||||
```python3
|
||||
class Solution:
|
||||
def subsets(self, nums: List[int]) -> List[List[int]]:
|
||||
res = []
|
||||
path = []
|
||||
def backtrack(nums,startIndex):
|
||||
res.append(path[:]) #收集子集,要放在终止添加的上面,否则会漏掉自己
|
||||
for i in range(startIndex,len(nums)): #当startIndex已经大于数组的长度了,就终止了,for循环本来也结束了,所以不需要终止条件
|
||||
path.append(nums[i])
|
||||
backtrack(nums,i+1) #递归
|
||||
path.pop() #回溯
|
||||
backtrack(nums,0)
|
||||
return res
|
||||
```
|
||||
|
||||
Go:
|
||||
```Go
|
||||
|
@ -208,7 +208,23 @@ class Solution {
|
||||
```
|
||||
|
||||
Python:
|
||||
|
||||
```python3
|
||||
class Solution:
|
||||
def subsetsWithDup(self, nums: List[int]) -> List[List[int]]:
|
||||
res = [] #存放符合条件结果的集合
|
||||
path = [] #用来存放符合条件结果
|
||||
def backtrack(nums,startIndex):
|
||||
res.append(path[:])
|
||||
for i in range(startIndex,len(nums)):
|
||||
if i > startIndex and nums[i] == nums[i - 1]: #我们要对同一树层使用过的元素进行跳过
|
||||
continue
|
||||
path.append(nums[i])
|
||||
backtrack(nums,i+1) #递归
|
||||
path.pop() #回溯
|
||||
nums = sorted(nums) #去重需要排序
|
||||
backtrack(nums,0)
|
||||
return res
|
||||
```
|
||||
|
||||
Go:
|
||||
```Go
|
||||
|
@ -338,6 +338,35 @@ class Solution(object):
|
||||
return ans```
|
||||
```
|
||||
|
||||
JavaScript:
|
||||
|
||||
```js
|
||||
/**
|
||||
* @param {string} s
|
||||
* @return {string[]}
|
||||
*/
|
||||
var restoreIpAddresses = function(s) {
|
||||
const res = [], path = [];
|
||||
backtracking(0, 0)
|
||||
return res;
|
||||
function backtracking(i) {
|
||||
const len = path.length;
|
||||
if(len > 4) return;
|
||||
if(len === 4 && i === s.length) {
|
||||
res.push(path.join("."));
|
||||
return;
|
||||
}
|
||||
for(let j = i; j < s.length; j++) {
|
||||
const str = s.substr(i, j - i + 1);
|
||||
if(str.length > 3 || +str > 255) break;
|
||||
if(str.length > 1 && str[0] === "0") break;
|
||||
path.push(str);
|
||||
backtracking(j + 1);
|
||||
path.pop()
|
||||
}
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
-----------------------
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
|
@ -186,7 +186,16 @@ class Solution {
|
||||
```
|
||||
|
||||
Python:
|
||||
|
||||
```python
|
||||
class Solution:
|
||||
def numTrees(self, n: int) -> int:
|
||||
dp = [0] * (n + 1)
|
||||
dp[0], dp[1] = 1, 1
|
||||
for i in range(2, n + 1):
|
||||
for j in range(1, i + 1):
|
||||
dp[i] += dp[j - 1] * dp[i - j]
|
||||
return dp[-1]
|
||||
```
|
||||
|
||||
Go:
|
||||
```Go
|
||||
|
@ -360,9 +360,129 @@ Java:
|
||||
|
||||
Python:
|
||||
|
||||
> 递归法
|
||||
```python
|
||||
class Solution:
|
||||
def isSymmetric(self, root: TreeNode) -> bool:
|
||||
if not root:
|
||||
return True
|
||||
return self.compare(root.left, root.right)
|
||||
|
||||
def compare(self, left, right):
|
||||
#首先排除空节点的情况
|
||||
if left == None and right != None: return False
|
||||
elif left != None and right == None: return False
|
||||
elif left == None and right == None: return True
|
||||
#排除了空节点,再排除数值不相同的情况
|
||||
elif left.val != right.val: return False
|
||||
|
||||
#此时就是:左右节点都不为空,且数值相同的情况
|
||||
#此时才做递归,做下一层的判断
|
||||
outside = self.compare(left.left, right.right) #左子树:左、 右子树:右
|
||||
inside = self.compare(left.right, right.left) #左子树:右、 右子树:左
|
||||
isSame = outside and inside #左子树:中、 右子树:中 (逻辑处理)
|
||||
return isSame
|
||||
```
|
||||
|
||||
> 迭代法: 使用队列
|
||||
```python
|
||||
import collections
|
||||
class Solution:
|
||||
def isSymmetric(self, root: TreeNode) -> bool:
|
||||
if not root:
|
||||
return True
|
||||
queue = collections.deque()
|
||||
queue.append(root.left) #将左子树头结点加入队列
|
||||
queue.append(root.right) #将右子树头结点加入队列
|
||||
while queue: #接下来就要判断这这两个树是否相互翻转
|
||||
leftNode = queue.popleft()
|
||||
rightNode = queue.popleft()
|
||||
if not leftNode and not rightNode: #左节点为空、右节点为空,此时说明是对称的
|
||||
continue
|
||||
|
||||
#左右一个节点不为空,或者都不为空但数值不相同,返回false
|
||||
if not leftNode or not rightNode or leftNode.val != rightNode.val:
|
||||
return False
|
||||
queue.append(leftNode.left) #加入左节点左孩子
|
||||
queue.append(rightNode.right) #加入右节点右孩子
|
||||
queue.append(leftNode.right) #加入左节点右孩子
|
||||
queue.append(rightNode.left) #加入右节点左孩子
|
||||
return True
|
||||
```
|
||||
|
||||
> 迭代法:使用栈
|
||||
```python
|
||||
class Solution:
|
||||
def isSymmetric(self, root: TreeNode) -> bool:
|
||||
if not root:
|
||||
return True
|
||||
st = [] #这里改成了栈
|
||||
st.append(root.left)
|
||||
st.append(root.right)
|
||||
while st:
|
||||
leftNode = st.pop()
|
||||
rightNode = st.pop()
|
||||
if not leftNode and not rightNode:
|
||||
continue
|
||||
if not leftNode or not rightNode or leftNode.val != rightNode.val:
|
||||
return False
|
||||
st.append(leftNode.left)
|
||||
st.append(rightNode.right)
|
||||
st.append(leftNode.right)
|
||||
st.append(rightNode.left)
|
||||
return True
|
||||
```
|
||||
|
||||
Go:
|
||||
|
||||
```go
|
||||
/**
|
||||
* Definition for a binary tree node.
|
||||
* type TreeNode struct {
|
||||
* Val int
|
||||
* Left *TreeNode
|
||||
* Right *TreeNode
|
||||
* }
|
||||
*/
|
||||
// 递归
|
||||
func defs(left *TreeNode, right *TreeNode) bool {
|
||||
if left == nil && right == nil {
|
||||
return true;
|
||||
};
|
||||
if left == nil || right == nil {
|
||||
return false;
|
||||
};
|
||||
if left.Val != right.Val {
|
||||
return false;
|
||||
}
|
||||
return defs(left.Left, right.Right) && defs(right.Left, left.Right);
|
||||
}
|
||||
func isSymmetric(root *TreeNode) bool {
|
||||
return defs(root.Left, root.Right);
|
||||
}
|
||||
|
||||
// 迭代
|
||||
func isSymmetric(root *TreeNode) bool {
|
||||
var queue []*TreeNode;
|
||||
if root != nil {
|
||||
queue = append(queue, root.Left, root.Right);
|
||||
}
|
||||
for len(queue) > 0 {
|
||||
left := queue[0];
|
||||
right := queue[1];
|
||||
queue = queue[2:];
|
||||
if left == nil && right == nil {
|
||||
continue;
|
||||
}
|
||||
if left == nil || right == nil || left.Val != right.Val {
|
||||
return false;
|
||||
};
|
||||
queue = append(queue, left.Left, right.Right, right.Left, left.Right);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
JavaScript
|
||||
```javascript
|
||||
|
@ -80,6 +80,40 @@ public:
|
||||
}
|
||||
};
|
||||
```
|
||||
python代码:
|
||||
|
||||
```python
|
||||
# Definition for a binary tree node.
|
||||
# class TreeNode:
|
||||
# def __init__(self, val=0, left=None, right=None):
|
||||
# self.val = val
|
||||
# self.left = left
|
||||
# self.right = right
|
||||
class Solution:
|
||||
def levelOrder(self, root: TreeNode) -> List[List[int]]:
|
||||
if not root:
|
||||
return []
|
||||
|
||||
quene = [root]
|
||||
out_list = []
|
||||
|
||||
while quene:
|
||||
in_list = []
|
||||
for _ in range(len(quene)):
|
||||
node = quene.pop(0)
|
||||
in_list.append(node.val)
|
||||
if node.left:
|
||||
quene.append(node.left)
|
||||
if node.right:
|
||||
quene.append(node.right)
|
||||
|
||||
out_list.append(in_list)
|
||||
|
||||
return out_list
|
||||
```
|
||||
|
||||
|
||||
|
||||
javascript代码:
|
||||
|
||||
```javascript
|
||||
@ -151,6 +185,43 @@ public:
|
||||
}
|
||||
};
|
||||
```
|
||||
python代码:
|
||||
|
||||
```python
|
||||
# Definition for a binary tree node.
|
||||
# class TreeNode:
|
||||
# def __init__(self, val=0, left=None, right=None):
|
||||
# self.val = val
|
||||
# self.left = left
|
||||
# self.right = right
|
||||
class Solution:
|
||||
def levelOrderBottom(self, root: TreeNode) -> List[List[int]]:
|
||||
if not root:
|
||||
return []
|
||||
quene = [root]
|
||||
out_list = []
|
||||
|
||||
while quene:
|
||||
in_list = []
|
||||
for _ in range(len(quene)):
|
||||
node = quene.pop(0)
|
||||
in_list.append(node.val)
|
||||
if node.left:
|
||||
quene.append(node.left)
|
||||
if node.right:
|
||||
quene.append(node.right)
|
||||
|
||||
out_list.append(in_list)
|
||||
|
||||
out_list.reverse()
|
||||
return out_list
|
||||
|
||||
# 执行用时:36 ms, 在所有 Python3 提交中击败了92.00%的用户
|
||||
# 内存消耗:15.2 MB, 在所有 Python3 提交中击败了63.76%的用户
|
||||
```
|
||||
|
||||
|
||||
|
||||
javascript代码
|
||||
|
||||
```javascript
|
||||
@ -212,6 +283,50 @@ public:
|
||||
}
|
||||
};
|
||||
```
|
||||
python代码:
|
||||
|
||||
```python
|
||||
# Definition for a binary tree node.
|
||||
# class TreeNode:
|
||||
# def __init__(self, val=0, left=None, right=None):
|
||||
# self.val = val
|
||||
# self.left = left
|
||||
# self.right = right
|
||||
class Solution:
|
||||
def rightSideView(self, root: TreeNode) -> List[int]:
|
||||
if not root:
|
||||
return []
|
||||
|
||||
# deque来自collections模块,不在力扣平台时,需要手动写入
|
||||
# 'from collections import deque' 导入
|
||||
# deque相比list的好处是,list的pop(0)是O(n)复杂度,deque的popleft()是O(1)复杂度
|
||||
|
||||
quene = deque([root])
|
||||
out_list = []
|
||||
|
||||
while quene:
|
||||
# 每次都取最后一个node就可以了
|
||||
node = quene[-1]
|
||||
out_list.append(node.val)
|
||||
|
||||
# 执行这个遍历的目的是获取下一层所有的node
|
||||
for _ in range(len(quene)):
|
||||
node = quene.popleft()
|
||||
if node.left:
|
||||
quene.append(node.left)
|
||||
if node.right:
|
||||
quene.append(node.right)
|
||||
|
||||
return out_list
|
||||
|
||||
# 执行用时:36 ms, 在所有 Python3 提交中击败了89.47%的用户
|
||||
# 内存消耗:14.6 MB, 在所有 Python3 提交中击败了96.65%的用户
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
javascript代码:
|
||||
|
||||
```javascript
|
||||
@ -275,6 +390,46 @@ public:
|
||||
|
||||
```
|
||||
|
||||
python代码:
|
||||
|
||||
```python
|
||||
# Definition for a binary tree node.
|
||||
# class TreeNode:
|
||||
# def __init__(self, val=0, left=None, right=None):
|
||||
# self.val = val
|
||||
# self.left = left
|
||||
# self.right = right
|
||||
class Solution:
|
||||
def averageOfLevels(self, root: TreeNode) -> List[float]:
|
||||
if not root:
|
||||
return []
|
||||
|
||||
quene = deque([root])
|
||||
out_list = []
|
||||
|
||||
while quene:
|
||||
in_list = []
|
||||
|
||||
for _ in range(len(quene)):
|
||||
node = quene.popleft()
|
||||
in_list.append(node.val)
|
||||
if node.left:
|
||||
quene.append(node.left)
|
||||
if node.right:
|
||||
quene.append(node.right)
|
||||
|
||||
out_list.append(in_list)
|
||||
|
||||
out_list = map(lambda x: sum(x) / len(x), out_list)
|
||||
|
||||
return out_list
|
||||
|
||||
# 执行用时:56 ms, 在所有 Python3 提交中击败了81.48%的用户
|
||||
# 内存消耗:17 MB, 在所有 Python3 提交中击败了89.68%的用户
|
||||
```
|
||||
|
||||
|
||||
|
||||
javascript代码:
|
||||
|
||||
```javascript
|
||||
@ -351,7 +506,68 @@ public:
|
||||
};
|
||||
```
|
||||
|
||||
python代码:
|
||||
|
||||
```python
|
||||
"""
|
||||
# Definition for a Node.
|
||||
class Node:
|
||||
def __init__(self, val=None, children=None):
|
||||
self.val = val
|
||||
self.children = children
|
||||
"""
|
||||
|
||||
class Solution:
|
||||
def levelOrder(self, root: 'Node') -> List[List[int]]:
|
||||
if not root:
|
||||
return []
|
||||
|
||||
quene = deque([root])
|
||||
out_list = []
|
||||
|
||||
while quene:
|
||||
in_list = []
|
||||
|
||||
for _ in range(len(quene)):
|
||||
node = quene.popleft()
|
||||
in_list.append(node.val)
|
||||
if node.children:
|
||||
# 这个地方要用extend而不是append,我们看下面的例子:
|
||||
# In [18]: alist=[]
|
||||
# In [19]: alist.append([1,2,3])
|
||||
# In [20]: alist
|
||||
# Out[20]: [[1, 2, 3]]
|
||||
# In [21]: alist.extend([4,5,6])
|
||||
# In [22]: alist
|
||||
# Out[22]: [[1, 2, 3], 4, 5, 6]
|
||||
# 可以看到extend对要添加的list进行了一个解包操作
|
||||
# print(root.children),可以得到children是一个包含
|
||||
# 孩子节点地址的list,我们使用for遍历quene的时候,
|
||||
# 希望quene是一个单层list,所以要用extend
|
||||
# 使用extend的情况,如果print(quene),结果是
|
||||
# deque([<__main__.Node object at 0x7f60763ae0a0>])
|
||||
# deque([<__main__.Node object at 0x7f607636e6d0>, <__main__.Node object at 0x7f607636e130>, <__main__.Node object at 0x7f607636e310>])
|
||||
# deque([<__main__.Node object at 0x7f607636e880>, <__main__.Node object at 0x7f607636ef10>])
|
||||
# 可以看到是单层list
|
||||
# 如果使用append,print(quene)的结果是
|
||||
# deque([<__main__.Node object at 0x7f18907530a0>])
|
||||
# deque([[<__main__.Node object at 0x7f18907136d0>, <__main__.Node object at 0x7f1890713130>, <__main__.Node object at 0x7f1890713310>]])
|
||||
# 可以看到是两层list,这样for的遍历就会报错
|
||||
|
||||
quene.extend(node.children)
|
||||
|
||||
out_list.append(in_list)
|
||||
|
||||
return out_list
|
||||
|
||||
# 执行用时:60 ms, 在所有 Python3 提交中击败了76.99%的用户
|
||||
# 内存消耗:16.5 MB, 在所有 Python3 提交中击败了89.19%的用户
|
||||
```
|
||||
|
||||
|
||||
|
||||
JavaScript代码:
|
||||
|
||||
```JavaScript
|
||||
var levelOrder = function(root) {
|
||||
//每一层可能有2个以上,所以不再使用node.left node.right
|
||||
@ -836,6 +1052,249 @@ func levelOrder(root *TreeNode) [][]int {
|
||||
return result
|
||||
}
|
||||
```
|
||||
> 二叉树的层序遍历(GO语言完全版)
|
||||
|
||||
```go
|
||||
/**
|
||||
102. 二叉树的层序遍历
|
||||
*/
|
||||
func levelOrder(root *TreeNode) [][]int {
|
||||
res:=[][]int{}
|
||||
if root==nil{//防止为空
|
||||
return res
|
||||
}
|
||||
queue:=list.New()
|
||||
queue.PushBack(root)
|
||||
var tmpArr []int
|
||||
for queue.Len()>0 {
|
||||
length:=queue.Len()//保存当前层的长度,然后处理当前层(十分重要,防止添加下层元素影响判断层中元素的个数)
|
||||
for i:=0;i<length;i++{
|
||||
node:=queue.Remove(queue.Front()).(*TreeNode)//出队列
|
||||
if node.Left!=nil{
|
||||
queue.PushBack(node.Left)
|
||||
}
|
||||
if node.Right!=nil{
|
||||
queue.PushBack(node.Right)
|
||||
}
|
||||
tmpArr=append(tmpArr,node.Val)//将值加入本层切片中
|
||||
}
|
||||
res=append(res,tmpArr)//放入结果集
|
||||
tmpArr=[]int{}//清空层的数据
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
/**
|
||||
107. 二叉树的层序遍历 II
|
||||
*/
|
||||
func levelOrderBottom(root *TreeNode) [][]int {
|
||||
queue:=list.New()
|
||||
res:=[][]int{}
|
||||
if root==nil{
|
||||
return res
|
||||
}
|
||||
queue.PushBack(root)
|
||||
for queue.Len()>0{
|
||||
length:=queue.Len()
|
||||
tmp:=[]int{}
|
||||
for i:=0;i<length;i++{
|
||||
node:=queue.Remove(queue.Front()).(*TreeNode)
|
||||
if node.Left!=nil{
|
||||
queue.PushBack(node.Left)
|
||||
}
|
||||
if node.Right!=nil{
|
||||
queue.PushBack(node.Right)
|
||||
}
|
||||
tmp=append(tmp,node.Val)
|
||||
}
|
||||
res=append(res,tmp)
|
||||
}
|
||||
//反转结果集
|
||||
for i:=0;i<len(res)/2;i++{
|
||||
res[i],res[len(res)-i-1]=res[len(res)-i-1],res[i]
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
/**
|
||||
199. 二叉树的右视图
|
||||
*/
|
||||
func rightSideView(root *TreeNode) []int {
|
||||
queue:=list.New()
|
||||
res:=[][]int{}
|
||||
var finaRes []int
|
||||
if root==nil{
|
||||
return finaRes
|
||||
}
|
||||
queue.PushBack(root)
|
||||
for queue.Len()>0{
|
||||
length:=queue.Len()
|
||||
tmp:=[]int{}
|
||||
for i:=0;i<length;i++{
|
||||
node:=queue.Remove(queue.Front()).(*TreeNode)
|
||||
if node.Left!=nil{
|
||||
queue.PushBack(node.Left)
|
||||
}
|
||||
if node.Right!=nil{
|
||||
queue.PushBack(node.Right)
|
||||
}
|
||||
tmp=append(tmp,node.Val)
|
||||
}
|
||||
res=append(res,tmp)
|
||||
}
|
||||
//取每一层的最后一个元素
|
||||
for i:=0;i<len(res);i++{
|
||||
finaRes=append(finaRes,res[i][len(res[i])-1])
|
||||
}
|
||||
return finaRes
|
||||
}
|
||||
|
||||
/**
|
||||
637. 二叉树的层平均值
|
||||
*/
|
||||
func averageOfLevels(root *TreeNode) []float64 {
|
||||
res:=[][]int{}
|
||||
var finRes []float64
|
||||
if root==nil{//防止为空
|
||||
return finRes
|
||||
}
|
||||
queue:=list.New()
|
||||
queue.PushBack(root)
|
||||
var tmpArr []int
|
||||
for queue.Len()>0 {
|
||||
length:=queue.Len()//保存当前层的长度,然后处理当前层(十分重要,防止添加下层元素影响判断层中元素的个数)
|
||||
for i:=0;i<length;i++{
|
||||
node:=queue.Remove(queue.Front()).(*TreeNode)//出队列
|
||||
if node.Left!=nil{
|
||||
queue.PushBack(node.Left)
|
||||
}
|
||||
if node.Right!=nil{
|
||||
queue.PushBack(node.Right)
|
||||
}
|
||||
tmpArr=append(tmpArr,node.Val)//将值加入本层切片中
|
||||
}
|
||||
res=append(res,tmpArr)//放入结果集
|
||||
tmpArr=[]int{}//清空层的数据
|
||||
}
|
||||
//计算每层的平均值
|
||||
length:=len(res)
|
||||
for i:=0;i<length;i++{
|
||||
var sum int
|
||||
for j:=0;j<len(res[i]);j++{
|
||||
sum+=res[i][j]
|
||||
}
|
||||
tmp:=float64(sum)/float64(len(res[i]))
|
||||
finRes=append(finRes,tmp)//将平均值放入结果集合
|
||||
}
|
||||
return finRes
|
||||
}
|
||||
|
||||
/**
|
||||
429. N 叉树的层序遍历
|
||||
*/
|
||||
|
||||
func levelOrder(root *Node) [][]int {
|
||||
queue:=list.New()
|
||||
res:=[][]int{}//结果集
|
||||
if root==nil{
|
||||
return res
|
||||
}
|
||||
queue.PushBack(root)
|
||||
for queue.Len()>0{
|
||||
length:=queue.Len()//记录当前层的数量
|
||||
var tmp []int
|
||||
for T:=0;T<length;T++{//该层的每个元素:一添加到该层的结果集中;二找到该元素的下层元素加入到队列中,方便下次使用
|
||||
myNode:=queue.Remove(queue.Front()).(*Node)
|
||||
tmp=append(tmp,myNode.Val)
|
||||
for i:=0;i<len(myNode.Children);i++{
|
||||
queue.PushBack(myNode.Children[i])
|
||||
}
|
||||
}
|
||||
res=append(res,tmp)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
/**
|
||||
515. 在每个树行中找最大值
|
||||
*/
|
||||
func largestValues(root *TreeNode) []int {
|
||||
res:=[][]int{}
|
||||
var finRes []int
|
||||
if root==nil{//防止为空
|
||||
return finRes
|
||||
}
|
||||
queue:=list.New()
|
||||
queue.PushBack(root)
|
||||
var tmpArr []int
|
||||
//层次遍历
|
||||
for queue.Len()>0 {
|
||||
length:=queue.Len()//保存当前层的长度,然后处理当前层(十分重要,防止添加下层元素影响判断层中元素的个数)
|
||||
for i:=0;i<length;i++{
|
||||
node:=queue.Remove(queue.Front()).(*TreeNode)//出队列
|
||||
if node.Left!=nil{
|
||||
queue.PushBack(node.Left)
|
||||
}
|
||||
if node.Right!=nil{
|
||||
queue.PushBack(node.Right)
|
||||
}
|
||||
tmpArr=append(tmpArr,node.Val)//将值加入本层切片中
|
||||
}
|
||||
res=append(res,tmpArr)//放入结果集
|
||||
tmpArr=[]int{}//清空层的数据
|
||||
}
|
||||
//找到每层的最大值
|
||||
for i:=0;i<len(res);i++{
|
||||
finRes=append(finRes,max(res[i]...))
|
||||
}
|
||||
return finRes
|
||||
}
|
||||
func max(vals...int) int {
|
||||
max:=int(math.Inf(-1))//负无穷
|
||||
for _, val := range vals {
|
||||
if val > max {
|
||||
max = val
|
||||
}
|
||||
}
|
||||
return max
|
||||
}
|
||||
/**
|
||||
116. 填充每个节点的下一个右侧节点指针
|
||||
117. 填充每个节点的下一个右侧节点指针 II
|
||||
*/
|
||||
|
||||
func connect(root *Node) *Node {
|
||||
res:=[][]*Node{}
|
||||
if root==nil{//防止为空
|
||||
return root
|
||||
}
|
||||
queue:=list.New()
|
||||
queue.PushBack(root)
|
||||
var tmpArr []*Node
|
||||
for queue.Len()>0 {
|
||||
length:=queue.Len()//保存当前层的长度,然后处理当前层(十分重要,防止添加下层元素影响判断层中元素的个数)
|
||||
for i:=0;i<length;i++{
|
||||
node:=queue.Remove(queue.Front()).(*Node)//出队列
|
||||
if node.Left!=nil{
|
||||
queue.PushBack(node.Left)
|
||||
}
|
||||
if node.Right!=nil{
|
||||
queue.PushBack(node.Right)
|
||||
}
|
||||
tmpArr=append(tmpArr,node)//将值加入本层切片中
|
||||
}
|
||||
res=append(res,tmpArr)//放入结果集
|
||||
tmpArr=[]*Node{}//清空层的数据
|
||||
}
|
||||
//遍历每层元素,指定next
|
||||
for i:=0;i<len(res);i++{
|
||||
for j:=0;j<len(res[i])-1;j++{
|
||||
res[i][j].Next=res[i][j+1]
|
||||
}
|
||||
}
|
||||
return root
|
||||
}
|
||||
```
|
||||
Javascript:
|
||||
```javascript
|
||||
var levelOrder = function (root) {
|
||||
@ -857,6 +1316,276 @@ var levelOrder = function (root) {
|
||||
};
|
||||
```
|
||||
|
||||
> 二叉树的层序遍历(Javascript语言完全版) (迭代 + 递归)
|
||||
|
||||
```js
|
||||
/**
|
||||
* 102. 二叉树的层序遍历
|
||||
* @param {TreeNode} root
|
||||
* @return {number[][]}
|
||||
*/
|
||||
|
||||
// 迭代
|
||||
|
||||
var levelOrder = function(root) {
|
||||
const queue = [], res = [];
|
||||
root && queue.push(root);
|
||||
while(len = queue.length) {
|
||||
const val = [];
|
||||
while(len--) {
|
||||
const node = queue.shift();
|
||||
val.push(node.val);
|
||||
node.left && queue.push(node.left);
|
||||
node.right && queue.push(node.right);
|
||||
}
|
||||
res.push(val);
|
||||
}
|
||||
return res;
|
||||
};
|
||||
|
||||
// 递归
|
||||
var levelOrder = function(root) {
|
||||
const res = [];
|
||||
function defs (root, i) {
|
||||
if(!root) return;
|
||||
if(!res[i]) res[i] = [];
|
||||
res[i].push(root.val)
|
||||
root.left && defs(root.left, i + 1);
|
||||
root.right && defs(root.right, i + 1);
|
||||
}
|
||||
defs(root, 0);
|
||||
return res;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* 107. 二叉树的层序遍历 II
|
||||
* @param {TreeNode} root
|
||||
* @return {number[][]}
|
||||
*/
|
||||
|
||||
// 迭代
|
||||
|
||||
var levelOrderBottom = function(root) {
|
||||
const queue = [], res = [];
|
||||
root && queue.push(root);
|
||||
while(len = queue.length) {
|
||||
const val = [];
|
||||
while(len--) {
|
||||
const node = queue.shift();
|
||||
val.push(node.val);
|
||||
node.left && queue.push(node.left);
|
||||
node.right && queue.push(node.right);
|
||||
}
|
||||
res.push(val);
|
||||
}
|
||||
return res.reverse()
|
||||
};
|
||||
|
||||
// 递归
|
||||
|
||||
var levelOrderBottom = function(root) {
|
||||
const res = [];
|
||||
function defs (root, i) {
|
||||
if(!root) return;
|
||||
if(!res[i]) res[i] = [];
|
||||
res[i].push(root.val);
|
||||
root.left && defs(root.left, i + 1);
|
||||
root.right && defs(root.right, i + 1);
|
||||
}
|
||||
defs(root, 0);
|
||||
return res.reverse();
|
||||
};
|
||||
|
||||
/**
|
||||
* 199. 二叉树的右视图
|
||||
* @param {TreeNode} root
|
||||
* @return {number[]}
|
||||
*/
|
||||
|
||||
// 迭代
|
||||
|
||||
var rightSideView = function(root) {
|
||||
const res = [], queue = [];
|
||||
root && queue.push(root);
|
||||
while(l = queue.length) {
|
||||
while (l--) {
|
||||
const {val, left, right} = queue.shift();
|
||||
!l && res.push(val);
|
||||
left && queue.push(left);
|
||||
right && queue.push(right);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
};
|
||||
|
||||
// 递归
|
||||
var rightSideView = function(root) {
|
||||
const res = [];
|
||||
function defs(root, i) {
|
||||
if(!root) return;
|
||||
res[i] = root.val;
|
||||
root.left && defs(root.left, i + 1);
|
||||
root.right && defs(root.right, i + 1);
|
||||
}
|
||||
defs(root, 0);
|
||||
return res;
|
||||
};
|
||||
|
||||
/**
|
||||
* 637. 二叉树的层平均值
|
||||
* @param {TreeNode} root
|
||||
* @return {number[]}
|
||||
*/
|
||||
|
||||
// 迭代
|
||||
var averageOfLevels = function(root) {
|
||||
const queue = [], res = [];
|
||||
root && queue.push(root);
|
||||
while(len = queue.length) {
|
||||
let sum = 0, l = len;
|
||||
while(l--) {
|
||||
const {val, left, right} = queue.shift();
|
||||
sum += val;
|
||||
left && queue.push(left);
|
||||
right && queue.push(right);
|
||||
}
|
||||
res.push(sum/len);
|
||||
}
|
||||
return res;
|
||||
};
|
||||
|
||||
// 递归
|
||||
var averageOfLevels = function(root) {
|
||||
const resCount = [], res = [];
|
||||
function defs(root, i) {
|
||||
if(!root) return;
|
||||
if(isNaN(res[i])) resCount[i] = res[i] = 0;
|
||||
res[i] += root.val;
|
||||
resCount[i]++;
|
||||
root.left && defs(root.left, i + 1);
|
||||
root.right && defs(root.right, i + 1);
|
||||
}
|
||||
defs(root, 0);
|
||||
return res.map((val, i) => val / resCount[i]);
|
||||
};
|
||||
|
||||
/**
|
||||
* 515. 在每个树行中找最大值
|
||||
* @param {TreeNode} root
|
||||
* @return {number[]}
|
||||
*/
|
||||
|
||||
// 迭代
|
||||
const MIN_G = Number.MIN_SAFE_INTEGER;
|
||||
var largestValues = function(root) {
|
||||
const queue = [], res = [];
|
||||
root && queue.push(root);
|
||||
while(len = queue.length) {
|
||||
let max = MIN_G;
|
||||
while(len--) {
|
||||
const {val, left, right} = queue.shift();
|
||||
max = max > val ? max : val;
|
||||
left && queue.push(left);
|
||||
right && queue.push(right);
|
||||
}
|
||||
res.push(max);
|
||||
}
|
||||
return res;
|
||||
};
|
||||
|
||||
// 递归
|
||||
var largestValues = function(root) {
|
||||
const res = [];
|
||||
function defs (root, i) {
|
||||
if(!root) return;
|
||||
if(isNaN(res[i])) res[i] = root.val;
|
||||
res[i] = res[i] > root.val ? res[i] : root.val;
|
||||
root.left && defs(root.left, i + 1);
|
||||
root.right && defs(root.right, i + 1);
|
||||
}
|
||||
defs(root, 0);
|
||||
return res;
|
||||
};
|
||||
|
||||
/**
|
||||
* 429. N 叉树的层序遍历
|
||||
* @param {Node|null} root
|
||||
* @return {number[][]}
|
||||
*/
|
||||
|
||||
// 迭代
|
||||
var levelOrder = function(root) {
|
||||
const queue = [], res = [];
|
||||
root && queue.push(root);
|
||||
while(len = queue.length) {
|
||||
const vals = [];
|
||||
while(len--) {
|
||||
const {val, children} = queue.shift();
|
||||
vals.push(val);
|
||||
for(const e of children) {
|
||||
queue.push(e);
|
||||
}
|
||||
}
|
||||
res.push(vals);
|
||||
}
|
||||
return res;
|
||||
};
|
||||
|
||||
// 递归
|
||||
|
||||
var levelOrder = function(root) {
|
||||
const res = [];
|
||||
function defs (root, i) {
|
||||
if(!root) return;
|
||||
if(!res[i]) res[i] = [];
|
||||
res[i].push(root.val);
|
||||
for(const e of root.children) {
|
||||
defs(e, i + 1);
|
||||
}
|
||||
}
|
||||
defs(root, 0);
|
||||
return res;
|
||||
};
|
||||
|
||||
/**
|
||||
* 116. 填充每个节点的下一个右侧节点指针
|
||||
* 117. 填充每个节点的下一个右侧节点指针 II
|
||||
* @param {Node} root
|
||||
* @return {Node}
|
||||
*/
|
||||
|
||||
// 迭代
|
||||
var connect = function(root) {
|
||||
const queue = [];
|
||||
root && queue.push(root);
|
||||
while(len = queue.length) {
|
||||
while(len--) {
|
||||
const node1 = queue.shift(),
|
||||
node2 = len ? queue[0] : null;
|
||||
node1.next = node2;
|
||||
node1.left && queue.push(node1.left);
|
||||
node1.right && queue.push(node1.right);
|
||||
}
|
||||
}
|
||||
return root;
|
||||
};
|
||||
|
||||
// 递归
|
||||
var connect = function(root) {
|
||||
const res = [];
|
||||
function defs (root, i) {
|
||||
if(!root) return;
|
||||
if(res[i]) res[i].next = root;
|
||||
res[i] = root;
|
||||
root.left && defs(root.left, i + 1);
|
||||
root.right && defs(root.right, i + 1);
|
||||
}
|
||||
defs(root, 0);
|
||||
return root;
|
||||
};
|
||||
```
|
||||
|
||||
-----------------------
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
|
@ -281,9 +281,163 @@ class Solution {
|
||||
|
||||
Python:
|
||||
|
||||
104.二叉树的最大深度
|
||||
> 递归法:
|
||||
```python
|
||||
class Solution:
|
||||
def maxDepth(self, root: TreeNode) -> int:
|
||||
return self.getDepth(root)
|
||||
|
||||
def getDepth(self, node):
|
||||
if not node:
|
||||
return 0
|
||||
leftDepth = self.getDepth(node.left) #左
|
||||
rightDepth = self.getDepth(node.right) #右
|
||||
depth = 1 + max(leftDepth, rightDepth) #中
|
||||
return depth
|
||||
```
|
||||
> 递归法;精简代码
|
||||
```python
|
||||
class Solution:
|
||||
def maxDepth(self, root: TreeNode) -> int:
|
||||
if not root:
|
||||
return 0
|
||||
return 1 + max(self.maxDepth(root.left), self.maxDepth(root.right))
|
||||
```
|
||||
|
||||
> 迭代法:
|
||||
```python
|
||||
import collections
|
||||
class Solution:
|
||||
def maxDepth(self, root: TreeNode) -> int:
|
||||
if not root:
|
||||
return 0
|
||||
depth = 0 #记录深度
|
||||
queue = collections.deque()
|
||||
queue.append(root)
|
||||
while queue:
|
||||
size = len(queue)
|
||||
depth += 1
|
||||
for i in range(size):
|
||||
node = queue.popleft()
|
||||
if node.left:
|
||||
queue.append(node.left)
|
||||
if node.right:
|
||||
queue.append(node.right)
|
||||
return depth
|
||||
```
|
||||
|
||||
559.N叉树的最大深度
|
||||
> 递归法:
|
||||
```python
|
||||
class Solution:
|
||||
def maxDepth(self, root: 'Node') -> int:
|
||||
if not root:
|
||||
return 0
|
||||
depth = 0
|
||||
for i in range(len(root.children)):
|
||||
depth = max(depth, self.maxDepth(root.children[i]))
|
||||
return depth + 1
|
||||
```
|
||||
|
||||
> 迭代法:
|
||||
```python
|
||||
import collections
|
||||
class Solution:
|
||||
def maxDepth(self, root: 'Node') -> int:
|
||||
queue = collections.deque()
|
||||
if root:
|
||||
queue.append(root)
|
||||
depth = 0 #记录深度
|
||||
while queue:
|
||||
size = len(queue)
|
||||
depth += 1
|
||||
for i in range(size):
|
||||
node = queue.popleft()
|
||||
for j in range(len(node.children)):
|
||||
if node.children[j]:
|
||||
queue.append(node.children[j])
|
||||
return depth
|
||||
```
|
||||
|
||||
> 使用栈来模拟后序遍历依然可以
|
||||
```python
|
||||
class Solution:
|
||||
def maxDepth(self, root: 'Node') -> int:
|
||||
st = []
|
||||
if root:
|
||||
st.append(root)
|
||||
depth = 0
|
||||
result = 0
|
||||
while st:
|
||||
node = st.pop()
|
||||
if node != None:
|
||||
st.append(node) #中
|
||||
st.append(None)
|
||||
depth += 1
|
||||
for i in range(len(node.children)): #处理孩子
|
||||
if node.children[i]:
|
||||
st.append(node.children[i])
|
||||
|
||||
else:
|
||||
node = st.pop()
|
||||
depth -= 1
|
||||
result = max(result, depth)
|
||||
return result
|
||||
```
|
||||
|
||||
|
||||
Go:
|
||||
|
||||
```go
|
||||
/**
|
||||
* Definition for a binary tree node.
|
||||
* type TreeNode struct {
|
||||
* Val int
|
||||
* Left *TreeNode
|
||||
* Right *TreeNode
|
||||
* }
|
||||
*/
|
||||
func max (a, b int) int {
|
||||
if a > b {
|
||||
return a;
|
||||
}
|
||||
return b;
|
||||
}
|
||||
// 递归
|
||||
func maxDepth(root *TreeNode) int {
|
||||
if root == nil {
|
||||
return 0;
|
||||
}
|
||||
return max(maxDepth(root.Left), maxDepth(root.Right)) + 1;
|
||||
}
|
||||
|
||||
// 遍历
|
||||
func maxDepth(root *TreeNode) int {
|
||||
levl := 0;
|
||||
queue := make([]*TreeNode, 0);
|
||||
if root != nil {
|
||||
queue = append(queue, root);
|
||||
}
|
||||
for l := len(queue); l > 0; {
|
||||
for ;l > 0;l-- {
|
||||
node := queue[0];
|
||||
if node.Left != nil {
|
||||
queue = append(queue, node.Left);
|
||||
}
|
||||
if node.Right != nil {
|
||||
queue = append(queue, node.Right);
|
||||
}
|
||||
queue = queue[1:];
|
||||
}
|
||||
levl++;
|
||||
l = len(queue);
|
||||
}
|
||||
return levl;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
JavaScript
|
||||
```javascript
|
||||
|
@ -498,6 +498,62 @@ class Solution {
|
||||
|
||||
Python:
|
||||
|
||||
> 递归法:
|
||||
```python
|
||||
class Solution:
|
||||
def isBalanced(self, root: TreeNode) -> bool:
|
||||
return True if self.getDepth(root) != -1 else False
|
||||
|
||||
#返回以该节点为根节点的二叉树的高度,如果不是二叉搜索树了则返回-1
|
||||
def getDepth(self, node):
|
||||
if not node:
|
||||
return 0
|
||||
leftDepth = self.getDepth(node.left)
|
||||
if leftDepth == -1: return -1 #说明左子树已经不是二叉平衡树
|
||||
rightDepth = self.getDepth(node.right)
|
||||
if rightDepth == -1: return -1 #说明右子树已经不是二叉平衡树
|
||||
return -1 if abs(leftDepth - rightDepth)>1 else 1 + max(leftDepth, rightDepth)
|
||||
```
|
||||
|
||||
> 迭代法:
|
||||
```python
|
||||
class Solution:
|
||||
def isBalanced(self, root: TreeNode) -> bool:
|
||||
st = []
|
||||
if not root:
|
||||
return True
|
||||
st.append(root)
|
||||
while st:
|
||||
node = st.pop() #中
|
||||
if abs(self.getDepth(node.left) - self.getDepth(node.right)) > 1:
|
||||
return False
|
||||
if node.right:
|
||||
st.append(node.right) #右(空节点不入栈)
|
||||
if node.left:
|
||||
st.append(node.left) #左(空节点不入栈)
|
||||
return True
|
||||
|
||||
def getDepth(self, cur):
|
||||
st = []
|
||||
if cur:
|
||||
st.append(cur)
|
||||
depth = 0
|
||||
result = 0
|
||||
while st:
|
||||
node = st.pop()
|
||||
if node:
|
||||
st.append(node) #中
|
||||
st.append(None)
|
||||
depth += 1
|
||||
if node.right: st.append(node.right) #右
|
||||
if node.left: st.append(node.left) #左
|
||||
else:
|
||||
node = st.pop()
|
||||
depth -= 1
|
||||
result = max(result, depth)
|
||||
return result
|
||||
```
|
||||
|
||||
|
||||
Go:
|
||||
```Go
|
||||
|
@ -301,6 +301,64 @@ class Solution:
|
||||
|
||||
Go:
|
||||
|
||||
```go
|
||||
/**
|
||||
* Definition for a binary tree node.
|
||||
* type TreeNode struct {
|
||||
* Val int
|
||||
* Left *TreeNode
|
||||
* Right *TreeNode
|
||||
* }
|
||||
*/
|
||||
func min(a, b int) int {
|
||||
if a < b {
|
||||
return a;
|
||||
}
|
||||
return b;
|
||||
}
|
||||
// 递归
|
||||
func minDepth(root *TreeNode) int {
|
||||
if root == nil {
|
||||
return 0;
|
||||
}
|
||||
if root.Left == nil && root.Right != nil {
|
||||
return 1 + minDepth(root.Right);
|
||||
}
|
||||
if root.Right == nil && root.Left != nil {
|
||||
return 1 + minDepth(root.Left);
|
||||
}
|
||||
return min(minDepth(root.Left), minDepth(root.Right)) + 1;
|
||||
}
|
||||
|
||||
// 迭代
|
||||
|
||||
func minDepth(root *TreeNode) int {
|
||||
dep := 0;
|
||||
queue := make([]*TreeNode, 0);
|
||||
if root != nil {
|
||||
queue = append(queue, root);
|
||||
}
|
||||
for l := len(queue); l > 0; {
|
||||
dep++;
|
||||
for ; l > 0; l-- {
|
||||
node := queue[0];
|
||||
if node.Left == nil && node.Right == nil {
|
||||
return dep;
|
||||
}
|
||||
if node.Left != nil {
|
||||
queue = append(queue, node.Left);
|
||||
}
|
||||
if node.Right != nil {
|
||||
queue = append(queue, node.Right);
|
||||
}
|
||||
queue = queue[1:];
|
||||
}
|
||||
l = len(queue);
|
||||
}
|
||||
return dep;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
JavaScript:
|
||||
|
||||
|
@ -345,6 +345,36 @@ class Solution {
|
||||
return hasPathSum(root.left, targetSum - root.val) || hasPathSum(root.right, targetSum - root.val);
|
||||
}
|
||||
}
|
||||
```
|
||||
迭代
|
||||
```java
|
||||
class Solution {
|
||||
public boolean hasPathSum(TreeNode root, int targetSum) {
|
||||
if(root==null)return false;
|
||||
Stack<TreeNode> stack1 = new Stack<>();
|
||||
Stack<Integer> stack2 = new Stack<>();
|
||||
stack1.push(root);stack2.push(root.val);
|
||||
while(!stack1.isEmpty()){
|
||||
int size = stack1.size();
|
||||
for(int i=0;i<size;i++){
|
||||
TreeNode node = stack1.pop();int sum=stack2.pop();
|
||||
// 如果该节点是叶子节点了,同时该节点的路径数值等于sum,那么就返回true
|
||||
if(node.left==null && node.right==null && sum==targetSum)return true;
|
||||
// 右节点,压进去一个节点的时候,将该节点的路径数值也记录下来
|
||||
if(node.right!=null){
|
||||
stack1.push(node.right);stack2.push(sum+node.right.val);
|
||||
}
|
||||
// 左节点,压进去一个节点的时候,将该节点的路径数值也记录下来
|
||||
if(node.left!=null){
|
||||
stack1.push(node.left);stack2.push(sum+node.left.val);
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
0113.路径总和-ii
|
||||
@ -524,6 +554,62 @@ let pathSum = function (root, targetSum) {
|
||||
};
|
||||
```
|
||||
|
||||
0112 路径总和
|
||||
```javascript
|
||||
var hasPathSum = function(root, targetSum) {
|
||||
//递归方法
|
||||
// 1. 确定函数参数
|
||||
const traversal = function(node,count){
|
||||
// 2. 确定终止条件
|
||||
if(node.left===null&&node.right===null&&count===0){
|
||||
return true;
|
||||
}
|
||||
if(node.left===null&&node.right===null){
|
||||
return false;
|
||||
}
|
||||
//3. 单层递归逻辑
|
||||
if(node.left){
|
||||
if(traversal(node.left,count-node.left.val)){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if(node.right){
|
||||
if(traversal(node.right,count-node.right.val)){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if(root===null){
|
||||
return false;
|
||||
}
|
||||
return traversal(root,targetSum-root.val);
|
||||
};
|
||||
```
|
||||
113 路径总和
|
||||
```javascript
|
||||
var pathSum = function(root, targetSum) {
|
||||
//递归方法
|
||||
let resPath = [],curPath = [];
|
||||
// 1. 确定递归函数参数
|
||||
const travelTree = function(node,count){
|
||||
curPath.push(node.val);
|
||||
count-=node.val;
|
||||
if(node.left===null&&node.right===null&&count===0){
|
||||
resPath.push([...curPath]);
|
||||
}
|
||||
node.left&&travelTree(node.left,count);
|
||||
node.right&&travelTree(node.right,count);
|
||||
let cur = curPath.pop();
|
||||
count-=cur;
|
||||
}
|
||||
if(root===null){
|
||||
return resPath;
|
||||
}
|
||||
travelTree(root,targetSum);
|
||||
return resPath;
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
@ -16,7 +16,7 @@
|
||||
|
||||
题目数据保证答案符合 32 位带符号整数范围。
|
||||
|
||||

|
||||

|
||||
|
||||
提示:
|
||||
|
||||
|
@ -16,14 +16,14 @@
|
||||
|
||||
返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0 。
|
||||
|
||||
示例 1:
|
||||
输入:[7,1,5,3,6,4]
|
||||
输出:5
|
||||
示例 1:
|
||||
输入:[7,1,5,3,6,4]
|
||||
输出:5
|
||||
解释:在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格;同时,你不能在买入前卖出股票。
|
||||
|
||||
示例 2:
|
||||
输入:prices = [7,6,4,3,1]
|
||||
输出:0
|
||||
示例 2:
|
||||
输入:prices = [7,6,4,3,1]
|
||||
输出:0
|
||||
解释:在这种情况下, 没有交易完成, 所以最大利润为 0。
|
||||
|
||||
|
||||
@ -33,7 +33,7 @@
|
||||
|
||||
这道题目最直观的想法,就是暴力,找最优间距了。
|
||||
|
||||
```
|
||||
```C++
|
||||
class Solution {
|
||||
public:
|
||||
int maxProfit(vector<int>& prices) {
|
||||
|
@ -101,9 +101,9 @@ dp[i][4] = max(dp[i - 1][4], dp[i - 1][3] + prices[i]);
|
||||
|
||||
所以dp[0][2] = 0;
|
||||
|
||||
第0天第二次买入操作,初始值应该是多少呢?
|
||||
第0天第二次买入操作,初始值应该是多少呢?应该不少同学疑惑,第一次还没买入呢,怎么初始化第二次买入呢?
|
||||
|
||||
不用管第几次,现在手头上没有现金,只要买入,现金就做相应的减少。
|
||||
第二次买入依赖于第一次卖出的状态,其实相当于第0天第一次买入了,第一次卖出了,然后在买入一次(第二次买入),那么现在手头上没有现金,只要买入,现金就做相应的减少。
|
||||
|
||||
所以第二次买入操作,初始化为:dp[0][3] = -prices[0];
|
||||
|
||||
|
@ -292,7 +292,7 @@ class Solution {
|
||||
```
|
||||
|
||||
Python:
|
||||
```python3
|
||||
```py
|
||||
class Solution:
|
||||
def partition(self, s: str) -> List[List[str]]:
|
||||
res = []
|
||||
@ -313,7 +313,38 @@ class Solution:
|
||||
|
||||
Go:
|
||||
|
||||
javaScript:
|
||||
|
||||
```js
|
||||
/**
|
||||
* @param {string} s
|
||||
* @return {string[][]}
|
||||
*/
|
||||
const isPalindrome = (s, l, r) => {
|
||||
for (let i = l, j = r; i < j; i++, j--) {
|
||||
if(s[i] !== s[j]) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
var partition = function(s) {
|
||||
const res = [], path = [], len = s.length;
|
||||
backtracking(0);
|
||||
return res;
|
||||
function backtracking(i) {
|
||||
if(i >= len) {
|
||||
res.push(Array.from(path));
|
||||
return;
|
||||
}
|
||||
for(let j = i; j < len; j++) {
|
||||
if(!isPalindrome(s, i, j)) continue;
|
||||
path.push(s.substr(i, j - i + 1));
|
||||
backtracking(j + 1);
|
||||
path.pop();
|
||||
}
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
-----------------------
|
||||
|
@ -241,7 +241,28 @@ class Solution:
|
||||
|
||||
Go:
|
||||
|
||||
Javascript:
|
||||
```Javascript
|
||||
var canCompleteCircuit = function(gas, cost) {
|
||||
const gasLen = gas.length
|
||||
let start = 0
|
||||
let curSum = 0
|
||||
let totalSum = 0
|
||||
|
||||
for(let i = 0; i < gasLen; i++) {
|
||||
curSum += gas[i] - cost[i]
|
||||
totalSum += gas[i] - cost[i]
|
||||
if(curSum < 0) {
|
||||
curSum = 0
|
||||
start = i + 1
|
||||
}
|
||||
}
|
||||
|
||||
if(totalSum < 0) return -1
|
||||
|
||||
return start
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
-----------------------
|
||||
|
@ -176,7 +176,30 @@ class Solution:
|
||||
|
||||
Go:
|
||||
|
||||
Javascript:
|
||||
```Javascript
|
||||
var candy = function(ratings) {
|
||||
let candys = new Array(ratings.length).fill(1)
|
||||
|
||||
for(let i = 1; i < ratings.length; i++) {
|
||||
if(ratings[i] > ratings[i - 1]) {
|
||||
candys[i] = candys[i - 1] + 1
|
||||
}
|
||||
}
|
||||
|
||||
for(let i = ratings.length - 2; i >= 0; i--) {
|
||||
if(ratings[i] > ratings[i + 1]) {
|
||||
candys[i] = Math.max(candys[i], candys[i + 1] + 1)
|
||||
}
|
||||
}
|
||||
|
||||
let count = candys.reduce((a, b) => {
|
||||
return a + b
|
||||
})
|
||||
|
||||
return count
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
-----------------------
|
||||
|
@ -234,25 +234,19 @@ class Solution:
|
||||
```
|
||||
|
||||
Go:
|
||||
```func detectCycle(head *ListNode) *ListNode {
|
||||
if head ==nil{
|
||||
return head
|
||||
}
|
||||
slow:=head
|
||||
fast:=head.Next
|
||||
|
||||
for fast!=nil&&fast.Next!=nil{
|
||||
if fast==slow{
|
||||
slow=head
|
||||
fast=fast.Next
|
||||
for fast!=slow {
|
||||
fast=fast.Next
|
||||
slow=slow.Next
|
||||
```go
|
||||
func detectCycle(head *ListNode) *ListNode {
|
||||
slow, fast := head, head
|
||||
for fast != nil && fast.Next != nil {
|
||||
slow = slow.Next
|
||||
fast = fast.Next.Next
|
||||
if slow == fast {
|
||||
for slow != head {
|
||||
slow = slow.Next
|
||||
head = head.Next
|
||||
}
|
||||
return slow
|
||||
return head
|
||||
}
|
||||
fast=fast.Next.Next
|
||||
slow=slow.Next
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -224,6 +224,22 @@ var evalRPN = function(tokens) {
|
||||
};
|
||||
```
|
||||
|
||||
python3
|
||||
|
||||
```python
|
||||
def evalRPN(tokens) -> int:
|
||||
stack = list()
|
||||
for i in range(len(tokens)):
|
||||
if tokens[i] not in ["+", "-", "*", "/"]:
|
||||
stack.append(tokens[i])
|
||||
else:
|
||||
tmp1 = stack.pop()
|
||||
tmp2 = stack.pop()
|
||||
res = eval(tmp2+tokens[i]+tmp1)
|
||||
stack.append(str(int(res)))
|
||||
return stack[-1]
|
||||
```
|
||||
|
||||
|
||||
-----------------------
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
|
@ -318,9 +318,61 @@ class Solution {
|
||||
|
||||
Python:
|
||||
|
||||
|
||||
Go:
|
||||
|
||||
```go
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func reverseWords(s string) string {
|
||||
//1.使用双指针删除冗余的空格
|
||||
slowIndex, fastIndex := 0, 0
|
||||
b := []byte(s)
|
||||
//删除头部冗余空格
|
||||
for len(b) > 0 && fastIndex < len(b) && b[fastIndex] == ' ' {
|
||||
fastIndex++
|
||||
}
|
||||
//删除单词间冗余空格
|
||||
for ; fastIndex < len(b); fastIndex++ {
|
||||
if fastIndex-1 > 0 && b[fastIndex-1] == b[fastIndex] && b[fastIndex] == ' ' {
|
||||
continue
|
||||
}
|
||||
b[slowIndex] = b[fastIndex]
|
||||
slowIndex++
|
||||
}
|
||||
//删除尾部冗余空格
|
||||
if slowIndex-1 > 0 && b[slowIndex-1] == ' ' {
|
||||
b = b[:slowIndex-1]
|
||||
} else {
|
||||
b = b[:slowIndex]
|
||||
}
|
||||
//2.反转整个字符串
|
||||
reverse(&b, 0, len(b)-1)
|
||||
//3.反转单个单词 i单词开始位置,j单词结束位置
|
||||
i := 0
|
||||
for i < len(b) {
|
||||
j := i
|
||||
for ; j < len(b) && b[j] != ' '; j++ {
|
||||
}
|
||||
reverse(&b, i, j-1)
|
||||
i = j
|
||||
i++
|
||||
}
|
||||
return string(b)
|
||||
}
|
||||
|
||||
func reverse(b *[]byte, left, right int) {
|
||||
for left < right {
|
||||
(*b)[left], (*b)[right] = (*b)[right], (*b)[left]
|
||||
left++
|
||||
right--
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -328,4 +380,4 @@ Go:
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
@ -108,9 +108,49 @@ class Solution {
|
||||
```
|
||||
|
||||
Python:
|
||||
|
||||
```python
|
||||
class Solution:
|
||||
def isHappy(self, n: int) -> bool:
|
||||
set_ = set()
|
||||
while 1:
|
||||
sum_ = self.getSum(n)
|
||||
if sum_ == 1:
|
||||
return True
|
||||
#如果这个sum曾经出现过,说明已经陷入了无限循环了,立刻return false
|
||||
if sum_ in set_:
|
||||
return False
|
||||
else:
|
||||
set_.add(sum_)
|
||||
n = sum_
|
||||
|
||||
#取数值各个位上的单数之和
|
||||
def getSum(self, n):
|
||||
sum_ = 0
|
||||
while n > 0:
|
||||
sum_ += (n%10) * (n%10)
|
||||
n //= 10
|
||||
return sum_
|
||||
```
|
||||
|
||||
Go:
|
||||
```go
|
||||
func isHappy(n int) bool {
|
||||
m := make(map[int]bool)
|
||||
for n != 1 && !m[n] {
|
||||
n, m[n] = getSum(n), true
|
||||
}
|
||||
return n == 1
|
||||
}
|
||||
|
||||
func getSum(n int) int {
|
||||
sum := 0
|
||||
for n > 0 {
|
||||
sum += (n % 10) * (n % 10)
|
||||
n = n / 10
|
||||
}
|
||||
return sum
|
||||
}
|
||||
```
|
||||
|
||||
javaScript:
|
||||
|
||||
|
@ -208,7 +208,23 @@ public ListNode removeElements(ListNode head, int val) {
|
||||
```
|
||||
|
||||
Python:
|
||||
|
||||
```python
|
||||
# Definition for singly-linked list.
|
||||
# class ListNode:
|
||||
# def __init__(self, val=0, next=None):
|
||||
# self.val = val
|
||||
# self.next = next
|
||||
class Solution:
|
||||
def removeElements(self, head: ListNode, val: int) -> ListNode:
|
||||
dummy_head = ListNode(next=head) #添加一个虚拟节点
|
||||
cur = dummy_head
|
||||
while(cur.next!=None):
|
||||
if(cur.next.val == val):
|
||||
cur.next = cur.next.next #删除cur.next节点
|
||||
else:
|
||||
cur = cur.next
|
||||
return dummy_head.next
|
||||
```
|
||||
|
||||
Go:
|
||||
|
||||
|
@ -143,7 +143,25 @@ class Solution {
|
||||
```
|
||||
|
||||
Python:
|
||||
|
||||
```python
|
||||
#双指针
|
||||
# Definition for singly-linked list.
|
||||
# class ListNode:
|
||||
# def __init__(self, val=0, next=None):
|
||||
# self.val = val
|
||||
# self.next = next
|
||||
class Solution:
|
||||
def reverseList(self, head: ListNode) -> ListNode:
|
||||
cur = head
|
||||
pre = None
|
||||
while(cur!=None):
|
||||
temp = cur.next # 保存一下 cur的下一个节点,因为接下来要改变cur->next
|
||||
cur.next = pre #反转
|
||||
#更新pre、cur指针
|
||||
pre = cur
|
||||
cur = temp
|
||||
return pre
|
||||
```
|
||||
|
||||
Go:
|
||||
|
||||
|
@ -180,7 +180,7 @@ if (sum > targetSum) { // 剪枝操作
|
||||
|
||||
最后C++代码如下:
|
||||
|
||||
```
|
||||
```c++
|
||||
class Solution {
|
||||
private:
|
||||
vector<vector<int>> result; // 存放结果集
|
||||
@ -262,7 +262,7 @@ class Solution {
|
||||
```
|
||||
|
||||
Python:
|
||||
```python3
|
||||
```py
|
||||
class Solution:
|
||||
def combinationSum3(self, k: int, n: int) -> List[List[int]]:
|
||||
res = [] #存放结果集
|
||||
@ -284,6 +284,44 @@ class Solution:
|
||||
|
||||
Go:
|
||||
|
||||
javaScript:
|
||||
|
||||
```js
|
||||
// 等差数列
|
||||
var maxV = k => k * (9 + 10 - k) / 2;
|
||||
var minV = k => k * (1 + k) / 2;
|
||||
var combinationSum3 = function(k, n) {
|
||||
if (k > 9 || k < 1) return [];
|
||||
// if (n > maxV(k) || n < minV(k)) return [];
|
||||
// if (n === maxV(k)) return [Array.from({length: k}).map((v, i) => 9 - i)];
|
||||
// if (n === minV(k)) return [Array.from({length: k}).map((v, i) => i + 1)];
|
||||
|
||||
const res = [], path = [];
|
||||
backtracking(k, n, 1, 0);
|
||||
return res;
|
||||
function backtracking(k, n, i, sum){
|
||||
const len = path.length;
|
||||
if (len > k || sum > n) return;
|
||||
if (maxV(k - len) < n - sum) return;
|
||||
if (minV(k - len) > n - sum) return;
|
||||
|
||||
if(len === k && sum == n) {
|
||||
res.push(Array.from(path));
|
||||
return;
|
||||
}
|
||||
|
||||
const min = Math.min(n - sum, 9 + len - k + 1);
|
||||
|
||||
for(let a = i; a <= min; a++) {
|
||||
path.push(a);
|
||||
sum += a;
|
||||
backtracking(k, n, a + 1, sum);
|
||||
path.pop();
|
||||
sum -= a;
|
||||
}
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
@ -240,6 +240,71 @@ class Solution {
|
||||
|
||||
Python:
|
||||
|
||||
> 递归法:
|
||||
```python
|
||||
class Solution:
|
||||
def countNodes(self, root: TreeNode) -> int:
|
||||
return self.getNodesNum(root)
|
||||
|
||||
def getNodesNum(self, cur):
|
||||
if not cur:
|
||||
return 0
|
||||
leftNum = self.getNodesNum(cur.left) #左
|
||||
rightNum = self.getNodesNum(cur.right) #右
|
||||
treeNum = leftNum + rightNum + 1 #中
|
||||
return treeNum
|
||||
```
|
||||
|
||||
> 递归法:精简版
|
||||
```python
|
||||
class Solution:
|
||||
def countNodes(self, root: TreeNode) -> int:
|
||||
if not root:
|
||||
return 0
|
||||
return 1 + self.countNodes(root.left) + self.countNodes(root.right)
|
||||
```
|
||||
|
||||
> 迭代法:
|
||||
```python
|
||||
import collections
|
||||
class Solution:
|
||||
def countNodes(self, root: TreeNode) -> int:
|
||||
queue = collections.deque()
|
||||
if root:
|
||||
queue.append(root)
|
||||
result = 0
|
||||
while queue:
|
||||
size = len(queue)
|
||||
for i in range(size):
|
||||
node = queue.popleft()
|
||||
result += 1 #记录节点数量
|
||||
if node.left:
|
||||
queue.append(node.left)
|
||||
if node.right:
|
||||
queue.append(node.right)
|
||||
return result
|
||||
```
|
||||
|
||||
> 完全二叉树
|
||||
```python
|
||||
class Solution:
|
||||
def countNodes(self, root: TreeNode) -> int:
|
||||
if not root:
|
||||
return 0
|
||||
left = root.left
|
||||
right = root.right
|
||||
leftHeight = 0 #这里初始为0是有目的的,为了下面求指数方便
|
||||
rightHeight = 0
|
||||
while left: #求左子树深度
|
||||
left = left.left
|
||||
leftHeight += 1
|
||||
while right: #求右子树深度
|
||||
right = right.right
|
||||
rightHeight += 1
|
||||
if leftHeight == rightHeight:
|
||||
return (2 << leftHeight) - 1 #注意(2<<1) 相当于2^2,所以leftHeight初始为0
|
||||
return self.countNodes(root.left) + self.countNodes(root.right) + 1
|
||||
```
|
||||
|
||||
Go:
|
||||
|
||||
|
@ -230,6 +230,56 @@ class Solution {
|
||||
|
||||
Python:
|
||||
|
||||
> 递归法:前序遍历
|
||||
```python
|
||||
class Solution:
|
||||
def invertTree(self, root: TreeNode) -> TreeNode:
|
||||
if not root:
|
||||
return None
|
||||
root.left, root.right = root.right, root.left #中
|
||||
self.invertTree(root.left) #左
|
||||
self.invertTree(root.right) #右
|
||||
return root
|
||||
```
|
||||
|
||||
> 迭代法:深度优先遍历(前序遍历)
|
||||
```python
|
||||
class Solution:
|
||||
def invertTree(self, root: TreeNode) -> TreeNode:
|
||||
if not root:
|
||||
return root
|
||||
st = []
|
||||
st.append(root)
|
||||
while st:
|
||||
node = st.pop()
|
||||
node.left, node.right = node.right, node.left #中
|
||||
if node.right:
|
||||
st.append(node.right) #右
|
||||
if node.left:
|
||||
st.append(node.left) #左
|
||||
return root
|
||||
```
|
||||
|
||||
> 迭代法:广度优先遍历(层序遍历)
|
||||
```python
|
||||
import collections
|
||||
class Solution:
|
||||
def invertTree(self, root: TreeNode) -> TreeNode:
|
||||
queue = collections.deque() #使用deque()
|
||||
if root:
|
||||
queue.append(root)
|
||||
while queue:
|
||||
size = len(queue)
|
||||
for i in range(size):
|
||||
node = queue.popleft()
|
||||
node.left, node.right = node.right, node.left #节点处理
|
||||
if node.left:
|
||||
queue.append(node.left)
|
||||
if node.right:
|
||||
queue.append(node.right)
|
||||
return root
|
||||
```
|
||||
|
||||
Go:
|
||||
```Go
|
||||
func invertTree(root *TreeNode) *TreeNode {
|
||||
|
@ -282,6 +282,50 @@ class MyQueue {
|
||||
|
||||
|
||||
Python:
|
||||
```python
|
||||
# 使用两个栈实现先进先出的队列
|
||||
class MyQueue:
|
||||
def __init__(self):
|
||||
"""
|
||||
Initialize your data structure here.
|
||||
"""
|
||||
self.stack1 = list()
|
||||
self.stack2 = list()
|
||||
|
||||
def push(self, x: int) -> None:
|
||||
"""
|
||||
Push element x to the back of queue.
|
||||
"""
|
||||
# self.stack1用于接受元素
|
||||
self.stack1.append(x)
|
||||
|
||||
def pop(self) -> int:
|
||||
"""
|
||||
Removes the element from in front of queue and returns that element.
|
||||
"""
|
||||
# self.stack2用于弹出元素,如果self.stack2为[],则将self.stack1中元素全部弹出给self.stack2
|
||||
if self.stack2 == []:
|
||||
while self.stack1:
|
||||
tmp = self.stack1.pop()
|
||||
self.stack2.append(tmp)
|
||||
return self.stack2.pop()
|
||||
|
||||
def peek(self) -> int:
|
||||
"""
|
||||
Get the front element.
|
||||
"""
|
||||
if self.stack2 == []:
|
||||
while self.stack1:
|
||||
tmp = self.stack1.pop()
|
||||
self.stack2.append(tmp)
|
||||
return self.stack2[-1]
|
||||
|
||||
def empty(self) -> bool:
|
||||
"""
|
||||
Returns whether the queue is empty.
|
||||
"""
|
||||
return self.stack1 == [] and self.stack2 == []
|
||||
```
|
||||
|
||||
|
||||
Go:
|
||||
|
@ -263,10 +263,75 @@ class Solution {
|
||||
```
|
||||
|
||||
Python:
|
||||
```python
|
||||
class MyQueue: #单调队列(从大到小
|
||||
def __init__(self):
|
||||
self.queue = [] #使用list来实现单调队列
|
||||
|
||||
#每次弹出的时候,比较当前要弹出的数值是否等于队列出口元素的数值,如果相等则弹出。
|
||||
#同时pop之前判断队列当前是否为空。
|
||||
def pop(self, value):
|
||||
if self.queue and value == self.queue[0]:
|
||||
self.queue.pop(0)#list.pop()时间复杂度为O(n),这里可以使用collections.deque()
|
||||
|
||||
#如果push的数值大于入口元素的数值,那么就将队列后端的数值弹出,直到push的数值小于等于队列入口元素的数值为止。
|
||||
#这样就保持了队列里的数值是单调从大到小的了。
|
||||
def push(self, value):
|
||||
while self.queue and value > self.queue[-1]:
|
||||
self.queue.pop()
|
||||
self.queue.append(value)
|
||||
|
||||
#查询当前队列里的最大值 直接返回队列前端也就是front就可以了。
|
||||
def front(self):
|
||||
return self.queue[0]
|
||||
|
||||
class Solution:
|
||||
def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
|
||||
que = MyQueue()
|
||||
result = []
|
||||
for i in range(k): #先将前k的元素放进队列
|
||||
que.push(nums[i])
|
||||
result.append(que.front()) #result 记录前k的元素的最大值
|
||||
for i in range(k, len(nums)):
|
||||
que.pop(nums[i - k]) #滑动窗口移除最前面元素
|
||||
que.push(nums[i]) #滑动窗口前加入最后面的元素
|
||||
result.append(que.front()) #记录对应的最大值
|
||||
return result
|
||||
```
|
||||
|
||||
|
||||
Go:
|
||||
|
||||
```go
|
||||
func maxSlidingWindow(nums []int, k int) []int {
|
||||
var queue []int
|
||||
var rtn []int
|
||||
|
||||
for f := 0; f < len(nums); f++ {
|
||||
//维持队列递减, 将 k 插入合适的位置, queue中 <=k 的 元素都不可能是窗口中的最大值, 直接弹出
|
||||
for len(queue) > 0 && nums[f] > nums[queue[len(queue)-1]] {
|
||||
queue = queue[:len(queue)-1]
|
||||
}
|
||||
// 等大的后来者也应入队
|
||||
if len(queue) == 0 || nums[f] <= nums[queue[len(queue)-1]] {
|
||||
queue = append(queue, f)
|
||||
}
|
||||
|
||||
if f >= k - 1 {
|
||||
rtn = append(rtn, nums[queue[0]])
|
||||
//弹出离开窗口的队首
|
||||
if f - k + 1 == queue[0] {
|
||||
queue = queue[1:]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return rtn
|
||||
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
Javascript:
|
||||
```javascript
|
||||
var maxSlidingWindow = function (nums, k) {
|
||||
|
@ -113,7 +113,22 @@ class Solution {
|
||||
```
|
||||
|
||||
Python:
|
||||
|
||||
```python
|
||||
class Solution:
|
||||
def isAnagram(self, s: str, t: str) -> bool:
|
||||
record = [0] * 26
|
||||
for i in range(len(s)):
|
||||
#并不需要记住字符a的ASCII,只要求出一个相对数值就可以了
|
||||
record[ord(s[i]) - ord("a")] += 1
|
||||
print(record)
|
||||
for i in range(len(t)):
|
||||
record[ord(t[i]) - ord("a")] -= 1
|
||||
for i in range(26):
|
||||
if record[i] != 0:
|
||||
#record数组如果有的元素不为零0,说明字符串s和t 一定是谁多了字符或者谁少了字符。
|
||||
return False
|
||||
return True
|
||||
```
|
||||
|
||||
Go:
|
||||
```go
|
||||
|
@ -51,10 +51,6 @@ dp[i]的定义讲贯彻整个解题过程,下面哪一步想不懂了,就想
|
||||
|
||||
**那有同学问了,j怎么就不拆分呢?**
|
||||
|
||||
j是从1开始遍历,拆分j的情况,在遍历j的过程中其实都计算过了。
|
||||
|
||||
**那有同学问了,j怎么就不拆分呢?**
|
||||
|
||||
j是从1开始遍历,拆分j的情况,在遍历j的过程中其实都计算过了。那么从1遍历j,比较(i - j) * j和dp[i - j] * j 取最大的。递推公式:dp[i] = max(dp[i], max((i - j) * j, dp[i - j] * j));
|
||||
|
||||
也可以这么理解,j * (i - j) 是单纯的把整数拆分为两个数相乘,而j * dp[i - j]是拆分成两个以及两个以上的个数相乘。
|
||||
@ -213,8 +209,19 @@ class Solution {
|
||||
```
|
||||
|
||||
Python:
|
||||
|
||||
|
||||
```python
|
||||
class Solution:
|
||||
def integerBreak(self, n: int) -> int:
|
||||
dp = [0] * (n + 1)
|
||||
dp[2] = 1
|
||||
for i in range(3, n + 1):
|
||||
# 假设对正整数 i 拆分出的第一个正整数是 j(1 <= j < i),则有以下两种方案:
|
||||
# 1) 将 i 拆分成 j 和 i−j 的和,且 i−j 不再拆分成多个正整数,此时的乘积是 j * (i-j)
|
||||
# 2) 将 i 拆分成 j 和 i−j 的和,且 i−j 继续拆分成多个正整数,此时的乘积是 j * dp[i-j]
|
||||
for j in range(1, i):
|
||||
dp[i] = max(dp[i], max(j * (i - j), j * dp[i - j]))
|
||||
return dp[n]
|
||||
```
|
||||
Go:
|
||||
|
||||
|
||||
|
@ -162,7 +162,33 @@ class Solution {
|
||||
|
||||
|
||||
Python:
|
||||
|
||||
```python
|
||||
#时间复杂度:O(nlogk)
|
||||
#空间复杂度:O(n)
|
||||
import heapq
|
||||
class Solution:
|
||||
def topKFrequent(self, nums: List[int], k: int) -> List[int]:
|
||||
#要统计元素出现频率
|
||||
map_ = {} #nums[i]:对应出现的次数
|
||||
for i in range(len(nums)):
|
||||
map_[nums[i]] = map_.get(nums[i], 0) + 1
|
||||
|
||||
#对频率排序
|
||||
#定义一个小顶堆,大小为k
|
||||
pri_que = [] #小顶堆
|
||||
|
||||
#用固定大小为k的小顶堆,扫面所有频率的数值
|
||||
for key, freq in map_.items():
|
||||
heapq.heappush(pri_que, (freq, key))
|
||||
if len(pri_que) > k: #如果堆的大小大于了K,则队列弹出,保证堆的大小一直为k
|
||||
heapq.heappop(pri_que)
|
||||
|
||||
#找出前K个高频元素,因为小顶堆先弹出的是最小的,所以倒叙来输出到数组
|
||||
result = [0] * k
|
||||
for i in range(k-1, -1, -1):
|
||||
result[i] = heapq.heappop(pri_que)[1]
|
||||
return result
|
||||
```
|
||||
|
||||
Go:
|
||||
|
||||
|
@ -132,6 +132,23 @@ class Solution:
|
||||
|
||||
|
||||
Go:
|
||||
```go
|
||||
func intersection(nums1 []int, nums2 []int) []int {
|
||||
m := make(map[int]int)
|
||||
for _, v := range nums1 {
|
||||
m[v] = 1
|
||||
}
|
||||
var res []int
|
||||
// 利用count>0,实现重复值只拿一次放入返回结果中
|
||||
for _, v := range nums2 {
|
||||
if count, ok := m[v]; ok && count > 0 {
|
||||
res = append(res, v)
|
||||
m[v]--
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
```
|
||||
|
||||
javaScript:
|
||||
|
||||
|
@ -138,7 +138,21 @@ class Solution {
|
||||
```
|
||||
|
||||
Python:
|
||||
|
||||
```python
|
||||
class Solution:
|
||||
def wiggleMaxLength(self, nums: List[int]) -> int:
|
||||
#贪心 求波峰数量 + 波谷数量
|
||||
if len(nums)<=1:
|
||||
return len(nums)
|
||||
cur, pre = 0,0 #当前一对差值,前一对差值
|
||||
count = 1#默认最右边有一个峰值
|
||||
for i in range(len(nums)-1):
|
||||
cur = nums[i+1] - nums[i]
|
||||
if((cur>0 and pre<=0) or (cur<0 and pre>=0)):
|
||||
count += 1
|
||||
pre = cur
|
||||
return count
|
||||
```
|
||||
|
||||
Go:
|
||||
|
||||
|
@ -163,7 +163,7 @@ class Solution {
|
||||
return dp[target];
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
Python:
|
||||
|
||||
|
@ -166,6 +166,21 @@ class Solution(object):
|
||||
```
|
||||
|
||||
Go:
|
||||
```go
|
||||
func canConstruct(ransomNote string, magazine string) bool {
|
||||
record := make([]int, 26)
|
||||
for _, v := range magazine {
|
||||
record[v-'a']++
|
||||
}
|
||||
for _, v := range ransomNote {
|
||||
record[v-'a']--
|
||||
if record[v-'a'] < 0 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
```
|
||||
|
||||
javaScript:
|
||||
|
||||
|
@ -226,7 +226,51 @@ class Solution:
|
||||
```
|
||||
Go:
|
||||
|
||||
|
||||
JavaScript:
|
||||
递归版本
|
||||
```javascript
|
||||
var sumOfLeftLeaves = function(root) {
|
||||
//采用后序遍历 递归遍历
|
||||
// 1. 确定递归函数参数
|
||||
const nodesSum = function(node){
|
||||
// 2. 确定终止条件
|
||||
if(node===null){
|
||||
return 0;
|
||||
}
|
||||
let leftValue = sumOfLeftLeaves(node.left);
|
||||
let rightValue = sumOfLeftLeaves(node.right);
|
||||
// 3. 单层递归逻辑
|
||||
let midValue = 0;
|
||||
if(node.left&&node.left.left===null&&node.left.right===null){
|
||||
midValue = node.left.val;
|
||||
}
|
||||
let sum = midValue + leftValue + rightValue;
|
||||
return sum;
|
||||
}
|
||||
return nodesSum(root);
|
||||
};
|
||||
```
|
||||
迭代版本
|
||||
```javascript
|
||||
var sumOfLeftLeaves = function(root) {
|
||||
//采用层序遍历
|
||||
if(root===null){
|
||||
return null;
|
||||
}
|
||||
let queue = [];
|
||||
let sum = 0;
|
||||
queue.push(root);
|
||||
while(queue.length){
|
||||
let node = queue.shift();
|
||||
if(node.left!==null&&node.left.left===null&&node.left.right===null){
|
||||
sum+=node.left.val;
|
||||
}
|
||||
node.left&&queue.push(node.left);
|
||||
node.right&&queue.push(node.right);
|
||||
}
|
||||
return sum;
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
@ -5,7 +5,6 @@
|
||||
<a href="https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ"><img src="https://img.shields.io/badge/知识星球-代码随想录-blue" alt=""></a>
|
||||
</p>
|
||||
<p align="center"><strong>欢迎大家<a href="https://mp.weixin.qq.com/s/tqCxrMEU-ajQumL1i8im9A">参与本项目</a>,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!</strong></p>
|
||||
# 动态规划:分割等和子集可以用01背包!
|
||||
|
||||
## 416. 分割等和子集
|
||||
|
||||
@ -29,6 +28,10 @@
|
||||
输出: false
|
||||
解释: 数组不能分割成两个元素和相等的子集.
|
||||
|
||||
提示:
|
||||
* 1 <= nums.length <= 200
|
||||
* 1 <= nums[i] <= 100
|
||||
|
||||
## 思路
|
||||
|
||||
这道题目初步看,是如下两题几乎是一样的,大家可以用回溯法,解决如下两题
|
||||
@ -174,7 +177,7 @@ public:
|
||||
|
||||
这道题目就是一道01背包应用类的题目,需要我们拆解题目,然后套入01背包的场景。
|
||||
|
||||
01背包相对于本题,主要要理解,题目中物品是nums[i],重量是nums[i]i,价值也是nums[i],背包体积是sum/2。
|
||||
01背包相对于本题,主要要理解,题目中物品是nums[i],重量是nums[i],价值也是nums[i],背包体积是sum/2。
|
||||
|
||||
看代码的话,就可以发现,基本就是按照01背包的写法来的。
|
||||
|
||||
@ -222,8 +225,18 @@ class Solution {
|
||||
```
|
||||
|
||||
Python:
|
||||
|
||||
|
||||
```python
|
||||
class Solution:
|
||||
def canPartition(self, nums: List[int]) -> bool:
|
||||
taraget = sum(nums)
|
||||
if taraget % 2 == 1: return False
|
||||
taraget //= 2
|
||||
dp = [0] * 10001
|
||||
for i in range(len(nums)):
|
||||
for j in range(taraget, nums[i] - 1, -1):
|
||||
dp[j] = max(dp[j], dp[j - nums[i]] + nums[i])
|
||||
return taraget == dp[taraget]
|
||||
```
|
||||
Go:
|
||||
|
||||
|
||||
|
@ -142,16 +142,8 @@ Java:
|
||||
```java
|
||||
class Solution {
|
||||
public int findMinArrowShots(int[][] points) {
|
||||
Arrays.sort(points, new Comparator<int[]>() {
|
||||
@Override
|
||||
public int compare(int[] o1, int[] o2) {
|
||||
if (o1[0] != o2[0]) {
|
||||
return Integer.compare(o1[0],o2[0]);
|
||||
} else {
|
||||
return Integer.compare(o1[0],o2[0]);
|
||||
}
|
||||
}
|
||||
});
|
||||
if (points.length == 0) return 0;
|
||||
Arrays.sort(points, (o1, o2) -> Integer.compare(o1[0], o2[0]));
|
||||
|
||||
int count = 1;
|
||||
for (int i = 1; i < points.length; i++) {
|
||||
|
@ -11,6 +11,7 @@
|
||||
|
||||
# 第454题.四数相加II
|
||||
|
||||
|
||||
https://leetcode-cn.com/problems/4sum-ii/
|
||||
|
||||
给定四个包含整数的数组列表 A , B , C , D ,计算有多少个元组 (i, j, k, l) ,使得 A[i] + B[j] + C[k] + D[l] = 0。
|
||||
@ -24,10 +25,8 @@ A = [ 1, 2]
|
||||
B = [-2,-1]
|
||||
C = [-1, 2]
|
||||
D = [ 0, 2]
|
||||
|
||||
输出:
|
||||
2
|
||||
|
||||
**解释:**
|
||||
两个元组如下:
|
||||
1. (0, 0, 0, 1) -> A[0] + B[0] + C[0] + D[1] = 1 + (-2) + (-1) + 2 = 0
|
||||
@ -154,6 +153,23 @@ class Solution(object):
|
||||
|
||||
|
||||
Go:
|
||||
```go
|
||||
func fourSumCount(nums1 []int, nums2 []int, nums3 []int, nums4 []int) int {
|
||||
m := make(map[int]int)
|
||||
count := 0
|
||||
for _, v1 := range nums1 {
|
||||
for _, v2 := range nums2 {
|
||||
m[v1+v2]++
|
||||
}
|
||||
}
|
||||
for _, v3 := range nums3 {
|
||||
for _, v4 := range nums4 {
|
||||
count += m[-v3-v4]
|
||||
}
|
||||
}
|
||||
return count
|
||||
}
|
||||
```
|
||||
|
||||
javaScript:
|
||||
|
||||
|
@ -134,7 +134,21 @@ class Solution {
|
||||
```
|
||||
|
||||
Python:
|
||||
|
||||
```python
|
||||
class Solution:
|
||||
def findContentChildren(self, g: List[int], s: List[int]) -> int:
|
||||
#先考虑胃口小的孩子
|
||||
g.sort()
|
||||
s.sort()
|
||||
i=j=0
|
||||
count = 0
|
||||
while(i<len(g) and j<len(s)):
|
||||
if g[i]<=s[j]:
|
||||
count+=1
|
||||
i+=1 #如果满足了,则继续遍历下一个孩子和下一块饼干
|
||||
j += 1 #如果最小的饼干没有满足当前最小胃口的孩子,则遍历下一块饼干
|
||||
return count
|
||||
```
|
||||
|
||||
Go:
|
||||
|
||||
|
@ -184,9 +184,116 @@ class Solution {
|
||||
|
||||
Python:
|
||||
|
||||
这里使用了前缀表统一减一的实现方式
|
||||
|
||||
```python
|
||||
class Solution:
|
||||
def repeatedSubstringPattern(self, s: str) -> bool:
|
||||
if len(s) == 0:
|
||||
return False
|
||||
nxt = [0] * len(s)
|
||||
self.getNext(nxt, s)
|
||||
if nxt[-1] != -1 and len(s) % (len(s) - (nxt[-1] + 1)) == 0:
|
||||
return True
|
||||
return False
|
||||
|
||||
def getNext(self, nxt, s):
|
||||
nxt[0] = -1
|
||||
j = -1
|
||||
for i in range(1, len(s)):
|
||||
while j >= 0 and s[i] != s[j+1]:
|
||||
j = nxt[j]
|
||||
if s[i] == s[j+1]:
|
||||
j += 1
|
||||
nxt[i] = j
|
||||
return nxt
|
||||
```
|
||||
|
||||
前缀表(不减一)的代码实现
|
||||
|
||||
```python
|
||||
class Solution:
|
||||
def repeatedSubstringPattern(self, s: str) -> bool:
|
||||
if len(s) == 0:
|
||||
return False
|
||||
nxt = [0] * len(s)
|
||||
self.getNext(nxt, s)
|
||||
if nxt[-1] != 0 and len(s) % (len(s) - nxt[-1]) == 0:
|
||||
return True
|
||||
return False
|
||||
|
||||
def getNext(self, nxt, s):
|
||||
nxt[0] = 0
|
||||
j = 0
|
||||
for i in range(1, len(s)):
|
||||
while j > 0 and s[i] != s[j]:
|
||||
j = nxt[j - 1]
|
||||
if s[i] == s[j]:
|
||||
j += 1
|
||||
nxt[i] = j
|
||||
return nxt
|
||||
```
|
||||
|
||||
Go:
|
||||
|
||||
这里使用了前缀表统一减一的实现方式
|
||||
|
||||
```go
|
||||
func repeatedSubstringPattern(s string) bool {
|
||||
n := len(s)
|
||||
if n == 0 {
|
||||
return false
|
||||
}
|
||||
next := make([]int, n)
|
||||
j := -1
|
||||
next[0] = j
|
||||
for i := 1; i < n; i++ {
|
||||
for j >= 0 && s[i] != s[j+1] {
|
||||
j = next[j]
|
||||
}
|
||||
if s[i] == s[j+1] {
|
||||
j++
|
||||
}
|
||||
next[i] = j
|
||||
}
|
||||
// next[n-1]+1 最长相同前后缀的长度
|
||||
if next[n-1] != -1 && n%(n-(next[n-1]+1)) == 0 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
```
|
||||
|
||||
前缀表(不减一)的代码实现
|
||||
|
||||
```go
|
||||
func repeatedSubstringPattern(s string) bool {
|
||||
n := len(s)
|
||||
if n == 0 {
|
||||
return false
|
||||
}
|
||||
j := 0
|
||||
next := make([]int, n)
|
||||
next[0] = j
|
||||
for i := 1; i < n; i++ {
|
||||
for j > 0 && s[i] != s[j] {
|
||||
j = next[j-1]
|
||||
}
|
||||
if s[i] == s[j] {
|
||||
j++
|
||||
}
|
||||
next[i] = j
|
||||
}
|
||||
// next[n-1] 最长相同前后缀的长度
|
||||
if next[n-1] != 0 && n%(n-next[n-1]) == 0 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -194,4 +301,4 @@ Go:
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
@ -190,7 +190,20 @@ class Solution {
|
||||
```
|
||||
|
||||
Python:
|
||||
|
||||
```python3
|
||||
class Solution:
|
||||
def findMaxForm(self, strs: List[str], m: int, n: int) -> int:
|
||||
dp = [[0] * (n + 1) for _ in range(m + 1)] # 默认初始化0
|
||||
# 遍历物品
|
||||
for str in strs:
|
||||
ones = str.count('1')
|
||||
zeros = str.count('0')
|
||||
# 遍历背包容量且从后向前遍历!
|
||||
for i in range(m, zeros - 1, -1):
|
||||
for j in range(n, ones - 1, -1):
|
||||
dp[i][j] = max(dp[i][j], dp[i - zeros][j - ones] + 1)
|
||||
return dp[m][n]
|
||||
```
|
||||
|
||||
Go:
|
||||
|
||||
|
@ -229,7 +229,28 @@ class Solution {
|
||||
|
||||
|
||||
Python:
|
||||
|
||||
```python3
|
||||
class Solution:
|
||||
def findSubsequences(self, nums: List[int]) -> List[List[int]]:
|
||||
res = []
|
||||
path = []
|
||||
def backtrack(nums,startIndex):
|
||||
repeat = [] #这里使用数组来进行去重操作
|
||||
if len(path) >=2:
|
||||
res.append(path[:]) #注意这里不要加return,要取树上的节点
|
||||
for i in range(startIndex,len(nums)):
|
||||
if nums[i] in repeat:
|
||||
continue
|
||||
if len(path) >= 1:
|
||||
if nums[i] < path[-1]:
|
||||
continue
|
||||
repeat.append(nums[i]) #记录这个元素在本层用过了,本层后面不能再用了
|
||||
path.append(nums[i])
|
||||
backtrack(nums,i+1)
|
||||
path.pop()
|
||||
backtrack(nums,0)
|
||||
return res
|
||||
```
|
||||
|
||||
Go:
|
||||
|
||||
|
@ -150,7 +150,7 @@ dp[j] 表示:填满j(包括j)这么大容积的包,有dp[i]种方法
|
||||
|
||||
有哪些来源可以推出dp[j]呢?
|
||||
|
||||
不考虑nums[i]的情况下,填满容量为j - nums[i]的背包,有dp[j - nums[i]]中方法。
|
||||
不考虑nums[i]的情况下,填满容量为j - nums[i]的背包,有dp[j - nums[i]]种方法。
|
||||
|
||||
那么只要搞到nums[i]的话,凑成dp[j]就有dp[j - nums[i]] 种方法。
|
||||
|
||||
@ -261,10 +261,50 @@ class Solution {
|
||||
```
|
||||
|
||||
Python:
|
||||
|
||||
```python
|
||||
class Solution:
|
||||
def findTargetSumWays(self, nums: List[int], target: int) -> int:
|
||||
sumValue = sum(nums)
|
||||
if target > sumValue or (sumValue + target) % 2 == 1: return 0
|
||||
bagSize = (sumValue + target) // 2
|
||||
dp = [0] * (bagSize + 1)
|
||||
dp[0] = 1
|
||||
for i in range(len(nums)):
|
||||
for j in range(bagSize, nums[i] - 1, -1):
|
||||
dp[j] += dp[j - nums[i]]
|
||||
return dp[bagSize]
|
||||
```
|
||||
|
||||
Go:
|
||||
|
||||
```go
|
||||
func findTargetSumWays(nums []int, target int) int {
|
||||
sum := 0
|
||||
for _, v := range nums {
|
||||
sum += v
|
||||
}
|
||||
if target > sum {
|
||||
return 0
|
||||
}
|
||||
if (sum+target)%2 == 1 {
|
||||
return 0
|
||||
}
|
||||
// 计算背包大小
|
||||
bag := (sum + target) / 2
|
||||
// 定义dp数组
|
||||
dp := make([]int, bag+1)
|
||||
// 初始化
|
||||
dp[0] = 1
|
||||
// 遍历顺序
|
||||
for i := 0; i < len(nums); i++ {
|
||||
for j := bag; j >= nums[i]; j-- {
|
||||
//推导公式
|
||||
dp[j] += dp[j-nums[i]]
|
||||
//fmt.Println(dp)
|
||||
}
|
||||
}
|
||||
return dp[bag]
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
@ -298,6 +298,53 @@ class Solution:
|
||||
```
|
||||
Go:
|
||||
|
||||
JavaScript:
|
||||
1. 递归版本
|
||||
```javascript
|
||||
var findBottomLeftValue = function(root) {
|
||||
//首先考虑递归遍历 前序遍历 找到最大深度的叶子节点即可
|
||||
let maxPath = 0,resNode = null;
|
||||
// 1. 确定递归函数的函数参数
|
||||
const dfsTree = function(node,curPath){
|
||||
// 2. 确定递归函数终止条件
|
||||
if(node.left===null&&node.right===null){
|
||||
if(curPath>maxPath){
|
||||
maxPath = curPath;
|
||||
resNode = node.val;
|
||||
}
|
||||
// return ;
|
||||
}
|
||||
node.left&&dfsTree(node.left,curPath+1);
|
||||
node.right&&dfsTree(node.right,curPath+1);
|
||||
}
|
||||
dfsTree(root,1);
|
||||
return resNode;
|
||||
};
|
||||
```
|
||||
2. 层序遍历
|
||||
```javascript
|
||||
var findBottomLeftValue = function(root) {
|
||||
//考虑层序遍历 记录最后一行的第一个节点
|
||||
let queue = [];
|
||||
if(root===null){
|
||||
return null;
|
||||
}
|
||||
queue.push(root);
|
||||
let resNode;
|
||||
while(queue.length){
|
||||
let length = queue.length;
|
||||
for(let i=0; i<length; i++){
|
||||
let node = queue.shift();
|
||||
if(i===0){
|
||||
resNode = node.val;
|
||||
}
|
||||
node.left&&queue.push(node.left);
|
||||
node.right&&queue.push(node.right);
|
||||
}
|
||||
}
|
||||
return resNode;
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
@ -106,27 +106,24 @@ Java:
|
||||
class Solution {
|
||||
public String reverseStr(String s, int k) {
|
||||
StringBuffer res = new StringBuffer();
|
||||
|
||||
for (int i = 0; i < s.length(); i += (2 * k)) {
|
||||
int length = s.length();
|
||||
int start = 0;
|
||||
while (start < length) {
|
||||
// 找到k处和2k处
|
||||
StringBuffer temp = new StringBuffer();
|
||||
// 剩余字符大于 k 个,每隔 2k 个字符的前 k 个字符进行反转
|
||||
if (i + k <= s.length()) {
|
||||
// 反转前 k 个字符
|
||||
temp.append(s.substring(i, i + k));
|
||||
res.append(temp.reverse());
|
||||
// 与length进行判断,如果大于length了,那就将其置为length
|
||||
int firstK = (start + k > length) ? length : start + k;
|
||||
int secondK = (start + (2 * k) > length) ? length : start + (2 * k);
|
||||
|
||||
// 反转完前 k 个字符之后,如果紧接着还有 k 个字符,则直接加入这 k 个字符
|
||||
if (i + 2 * k <= s.length()) {
|
||||
res.append(s.substring(i + k, i + 2 * k));
|
||||
// 不足 k 个字符,则直接加入剩下所有字符
|
||||
} else {
|
||||
res.append(s.substring(i + k, s.length()));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
// 剩余字符少于 k 个,则将剩余字符全部反转。
|
||||
temp.append(s.substring(i, s.length()));
|
||||
//无论start所处位置,至少会反转一次
|
||||
temp.append(s.substring(start, firstK));
|
||||
res.append(temp.reverse());
|
||||
|
||||
// 如果firstK到secondK之间有元素,这些元素直接放入res里即可。
|
||||
if (firstK < secondK) { //此时剩余长度一定大于k。
|
||||
res.append(s.substring(firstK, secondK));
|
||||
}
|
||||
start += (2 * k);
|
||||
}
|
||||
return res.toString();
|
||||
}
|
||||
@ -167,6 +164,30 @@ class Solution(object):
|
||||
|
||||
|
||||
Go:
|
||||
```go
|
||||
func reverseStr(s string, k int) string {
|
||||
ss := []byte(s)
|
||||
length := len(s)
|
||||
for i := 0; i < length; i += 2 * k {
|
||||
if i + k <= length {
|
||||
reverse(ss[i:i+k])
|
||||
} else {
|
||||
reverse(ss[i:length])
|
||||
}
|
||||
}
|
||||
return string(ss)
|
||||
}
|
||||
|
||||
func reverse(b []byte) {
|
||||
left := 0
|
||||
right := len(b) - 1
|
||||
for left < right {
|
||||
b[left], b[right] = b[right], b[left]
|
||||
left++
|
||||
right--
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
javaScript:
|
||||
|
||||
|
@ -128,7 +128,7 @@ public:
|
||||
|
||||
**此时遍历B数组的时候,就要从后向前遍历,这样避免重复覆盖**。
|
||||
|
||||
```
|
||||
```C++
|
||||
class Solution {
|
||||
public:
|
||||
int findLength(vector<int>& A, vector<int>& B) {
|
||||
|
@ -100,10 +100,66 @@ public:
|
||||
Java:
|
||||
|
||||
Python:
|
||||
|
||||
```Python
|
||||
class Solution:
|
||||
def sortedSquares(self, nums: List[int]) -> List[int]:
|
||||
n = len(nums)
|
||||
i,j,k = 0,n - 1,n - 1
|
||||
ans = [-1] * n
|
||||
while i <= j:
|
||||
lm = nums[i] ** 2
|
||||
rm = nums[j] ** 2
|
||||
if lm > rm:
|
||||
ans[k] = lm
|
||||
i += 1
|
||||
else:
|
||||
ans[k] = rm
|
||||
j -= 1
|
||||
k -= 1
|
||||
return ans
|
||||
```
|
||||
|
||||
Go:
|
||||
|
||||
```Go
|
||||
func sortedSquares(nums []int) []int {
|
||||
n := len(nums)
|
||||
i, j, k := 0, n-1, n-1
|
||||
ans := make([]int, n)
|
||||
for i <= j {
|
||||
lm, rm := nums[i]*nums[i], nums[j]*nums[j]
|
||||
if lm > rm {
|
||||
ans[k] = lm
|
||||
i++
|
||||
} else {
|
||||
ans[k] = rm
|
||||
j--
|
||||
}
|
||||
k--
|
||||
}
|
||||
return ans
|
||||
}
|
||||
```
|
||||
Rust
|
||||
```
|
||||
impl Solution {
|
||||
pub fn sorted_squares(nums: Vec<i32>) -> Vec<i32> {
|
||||
let n = nums.len();
|
||||
let (mut i,mut j,mut k) = (0,n - 1,n- 1);
|
||||
let mut ans = vec![0;n];
|
||||
while i <= j{
|
||||
if nums[i] * nums[i] < nums[j] * nums[j] {
|
||||
ans[k] = nums[j] * nums[j];
|
||||
j -= 1;
|
||||
}else{
|
||||
ans[k] = nums[i] * nums[i];
|
||||
i += 1;
|
||||
}
|
||||
k -= 1;
|
||||
}
|
||||
ans
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
-----------------------
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
|
@ -102,7 +102,7 @@ Java:
|
||||
```java
|
||||
class Solution {
|
||||
public int largestSumAfterKNegations(int[] A, int K) {
|
||||
if (A.length == 1) return A[0];
|
||||
if (A.length == 1) return k % 2 == 0 ? A[0] : -A[0];
|
||||
Arrays.sort(A);
|
||||
int sum = 0;
|
||||
int idx = 0;
|
||||
@ -140,7 +140,29 @@ class Solution:
|
||||
Go:
|
||||
|
||||
|
||||
Javascript:
|
||||
```Javascript
|
||||
var largestSumAfterKNegations = function(nums, k) {
|
||||
nums.sort((a, b) => {
|
||||
return Math.abs(b) - Math.abs(a)
|
||||
})
|
||||
for(let i = 0; i < nums.length; i++) {
|
||||
if(nums[i] < 0 && k > 0) {
|
||||
nums[i] *= -1
|
||||
k--
|
||||
}
|
||||
}
|
||||
|
||||
if(k > 0 && k % 2 === 1) {
|
||||
nums[nums.length - 1] *= -1
|
||||
}
|
||||
k = 0
|
||||
|
||||
return nums.reduce((a, b) => {
|
||||
return a + b
|
||||
})
|
||||
};
|
||||
```
|
||||
|
||||
-----------------------
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
|
@ -186,6 +186,23 @@ class Solution:
|
||||
|
||||
Go:
|
||||
|
||||
```go
|
||||
func removeDuplicates(s string) string {
|
||||
var stack []byte
|
||||
for i := 0; i < len(s);i++ {
|
||||
// 栈不空 且 与栈顶元素不等
|
||||
if len(stack) > 0 && stack[len(stack)-1] == s[i] {
|
||||
// 弹出栈顶元素 并 忽略当前元素(s[i])
|
||||
stack = stack[:len(stack)-1]
|
||||
}else{
|
||||
// 入栈
|
||||
stack = append(stack, s[i])
|
||||
}
|
||||
}
|
||||
return string(stack)
|
||||
}
|
||||
```
|
||||
|
||||
javaScript:
|
||||
|
||||
```js
|
||||
|
@ -178,10 +178,46 @@ class Solution {
|
||||
```
|
||||
|
||||
Python:
|
||||
|
||||
```python
|
||||
class Solution:
|
||||
def lastStoneWeightII(self, stones: List[int]) -> int:
|
||||
sumweight = sum(stones)
|
||||
target = sumweight // 2
|
||||
dp = [0] * 15001
|
||||
for i in range(len(stones)):
|
||||
for j in range(target, stones[i] - 1, -1):
|
||||
dp[j] = max(dp[j], dp[j - stones[i]] + stones[i])
|
||||
return sumweight - 2 * dp[target]
|
||||
```
|
||||
|
||||
Go:
|
||||
```go
|
||||
func lastStoneWeightII(stones []int) int {
|
||||
// 15001 = 30 * 1000 /2 +1
|
||||
dp := make([]int, 15001)
|
||||
// 求target
|
||||
sum := 0
|
||||
for _, v := range stones {
|
||||
sum += v
|
||||
}
|
||||
target := sum / 2
|
||||
// 遍历顺序
|
||||
for i := 0; i < len(stones); i++ {
|
||||
for j := target; j >= stones[i]; j-- {
|
||||
// 推导公式
|
||||
dp[j] = max(dp[j], dp[j-stones[i]]+stones[i])
|
||||
}
|
||||
}
|
||||
return sum - 2 * dp[target]
|
||||
}
|
||||
|
||||
func max(a, b int) int {
|
||||
if a > b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
@ -1,202 +0,0 @@
|
||||
<p align="center">
|
||||
<a href="https://mp.weixin.qq.com/s/RsdcQ9umo09R6cfnwXZlrQ"><img src="https://img.shields.io/badge/PDF下载-代码随想录-blueviolet" alt=""></a>
|
||||
<a href="https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw"><img src="https://img.shields.io/badge/刷题-微信群-green" alt=""></a>
|
||||
<a href="https://space.bilibili.com/525438321"><img src="https://img.shields.io/badge/B站-代码随想录-orange" alt=""></a>
|
||||
<a href="https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ"><img src="https://img.shields.io/badge/知识星球-代码随想录-blue" alt=""></a>
|
||||
</p>
|
||||
<p align="center"><strong>欢迎大家<a href="https://mp.weixin.qq.com/s/tqCxrMEU-ajQumL1i8im9A">参与本项目</a>,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!</strong></p>
|
||||
|
||||
## 二叉树理论基础
|
||||
|
||||
我们要开启新的征程了,大家跟上!
|
||||
|
||||
说道二叉树,大家对于二叉树其实都很熟悉了,本文呢我也不想教科书式的把二叉树的基础内容再啰嗦一遍,所以一下我讲的都是一些比较重点的内容。
|
||||
|
||||
相信只要耐心看完,都会有所收获。
|
||||
|
||||
## 二叉树的种类
|
||||
|
||||
在我们解题过程中二叉树有两种主要的形式:满二叉树和完全二叉树。
|
||||
|
||||
### 满二叉树
|
||||
|
||||
满二叉树:如果一棵二叉树只有度为0的结点和度为2的结点,并且度为0的结点在同一层上,则这棵二叉树为满二叉树。
|
||||
|
||||
如图所示:
|
||||
|
||||
<img src='https://img-blog.csdnimg.cn/20200806185805576.png' width=600> </img></div>
|
||||
|
||||
这棵二叉树为满二叉树,也可以说深度为 k,有 $(2^k)-1$ 个节点的二叉树。
|
||||
|
||||
|
||||
### 完全二叉树
|
||||
|
||||
什么是完全二叉树?
|
||||
|
||||
完全二叉树的定义如下:在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。若最底层为第 h 层,则该层包含 1 ~ $2^{(h-1)}$ 个节点。
|
||||
|
||||
**大家要自己看完全二叉树的定义,很多同学对完全二叉树其实不是真正的懂了。**
|
||||
|
||||
我来举一个典型的例子如题:
|
||||
|
||||
<img src='https://img-blog.csdnimg.cn/20200920221638903.png' width=600> </img></div>
|
||||
|
||||
相信不少同学最后一个二叉树是不是完全二叉树都中招了。
|
||||
|
||||
**之前我们刚刚讲过优先级队列其实是一个堆,堆就是一棵完全二叉树,同时保证父子节点的顺序关系。**
|
||||
|
||||
### 二叉搜索树
|
||||
|
||||
前面介绍的书,都没有数值的,而二叉搜索树是有数值的了,**二叉搜索树是一个有序树**。
|
||||
|
||||
|
||||
* 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
|
||||
* 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
|
||||
* 它的左、右子树也分别为二叉排序树
|
||||
|
||||
下面这两棵树都是搜索树
|
||||
<img src='https://img-blog.csdnimg.cn/20200806190304693.png' width=600> </img></div>
|
||||
|
||||
|
||||
### 平衡二叉搜索树
|
||||
|
||||
平衡二叉搜索树:又被称为AVL(Adelson-Velsky and Landis)树,且具有以下性质:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。
|
||||
|
||||
如图:
|
||||
|
||||
<img src='https://img-blog.csdnimg.cn/20200806190511967.png' width=600> </img></div>
|
||||
|
||||
最后一棵 不是平衡二叉树,因为它的左右两个子树的高度差的绝对值超过了1。
|
||||
|
||||
**C++中map、set、multimap,multiset的底层实现都是平衡二叉搜索树**,所以map、set的增删操作时间时间复杂度是logn,注意我这里没有说unordered_map、unordered_set,unordered_map、unordered_map底层实现是哈希表。
|
||||
|
||||
**所以大家使用自己熟悉的编程语言写算法,一定要知道常用的容器底层都是如何实现的,最基本的就是map、set等等,否则自己写的代码,自己对其性能分析都分析不清楚!**
|
||||
|
||||
|
||||
## 二叉树的存储方式
|
||||
|
||||
**二叉树可以链式存储,也可以顺序存储。**
|
||||
|
||||
那么链式存储方式就用指针, 顺序存储的方式就是用数组。
|
||||
|
||||
顾名思义就是顺序存储的元素在内存是连续分布的,而链式存储则是通过指针把分布在散落在各个地址的节点串联一起。
|
||||
|
||||
链式存储如图:
|
||||
|
||||
<img src='https://img-blog.csdnimg.cn/2020092019554618.png' width=600> </img></div>
|
||||
|
||||
链式存储是大家很熟悉的一种方式,那么我们来看看如何顺序存储呢?
|
||||
|
||||
其实就是用数组来存储二叉树,顺序存储的方式如图:
|
||||
|
||||
<img src='https://img-blog.csdnimg.cn/20200920200429452.png' width=600> </img></div>
|
||||
|
||||
用数组来存储二叉树如何遍历的呢?
|
||||
|
||||
**如果父节点的数组下标是 i,那么它的左孩子就是 i * 2 + 1,右孩子就是 i * 2 + 2。**
|
||||
|
||||
但是用链式表示的二叉树,更有利于我们理解,所以一般我们都是用链式存储二叉树。
|
||||
|
||||
**所以大家要了解,用数组依然可以表示二叉树。**
|
||||
|
||||
## 二叉树的遍历方式
|
||||
|
||||
关于二叉树的遍历方式,要知道二叉树遍历的基本方式都有哪些。
|
||||
|
||||
一些同学用做了很多二叉树的题目了,可能知道前序、中序、后序遍历,可能知道层序遍历,但是却没有框架。
|
||||
|
||||
我这里把二叉树的几种遍历方式列出来,大家就可以一一串起来了。
|
||||
|
||||
二叉树主要有两种遍历方式:
|
||||
1. 深度优先遍历:先往深走,遇到叶子节点再往回走。
|
||||
2. 广度优先遍历:一层一层的去遍历。
|
||||
|
||||
**这两种遍历是图论中最基本的两种遍历方式**,后面在介绍图论的时候 还会介绍到。
|
||||
|
||||
那么从深度优先遍历和广度优先遍历进一步拓展,才有如下遍历方式:
|
||||
|
||||
* 深度优先遍历
|
||||
* 前序遍历(递归法,迭代法)
|
||||
* 中序遍历(递归法,迭代法)
|
||||
* 后序遍历(递归法,迭代法)
|
||||
* 广度优先遍历
|
||||
* 层次遍历(迭代法)
|
||||
|
||||
|
||||
在深度优先遍历中:有三个顺序,前序、中序、后序遍历, 有同学总分不清这三个顺序,经常搞混,我这里教大家一个技巧。
|
||||
|
||||
**这里前、中、后,其实指的就是中间节点的遍历顺序**,只要大家记住 前序、中序、后序指的就是中间节点的位置就可以了。
|
||||
|
||||
看如下中间节点的顺序,就可以发现,中间节点的顺序就是所谓的遍历方式
|
||||
|
||||
* 前序遍历:中左右
|
||||
* 中序遍历:左中右
|
||||
* 后序遍历:左右中
|
||||
|
||||
大家可以对着如下图,看看自己理解的前后中序有没有问题。
|
||||
|
||||
<img src='https://img-blog.csdnimg.cn/20200806191109896.png' width=600> </img></div>
|
||||
|
||||
最后再说一说二叉树中深度优先和广度优先遍历实现方式,我们做二叉树相关题目,经常会使用递归的方式来实现深度优先遍历,也就是实现前序、中序、后序遍历,使用递归是比较方便的。
|
||||
|
||||
**之前我们讲栈与队列的时候,就说过栈其实就是递归的一种是实现结构**,也就说前序、中序、后序遍历的逻辑其实都是可以借助栈使用非递归的方式来实现的。
|
||||
|
||||
而广度优先遍历的实现一般使用队列来实现,这也是队列先进先出的特点所决定的,因为需要先进先出的结构,才能一层一层的来遍历二叉树。
|
||||
|
||||
**这里其实我们又了解了栈与队列的一个应用场景了。**
|
||||
|
||||
具体的实现我们后面都会讲的,这里大家先要清楚这些理论基础。
|
||||
|
||||
## 二叉树的定义
|
||||
|
||||
刚刚我们说过了二叉树有两种存储方式顺序存储,和链式存储,顺序存储就是用数组来存,这个定义没啥可说的,我们来看看链式存储的二叉树节点的定义方式。
|
||||
|
||||
|
||||
C++代码如下:
|
||||
|
||||
```
|
||||
struct TreeNode {
|
||||
int val;
|
||||
TreeNode *left;
|
||||
TreeNode *right;
|
||||
TreeNode(int x) : val(x), left(NULL), right(NULL) {}
|
||||
};
|
||||
```
|
||||
|
||||
大家会发现二叉树的定义 和链表是差不多的,相对于链表 ,二叉树的节点里多了一个指针, 有两个指针,指向左右孩子.
|
||||
|
||||
这里要提醒大家要注意二叉树节点定义的书写方式。
|
||||
|
||||
**在现场面试的时候 面试官可能要求手写代码,所以数据结构的定义以及简单逻辑的代码一定要锻炼白纸写出来。**
|
||||
|
||||
因为我们在刷leetcode的时候,节点的定义默认都定义好了,真到面试的时候,需要自己写节点定义的时候,有时候会一脸懵逼!
|
||||
|
||||
## 总结
|
||||
|
||||
二叉树是一种基础数据结构,在算法面试中都是常客,也是众多数据结构的基石。
|
||||
|
||||
本篇我们介绍了二叉树的种类、存储方式、遍历方式以及定义,比较全面的介绍了二叉树各个方面的重点,帮助大家扫一遍基础。
|
||||
|
||||
**说道二叉树,就不得不说递归,很多同学对递归都是又熟悉又陌生,递归的代码一般很简短,但每次都是一看就会,一写就废。**
|
||||
|
||||
|
||||
|
||||
## 其他语言版本
|
||||
|
||||
|
||||
Java:
|
||||
|
||||
|
||||
Python:
|
||||
|
||||
|
||||
Go:
|
||||
|
||||
|
||||
|
||||
|
||||
-----------------------
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
@ -239,7 +239,78 @@ Java:
|
||||
|
||||
```
|
||||
Python:
|
||||
> 迭代法前序遍历
|
||||
|
||||
```python
|
||||
class Solution:
|
||||
def preorderTraversal(self, root: TreeNode) -> List[int]:
|
||||
result = []
|
||||
st= []
|
||||
if root:
|
||||
st.append(root)
|
||||
while st:
|
||||
node = st.pop()
|
||||
if node != None:
|
||||
if node.right: #右
|
||||
st.append(node.right)
|
||||
if node.left: #左
|
||||
st.append(node.left)
|
||||
st.append(node) #中
|
||||
st.append(None)
|
||||
else:
|
||||
node = st.pop()
|
||||
result.append(node.val)
|
||||
return result
|
||||
```
|
||||
|
||||
> 迭代法中序遍历
|
||||
```python
|
||||
class Solution:
|
||||
def inorderTraversal(self, root: TreeNode) -> List[int]:
|
||||
result = []
|
||||
st = []
|
||||
if root:
|
||||
st.append(root)
|
||||
while st:
|
||||
node = st.pop()
|
||||
if node != None:
|
||||
if node.right: #添加右节点(空节点不入栈)
|
||||
st.append(node.right)
|
||||
|
||||
st.append(node) #添加中节点
|
||||
st.append(None) #中节点访问过,但是还没有处理,加入空节点做为标记。
|
||||
|
||||
if node.left: #添加左节点(空节点不入栈)
|
||||
st.append(node.left)
|
||||
else: #只有遇到空节点的时候,才将下一个节点放进结果集
|
||||
node = st.pop() #重新取出栈中元素
|
||||
result.append(node.val) #加入到结果集
|
||||
return result
|
||||
```
|
||||
|
||||
> 迭代法后序遍历
|
||||
```python
|
||||
class Solution:
|
||||
def postorderTraversal(self, root: TreeNode) -> List[int]:
|
||||
result = []
|
||||
st = []
|
||||
if root:
|
||||
st.append(root)
|
||||
while st:
|
||||
node = st.pop()
|
||||
if node != None:
|
||||
st.append(node) #中
|
||||
st.append(None)
|
||||
|
||||
if node.right: #右
|
||||
st.append(node.right)
|
||||
if node.left: #左
|
||||
st.append(node.left)
|
||||
else:
|
||||
node = st.pop()
|
||||
result.append(node.val)
|
||||
return result
|
||||
```
|
||||
|
||||
Go:
|
||||
> 前序遍历统一迭代法
|
||||
@ -374,6 +445,86 @@ func postorderTraversal(root *TreeNode) []int {
|
||||
}
|
||||
```
|
||||
|
||||
javaScript:
|
||||
|
||||
> 前序遍历统一迭代法
|
||||
|
||||
```js
|
||||
|
||||
// 前序遍历:中左右
|
||||
// 压栈顺序:右左中
|
||||
|
||||
var preorderTraversal = function(root, res = []) {
|
||||
const stack = [];
|
||||
if (root) stack.push(root);
|
||||
while(stack.length) {
|
||||
const node = stack.pop();
|
||||
if(!node) {
|
||||
res.push(stack.pop().val);
|
||||
continue;
|
||||
}
|
||||
if (node.right) stack.push(node.right); // 右
|
||||
if (node.left) stack.push(node.left); // 左
|
||||
stack.push(node); // 中
|
||||
stack.push(null);
|
||||
};
|
||||
return res;
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
> 中序遍历统一迭代法
|
||||
|
||||
```js
|
||||
|
||||
// 中序遍历:左中右
|
||||
// 压栈顺序:右中左
|
||||
|
||||
var inorderTraversal = function(root, res = []) {
|
||||
const stack = [];
|
||||
if (root) stack.push(root);
|
||||
while(stack.length) {
|
||||
const node = stack.pop();
|
||||
if(!node) {
|
||||
res.push(stack.pop().val);
|
||||
continue;
|
||||
}
|
||||
if (node.right) stack.push(node.right); // 右
|
||||
stack.push(node); // 中
|
||||
stack.push(null);
|
||||
if (node.left) stack.push(node.left); // 左
|
||||
};
|
||||
return res;
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
> 后序遍历统一迭代法
|
||||
|
||||
```js
|
||||
|
||||
// 后续遍历:左右中
|
||||
// 压栈顺序:中右左
|
||||
|
||||
var postorderTraversal = function(root, res = []) {
|
||||
const stack = [];
|
||||
if (root) stack.push(root);
|
||||
while(stack.length) {
|
||||
const node = stack.pop();
|
||||
if(!node) {
|
||||
res.push(stack.pop().val);
|
||||
continue;
|
||||
}
|
||||
stack.push(node); // 中
|
||||
stack.push(null);
|
||||
if (node.right) stack.push(node.right); // 右
|
||||
if (node.left) stack.push(node.left); // 左
|
||||
};
|
||||
return res;
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
-----------------------
|
||||
|
@ -1,11 +1,12 @@
|
||||
|
||||
|
||||
<p align="center">
|
||||
<a href="https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ"><img src="https://img.shields.io/badge/知识星球-代码随想录-blue" alt=""></a>
|
||||
<a href="https://mp.weixin.qq.com/s/RsdcQ9umo09R6cfnwXZlrQ"><img src="https://img.shields.io/badge/PDF下载-代码随想录-blueviolet" alt=""></a>
|
||||
<a href="https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw"><img src="https://img.shields.io/badge/刷题-微信群-green" alt=""></a>
|
||||
<a href="https://img-blog.csdnimg.cn/20201210231711160.png"><img src="https://img.shields.io/badge/公众号-代码随想录-brightgreen" alt=""></a>
|
||||
<a href="https://space.bilibili.com/525438321"><img src="https://img.shields.io/badge/B站-代码随想录-orange" alt=""></a>
|
||||
<a href="https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ"><img src="https://img.shields.io/badge/知识星球-代码随想录-blue" alt=""></a>
|
||||
</p>
|
||||
<p align="center"><strong>欢迎大家<a href="https://mp.weixin.qq.com/s/tqCxrMEU-ajQumL1i8im9A">参与本项目</a>,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!</strong></p>
|
||||
|
||||
|
||||
|
||||
大型互联网企业一般通过几轮技术面试来考察大家的各项能力,一般流程如下:
|
||||
|
||||
@ -212,11 +213,12 @@ leetcode是专门针对算法练习的题库,leetcode现在也推出了中文
|
||||
大家加油!
|
||||
|
||||
|
||||
------------------------
|
||||
|
||||
* 微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站:[代码随想录](https://space.bilibili.com/525438321)
|
||||
|
||||
|
||||
|
||||
-----------------------
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
|
||||
|
||||
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
|
@ -1,11 +1,12 @@
|
||||
|
||||
|
||||
<p align="center">
|
||||
<a href="https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ"><img src="https://img.shields.io/badge/知识星球-代码随想录-blue" alt=""></a>
|
||||
<a href="https://mp.weixin.qq.com/s/RsdcQ9umo09R6cfnwXZlrQ"><img src="https://img.shields.io/badge/PDF下载-代码随想录-blueviolet" alt=""></a>
|
||||
<a href="https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw"><img src="https://img.shields.io/badge/刷题-微信群-green" alt=""></a>
|
||||
<a href="https://img-blog.csdnimg.cn/20201210231711160.png"><img src="https://img.shields.io/badge/公众号-代码随想录-brightgreen" alt=""></a>
|
||||
<a href="https://space.bilibili.com/525438321"><img src="https://img.shields.io/badge/B站-代码随想录-orange" alt=""></a>
|
||||
<a href="https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ"><img src="https://img.shields.io/badge/知识星球-代码随想录-blue" alt=""></a>
|
||||
</p>
|
||||
<p align="center"><strong>欢迎大家<a href="https://mp.weixin.qq.com/s/tqCxrMEU-ajQumL1i8im9A">参与本项目</a>,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!</strong></p>
|
||||
|
||||
|
||||
|
||||
一些同学可能对计算机运行的速度还没有概念,就是感觉计算机运行速度应该会很快,那么在leetcode上做算法题目的时候为什么会超时呢?
|
||||
|
||||
@ -219,11 +220,12 @@ int main() {
|
||||
就酱,如果感觉「代码随想录」很干货,就帮忙宣传一波吧,很多录友发现这里之后都感觉相见恨晚!
|
||||
|
||||
|
||||
------------------------
|
||||
|
||||
* 微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站:[代码随想录](https://space.bilibili.com/525438321)
|
||||
|
||||
|
||||
|
||||
-----------------------
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
|
||||
|
||||
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
|
@ -1,10 +1,11 @@
|
||||
|
||||
<p align="center">
|
||||
<a href="https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ"><img src="https://img.shields.io/badge/知识星球-代码随想录-blue" alt=""></a>
|
||||
<a href="https://mp.weixin.qq.com/s/RsdcQ9umo09R6cfnwXZlrQ"><img src="https://img.shields.io/badge/PDF下载-代码随想录-blueviolet" alt=""></a>
|
||||
<a href="https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw"><img src="https://img.shields.io/badge/刷题-微信群-green" alt=""></a>
|
||||
<a href="https://img-blog.csdnimg.cn/20201210231711160.png"><img src="https://img.shields.io/badge/公众号-代码随想录-brightgreen" alt=""></a>
|
||||
<a href="https://space.bilibili.com/525438321"><img src="https://img.shields.io/badge/B站-代码随想录-orange" alt=""></a>
|
||||
<a href="https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ"><img src="https://img.shields.io/badge/知识星球-代码随想录-blue" alt=""></a>
|
||||
</p>
|
||||
<p align="center"><strong>欢迎大家<a href="https://mp.weixin.qq.com/s/tqCxrMEU-ajQumL1i8im9A">参与本项目</a>,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!</strong></p>
|
||||
|
||||
|
||||
# 上海互联网公司总结
|
||||
|
||||
@ -124,11 +125,12 @@
|
||||
相对于北京和上海,深圳互联网公司断层很明显,腾讯一家独大,二线三线垂直行业的公司很少,所以说深圳腾讯的员工流动性相对是较低的,因为基本没得选。
|
||||
|
||||
|
||||
------------------------
|
||||
|
||||
* 微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站:[代码随想录](https://space.bilibili.com/525438321)
|
||||
|
||||
|
||||
|
||||
-----------------------
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
|
||||
|
||||
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
|
@ -1,12 +1,12 @@
|
||||
|
||||
<p align="center">
|
||||
<a href="https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ"><img src="https://img.shields.io/badge/知识星球-代码随想录-blue" alt=""></a>
|
||||
<a href="https://mp.weixin.qq.com/s/RsdcQ9umo09R6cfnwXZlrQ"><img src="https://img.shields.io/badge/PDF下载-代码随想录-blueviolet" alt=""></a>
|
||||
<a href="https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw"><img src="https://img.shields.io/badge/刷题-微信群-green" alt=""></a>
|
||||
<a href="https://img-blog.csdnimg.cn/20201210231711160.png"><img src="https://img.shields.io/badge/公众号-代码随想录-brightgreen" alt=""></a>
|
||||
<a href="https://space.bilibili.com/525438321"><img src="https://img.shields.io/badge/B站-代码随想录-orange" alt=""></a>
|
||||
<a href="https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ"><img src="https://img.shields.io/badge/知识星球-代码随想录-blue" alt=""></a>
|
||||
</p>
|
||||
<p align="center"><strong>欢迎大家<a href="https://mp.weixin.qq.com/s/tqCxrMEU-ajQumL1i8im9A">参与本项目</a>,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!</strong></p>
|
||||
|
||||
|
||||
--------------------------
|
||||
现在很多企业都在牛客上进行面试,**很多录友和我反馈说搞不懂牛客上输入代码的ACM模式**。
|
||||
|
||||
什么是ACM输入模式呢? 就是自己构造输入数据格式,把要需要处理的容器填充好,OJ不会给你任何代码,包括include哪些函数都要自己写,最后也要自己控制返回数据的格式。
|
||||
@ -115,10 +115,11 @@ int main() {
|
||||
如果大家有精力的话,也可以去POJ上去刷刷题,POJ是ACM选手首选OJ,输入模式也是ACM模式。
|
||||
|
||||
|
||||
------------------------
|
||||
|
||||
* 微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站:[代码随想录](https://space.bilibili.com/525438321)
|
||||
|
||||
|
||||
-----------------------
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
|
||||
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
|
@ -1,13 +1,13 @@
|
||||
|
||||
|
||||
<p align="center">
|
||||
<a href="https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ"><img src="https://img.shields.io/badge/知识星球-代码随想录-blue" alt=""></a>
|
||||
<a href="https://mp.weixin.qq.com/s/RsdcQ9umo09R6cfnwXZlrQ"><img src="https://img.shields.io/badge/PDF下载-代码随想录-blueviolet" alt=""></a>
|
||||
<a href="https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw"><img src="https://img.shields.io/badge/刷题-微信群-green" alt=""></a>
|
||||
<a href="https://img-blog.csdnimg.cn/20201210231711160.png"><img src="https://img.shields.io/badge/公众号-代码随想录-brightgreen" alt=""></a>
|
||||
<a href="https://space.bilibili.com/525438321"><img src="https://img.shields.io/badge/B站-代码随想录-orange" alt=""></a>
|
||||
<a href="https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ"><img src="https://img.shields.io/badge/知识星球-代码随想录-blue" alt=""></a>
|
||||
</p>
|
||||
<p align="center"><strong>欢迎大家<a href="https://mp.weixin.qq.com/s/tqCxrMEU-ajQumL1i8im9A">参与本项目</a>,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!</strong></p>
|
||||
|
||||
|
||||
|
||||
--------------------------
|
||||
|
||||
# 看了这么多代码,谈一谈代码风格!
|
||||
|
||||
@ -142,10 +142,11 @@ Google规范是 大括号和 控制语句保持同一行的,我个人也很认
|
||||
就酱,以后我还会陆续分享,关于代码,求职,学习工作之类的内容。
|
||||
|
||||
|
||||
------------------------
|
||||
|
||||
* 微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站:[代码随想录](https://space.bilibili.com/525438321)
|
||||
|
||||
|
||||
-----------------------
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
|
||||
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
|
@ -1,12 +1,12 @@
|
||||
|
||||
<p align="center">
|
||||
<a href="https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ"><img src="https://img.shields.io/badge/知识星球-代码随想录-blue" alt=""></a>
|
||||
<a href="https://mp.weixin.qq.com/s/RsdcQ9umo09R6cfnwXZlrQ"><img src="https://img.shields.io/badge/PDF下载-代码随想录-blueviolet" alt=""></a>
|
||||
<a href="https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw"><img src="https://img.shields.io/badge/刷题-微信群-green" alt=""></a>
|
||||
<a href="https://img-blog.csdnimg.cn/20201210231711160.png"><img src="https://img.shields.io/badge/公众号-代码随想录-brightgreen" alt=""></a>
|
||||
<a href="https://space.bilibili.com/525438321"><img src="https://img.shields.io/badge/B站-代码随想录-orange" alt=""></a>
|
||||
<a href="https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ"><img src="https://img.shields.io/badge/知识星球-代码随想录-blue" alt=""></a>
|
||||
</p>
|
||||
<p align="center"><strong>欢迎大家<a href="https://mp.weixin.qq.com/s/tqCxrMEU-ajQumL1i8im9A">参与本项目</a>,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!</strong></p>
|
||||
|
||||
|
||||
--------------------------
|
||||
|
||||
Carl大胆断言:这可能是你见过对时间复杂度分析最通透的一篇文章了。
|
||||
|
||||
@ -117,7 +117,7 @@ O(2 * n^2 + 10 * n + 1000) < O(3 * n^2),所以说最后省略掉常数项系
|
||||

|
||||
|
||||
|
||||
假如有两个算法的时间复杂度,分别是log以2为底n的对数和log以10为底n的对数,那么这里如果还记得高中数学的话,应该不能理解`以2为底n的对数 = 以2为底10的对数 * 以10为底n的对数`。
|
||||
假如有两个算法的时间复杂度,分别是log以2为底n的对数和log以10为底n的对数,那么这里如果还记得高中数学的话,应该不难理解`以2为底n的对数 = 以2为底10的对数 * 以10为底n的对数`。
|
||||
|
||||
而以2为底10的对数是一个常数,在上文已经讲述了我们计算时间复杂度是忽略常数项系数的。
|
||||
|
||||
@ -165,10 +165,11 @@ O(2 * n^2 + 10 * n + 1000) < O(3 * n^2),所以说最后省略掉常数项系
|
||||
|
||||
|
||||
|
||||
------------------------
|
||||
|
||||
* 微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站:[代码随想录](https://space.bilibili.com/525438321)
|
||||
|
||||
|
||||
-----------------------
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
|
||||
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
|
@ -1,11 +1,12 @@
|
||||
|
||||
|
||||
<p align="center">
|
||||
<a href="https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ"><img src="https://img.shields.io/badge/知识星球-代码随想录-blue" alt=""></a>
|
||||
<a href="https://mp.weixin.qq.com/s/RsdcQ9umo09R6cfnwXZlrQ"><img src="https://img.shields.io/badge/PDF下载-代码随想录-blueviolet" alt=""></a>
|
||||
<a href="https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw"><img src="https://img.shields.io/badge/刷题-微信群-green" alt=""></a>
|
||||
<a href="https://img-blog.csdnimg.cn/20201210231711160.png"><img src="https://img.shields.io/badge/公众号-代码随想录-brightgreen" alt=""></a>
|
||||
<a href="https://space.bilibili.com/525438321"><img src="https://img.shields.io/badge/B站-代码随想录-orange" alt=""></a>
|
||||
<a href="https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ"><img src="https://img.shields.io/badge/知识星球-代码随想录-blue" alt=""></a>
|
||||
</p>
|
||||
<p align="center"><strong>欢迎大家<a href="https://mp.weixin.qq.com/s/tqCxrMEU-ajQumL1i8im9A">参与本项目</a>,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!</strong></p>
|
||||
|
||||
|
||||
|
||||
# 空间复杂度分析
|
||||
|
||||
@ -68,10 +69,11 @@ for (int i = 0; i < n; i++) {
|
||||
|
||||
至于如何求递归的空间复杂度,我会在专门写一篇文章来介绍的,敬请期待!
|
||||
|
||||
------------------------
|
||||
|
||||
* 微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站:[代码随想录](https://space.bilibili.com/525438321)
|
||||
|
||||
|
||||
-----------------------
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
|
||||
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
|
@ -1,12 +1,13 @@
|
||||
|
||||
|
||||
|
||||
<p align="center">
|
||||
<a href="https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ"><img src="https://img.shields.io/badge/知识星球-代码随想录-blue" alt=""></a>
|
||||
<a href="https://mp.weixin.qq.com/s/RsdcQ9umo09R6cfnwXZlrQ"><img src="https://img.shields.io/badge/PDF下载-代码随想录-blueviolet" alt=""></a>
|
||||
<a href="https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw"><img src="https://img.shields.io/badge/刷题-微信群-green" alt=""></a>
|
||||
<a href="https://img-blog.csdnimg.cn/20201210231711160.png"><img src="https://img.shields.io/badge/公众号-代码随想录-brightgreen" alt=""></a>
|
||||
<a href="https://space.bilibili.com/525438321"><img src="https://img.shields.io/badge/B站-代码随想录-orange" alt=""></a>
|
||||
<a href="https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ"><img src="https://img.shields.io/badge/知识星球-代码随想录-blue" alt=""></a>
|
||||
</p>
|
||||
<p align="center"><strong>欢迎大家<a href="https://mp.weixin.qq.com/s/tqCxrMEU-ajQumL1i8im9A">参与本项目</a>,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!</strong></p>
|
||||
|
||||
|
||||
|
||||
|
||||
理解代码的内存消耗,最关键是要知道自己所用编程语言的内存管理。
|
||||
|
||||
@ -145,10 +146,11 @@ char型的数据和int型的数据挨在一起,该int数据从地址1开始,
|
||||
|
||||
之后也可以有意识的去学习自己所用的编程语言是如何管理内存的,这些也是程序员的内功。
|
||||
|
||||
------------------------
|
||||
|
||||
* 微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站:[代码随想录](https://space.bilibili.com/525438321)
|
||||
|
||||
|
||||
-----------------------
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
|
||||
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
|
@ -1,10 +1,11 @@
|
||||
|
||||
<p align="center">
|
||||
<a href="https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ"><img src="https://img.shields.io/badge/知识星球-代码随想录-blue" alt=""></a>
|
||||
<a href="https://mp.weixin.qq.com/s/RsdcQ9umo09R6cfnwXZlrQ"><img src="https://img.shields.io/badge/PDF下载-代码随想录-blueviolet" alt=""></a>
|
||||
<a href="https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw"><img src="https://img.shields.io/badge/刷题-微信群-green" alt=""></a>
|
||||
<a href="https://img-blog.csdnimg.cn/20201210231711160.png"><img src="https://img.shields.io/badge/公众号-代码随想录-brightgreen" alt=""></a>
|
||||
<a href="https://space.bilibili.com/525438321"><img src="https://img.shields.io/badge/B站-代码随想录-orange" alt=""></a>
|
||||
<a href="https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ"><img src="https://img.shields.io/badge/知识星球-代码随想录-blue" alt=""></a>
|
||||
</p>
|
||||
<p align="center"><strong>欢迎大家<a href="https://mp.weixin.qq.com/s/tqCxrMEU-ajQumL1i8im9A">参与本项目</a>,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!</strong></p>
|
||||
|
||||
|
||||
很多录友都问过我一个问题,就是力扣上的代码如何在本地编译运行?
|
||||
|
||||
@ -62,10 +63,11 @@ int main() {
|
||||
|
||||
|
||||
|
||||
------------------------
|
||||
|
||||
* 微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站:[代码随想录](https://space.bilibili.com/525438321)
|
||||
|
||||
|
||||
-----------------------
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
|
||||
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
|
@ -1,11 +1,12 @@
|
||||
|
||||
|
||||
<p align="center">
|
||||
<a href="https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ"><img src="https://img.shields.io/badge/知识星球-代码随想录-blue" alt=""></a>
|
||||
<a href="https://mp.weixin.qq.com/s/RsdcQ9umo09R6cfnwXZlrQ"><img src="https://img.shields.io/badge/PDF下载-代码随想录-blueviolet" alt=""></a>
|
||||
<a href="https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw"><img src="https://img.shields.io/badge/刷题-微信群-green" alt=""></a>
|
||||
<a href="https://img-blog.csdnimg.cn/20201210231711160.png"><img src="https://img.shields.io/badge/公众号-代码随想录-brightgreen" alt=""></a>
|
||||
<a href="https://space.bilibili.com/525438321"><img src="https://img.shields.io/badge/B站-代码随想录-orange" alt=""></a>
|
||||
<a href="https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ"><img src="https://img.shields.io/badge/知识星球-代码随想录-blue" alt=""></a>
|
||||
</p>
|
||||
<p align="center"><strong>欢迎大家<a href="https://mp.weixin.qq.com/s/tqCxrMEU-ajQumL1i8im9A">参与本项目</a>,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!</strong></p>
|
||||
|
||||
|
||||
|
||||
# 北京互联网公司总结
|
||||
|
||||
@ -110,11 +111,12 @@
|
||||
|
||||
就酱,我也会陆续整理其他城市的互联网公司,希望对大家有所帮助。
|
||||
|
||||
------------------------
|
||||
|
||||
* 微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站:[代码随想录](https://space.bilibili.com/525438321)
|
||||
|
||||
|
||||
|
||||
-----------------------
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
|
||||
|
||||
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
|
@ -1,10 +1,11 @@
|
||||
|
||||
<p align="center">
|
||||
<a href="https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ"><img src="https://img.shields.io/badge/知识星球-代码随想录-blue" alt=""></a>
|
||||
<a href="https://mp.weixin.qq.com/s/RsdcQ9umo09R6cfnwXZlrQ"><img src="https://img.shields.io/badge/PDF下载-代码随想录-blueviolet" alt=""></a>
|
||||
<a href="https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw"><img src="https://img.shields.io/badge/刷题-微信群-green" alt=""></a>
|
||||
<a href="https://img-blog.csdnimg.cn/20201210231711160.png"><img src="https://img.shields.io/badge/公众号-代码随想录-brightgreen" alt=""></a>
|
||||
<a href="https://space.bilibili.com/525438321"><img src="https://img.shields.io/badge/B站-代码随想录-orange" alt=""></a>
|
||||
<a href="https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ"><img src="https://img.shields.io/badge/知识星球-代码随想录-blue" alt=""></a>
|
||||
</p>
|
||||
<p align="center"><strong>欢迎大家<a href="https://mp.weixin.qq.com/s/tqCxrMEU-ajQumL1i8im9A">参与本项目</a>,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!</strong></p>
|
||||
|
||||
|
||||
# 广州互联网公司总结
|
||||
|
||||
@ -73,11 +74,12 @@
|
||||
|
||||
|
||||
|
||||
------------------------
|
||||
|
||||
* 微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站:[代码随想录](https://space.bilibili.com/525438321)
|
||||
|
||||
|
||||
|
||||
-----------------------
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
|
||||
|
||||
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
|
@ -1,11 +1,12 @@
|
||||
|
||||
|
||||
<p align="center">
|
||||
<a href="https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ"><img src="https://img.shields.io/badge/知识星球-代码随想录-blue" alt=""></a>
|
||||
<a href="https://mp.weixin.qq.com/s/RsdcQ9umo09R6cfnwXZlrQ"><img src="https://img.shields.io/badge/PDF下载-代码随想录-blueviolet" alt=""></a>
|
||||
<a href="https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw"><img src="https://img.shields.io/badge/刷题-微信群-green" alt=""></a>
|
||||
<a href="https://img-blog.csdnimg.cn/20201210231711160.png"><img src="https://img.shields.io/badge/公众号-代码随想录-brightgreen" alt=""></a>
|
||||
<a href="https://space.bilibili.com/525438321"><img src="https://img.shields.io/badge/B站-代码随想录-orange" alt=""></a>
|
||||
<a href="https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ"><img src="https://img.shields.io/badge/知识星球-代码随想录-blue" alt=""></a>
|
||||
</p>
|
||||
<p align="center"><strong>欢迎大家<a href="https://mp.weixin.qq.com/s/tqCxrMEU-ajQumL1i8im9A">参与本项目</a>,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!</strong></p>
|
||||
|
||||
|
||||
|
||||
|
||||
# 成都互联网公司总结
|
||||
@ -71,11 +72,12 @@
|
||||
|
||||
|
||||
|
||||
------------------------
|
||||
|
||||
* 微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站:[代码随想录](https://space.bilibili.com/525438321)
|
||||
|
||||
|
||||
|
||||
-----------------------
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
|
||||
|
||||
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
|
@ -1,11 +1,12 @@
|
||||
|
||||
|
||||
<p align="center">
|
||||
<a href="https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ"><img src="https://img.shields.io/badge/知识星球-代码随想录-blue" alt=""></a>
|
||||
<a href="https://mp.weixin.qq.com/s/RsdcQ9umo09R6cfnwXZlrQ"><img src="https://img.shields.io/badge/PDF下载-代码随想录-blueviolet" alt=""></a>
|
||||
<a href="https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw"><img src="https://img.shields.io/badge/刷题-微信群-green" alt=""></a>
|
||||
<a href="https://img-blog.csdnimg.cn/20201210231711160.png"><img src="https://img.shields.io/badge/公众号-代码随想录-brightgreen" alt=""></a>
|
||||
<a href="https://space.bilibili.com/525438321"><img src="https://img.shields.io/badge/B站-代码随想录-orange" alt=""></a>
|
||||
<a href="https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ"><img src="https://img.shields.io/badge/知识星球-代码随想录-blue" alt=""></a>
|
||||
</p>
|
||||
<p align="center"><strong>欢迎大家<a href="https://mp.weixin.qq.com/s/tqCxrMEU-ajQumL1i8im9A">参与本项目</a>,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!</strong></p>
|
||||
|
||||
|
||||
|
||||
# 杭州互联网公司总结
|
||||
|
||||
@ -82,11 +83,12 @@
|
||||
|
||||
|
||||
|
||||
------------------------
|
||||
|
||||
* 微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站:[代码随想录](https://space.bilibili.com/525438321)
|
||||
|
||||
|
||||
|
||||
-----------------------
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
|
||||
|
||||
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
|
@ -1,10 +1,11 @@
|
||||
|
||||
<p align="center">
|
||||
<a href="https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ"><img src="https://img.shields.io/badge/知识星球-代码随想录-blue" alt=""></a>
|
||||
<a href="https://mp.weixin.qq.com/s/RsdcQ9umo09R6cfnwXZlrQ"><img src="https://img.shields.io/badge/PDF下载-代码随想录-blueviolet" alt=""></a>
|
||||
<a href="https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw"><img src="https://img.shields.io/badge/刷题-微信群-green" alt=""></a>
|
||||
<a href="https://img-blog.csdnimg.cn/20201210231711160.png"><img src="https://img.shields.io/badge/公众号-代码随想录-brightgreen" alt=""></a>
|
||||
<a href="https://space.bilibili.com/525438321"><img src="https://img.shields.io/badge/B站-代码随想录-orange" alt=""></a>
|
||||
<a href="https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ"><img src="https://img.shields.io/badge/知识星球-代码随想录-blue" alt=""></a>
|
||||
</p>
|
||||
<p align="center"><strong>欢迎大家<a href="https://mp.weixin.qq.com/s/tqCxrMEU-ajQumL1i8im9A">参与本项目</a>,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!</strong></p>
|
||||
|
||||
|
||||
# 深圳互联网公司总结
|
||||
|
||||
@ -76,11 +77,12 @@
|
||||
* 广发证券,深交所
|
||||
* 珍爱网(珍爱网是国内知名的婚恋服务网站之一)
|
||||
|
||||
------------------------
|
||||
|
||||
* 微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站:[代码随想录](https://space.bilibili.com/525438321)
|
||||
|
||||
|
||||
|
||||
-----------------------
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
|
||||
|
||||
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
|
@ -1,12 +1,13 @@
|
||||
|
||||
|
||||
|
||||
<p align="center">
|
||||
<a href="https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ"><img src="https://img.shields.io/badge/知识星球-代码随想录-blue" alt=""></a>
|
||||
<a href="https://mp.weixin.qq.com/s/RsdcQ9umo09R6cfnwXZlrQ"><img src="https://img.shields.io/badge/PDF下载-代码随想录-blueviolet" alt=""></a>
|
||||
<a href="https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw"><img src="https://img.shields.io/badge/刷题-微信群-green" alt=""></a>
|
||||
<a href="https://img-blog.csdnimg.cn/20201210231711160.png"><img src="https://img.shields.io/badge/公众号-代码随想录-brightgreen" alt=""></a>
|
||||
<a href="https://space.bilibili.com/525438321"><img src="https://img.shields.io/badge/B站-代码随想录-orange" alt=""></a>
|
||||
<a href="https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ"><img src="https://img.shields.io/badge/知识星球-代码随想录-blue" alt=""></a>
|
||||
</p>
|
||||
<p align="center"><strong>欢迎大家<a href="https://mp.weixin.qq.com/s/tqCxrMEU-ajQumL1i8im9A">参与本项目</a>,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!</strong></p>
|
||||
|
||||
|
||||
|
||||
|
||||
# 程序员应该用什么用具来写文档?
|
||||
|
||||
@ -132,10 +133,11 @@ Markdown支持部分html,例如这样
|
||||
如果还没有掌握markdown的你还在等啥,赶紧使用markdown记录起来吧
|
||||
|
||||
|
||||
------------------------
|
||||
|
||||
* 微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站:[代码随想录](https://space.bilibili.com/525438321)
|
||||
|
||||
|
||||
-----------------------
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
|
||||
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
|
@ -1,12 +1,12 @@
|
||||
|
||||
<p align="center">
|
||||
<a href="https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ"><img src="https://img.shields.io/badge/知识星球-代码随想录-blue" alt=""></a>
|
||||
<a href="https://mp.weixin.qq.com/s/RsdcQ9umo09R6cfnwXZlrQ"><img src="https://img.shields.io/badge/PDF下载-代码随想录-blueviolet" alt=""></a>
|
||||
<a href="https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw"><img src="https://img.shields.io/badge/刷题-微信群-green" alt=""></a>
|
||||
<a href="https://img-blog.csdnimg.cn/20201210231711160.png"><img src="https://img.shields.io/badge/公众号-代码随想录-brightgreen" alt=""></a>
|
||||
<a href="https://space.bilibili.com/525438321"><img src="https://img.shields.io/badge/B站-代码随想录-orange" alt=""></a>
|
||||
<a href="https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ"><img src="https://img.shields.io/badge/知识星球-代码随想录-blue" alt=""></a>
|
||||
</p>
|
||||
<p align="center"><strong>欢迎大家<a href="https://mp.weixin.qq.com/s/tqCxrMEU-ajQumL1i8im9A">参与本项目</a>,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!</strong></p>
|
||||
|
||||
|
||||
--------------------------
|
||||
|
||||
# 程序员的简历应该这么写!!(附简历模板)
|
||||
|
||||
@ -131,9 +131,10 @@ Carl校招社招都拿过大厂的offer,同时也看过很多应聘者的简
|
||||
|
||||
就酱,「代码随想录」就是这么干货,Carl多年积累的简历技巧都毫不保留的写出来了,如果感觉对你有帮助,就宣传一波「代码随想录」吧,值得大家的关注!
|
||||
|
||||
------------------------
|
||||
|
||||
* 微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站:[代码随想录](https://space.bilibili.com/525438321)
|
||||
|
||||
-----------------------
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
|
@ -1,10 +1,11 @@
|
||||
|
||||
<p align="center">
|
||||
<a href="https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ"><img src="https://img.shields.io/badge/知识星球-代码随想录-blue" alt=""></a>
|
||||
<a href="https://mp.weixin.qq.com/s/RsdcQ9umo09R6cfnwXZlrQ"><img src="https://img.shields.io/badge/PDF下载-代码随想录-blueviolet" alt=""></a>
|
||||
<a href="https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw"><img src="https://img.shields.io/badge/刷题-微信群-green" alt=""></a>
|
||||
<a href="https://img-blog.csdnimg.cn/20201210231711160.png"><img src="https://img.shields.io/badge/公众号-代码随想录-brightgreen" alt=""></a>
|
||||
<a href="https://space.bilibili.com/525438321"><img src="https://img.shields.io/badge/B站-代码随想录-orange" alt=""></a>
|
||||
<a href="https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ"><img src="https://img.shields.io/badge/知识星球-代码随想录-blue" alt=""></a>
|
||||
</p>
|
||||
<p align="center"><strong>欢迎大家<a href="https://mp.weixin.qq.com/s/tqCxrMEU-ajQumL1i8im9A">参与本项目</a>,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!</strong></p>
|
||||
|
||||
|
||||
# 递归算法的时间与空间复杂度分析!
|
||||
|
||||
@ -263,11 +264,12 @@ int binary_search( int arr[], int l, int r, int x) {
|
||||
|
||||
|
||||
|
||||
------------------------
|
||||
|
||||
* 微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站:[代码随想录](https://space.bilibili.com/525438321)
|
||||
|
||||
|
||||
|
||||
-----------------------
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
|
||||
|
||||
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
|
@ -1,11 +1,12 @@
|
||||
|
||||
|
||||
<p align="center">
|
||||
<a href="https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ"><img src="https://img.shields.io/badge/知识星球-代码随想录-blue" alt=""></a>
|
||||
<a href="https://mp.weixin.qq.com/s/RsdcQ9umo09R6cfnwXZlrQ"><img src="https://img.shields.io/badge/PDF下载-代码随想录-blueviolet" alt=""></a>
|
||||
<a href="https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw"><img src="https://img.shields.io/badge/刷题-微信群-green" alt=""></a>
|
||||
<a href="https://img-blog.csdnimg.cn/20201210231711160.png"><img src="https://img.shields.io/badge/公众号-代码随想录-brightgreen" alt=""></a>
|
||||
<a href="https://space.bilibili.com/525438321"><img src="https://img.shields.io/badge/B站-代码随想录-orange" alt=""></a>
|
||||
<a href="https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ"><img src="https://img.shields.io/badge/知识星球-代码随想录-blue" alt=""></a>
|
||||
</p>
|
||||
<p align="center"><strong>欢迎大家<a href="https://mp.weixin.qq.com/s/tqCxrMEU-ajQumL1i8im9A">参与本项目</a>,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!</strong></p>
|
||||
|
||||
|
||||
|
||||
|
||||
# 通过一道面试题目,讲一讲递归算法的时间复杂度!
|
||||
@ -149,10 +150,11 @@ int function3(int x, int n) {
|
||||
如果认真读完本篇,相信大家对递归算法的有一个新的认识的,同一道题目,同样是递归,效率可是不一样的!
|
||||
|
||||
|
||||
------------------------
|
||||
|
||||
* 微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站:[代码随想录](https://space.bilibili.com/525438321)
|
||||
|
||||
|
||||
-----------------------
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
|
||||
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
|
@ -23,7 +23,7 @@ https://leetcode-cn.com/problems/zuo-xuan-zhuan-zi-fu-chuan-lcof/
|
||||
示例 2:
|
||||
输入: s = "lrloseumgh", k = 6
|
||||
输出: "umghlrlose"
|
||||
|
||||
|
||||
限制:
|
||||
1 <= k < s.length <= 10000
|
||||
|
||||
@ -119,9 +119,31 @@ class Solution {
|
||||
```
|
||||
Python:
|
||||
|
||||
|
||||
Go:
|
||||
|
||||
```go
|
||||
func reverseLeftWords(s string, n int) string {
|
||||
b := []byte(s)
|
||||
// 1. 反转前n个字符
|
||||
// 2. 反转第n到end字符
|
||||
// 3. 反转整个字符
|
||||
reverse(b, 0, n-1)
|
||||
reverse(b, n, len(b)-1)
|
||||
reverse(b, 0, len(b)-1)
|
||||
return string(b)
|
||||
}
|
||||
// 切片是引用传递
|
||||
func reverse(b []byte, left, right int){
|
||||
for left < right{
|
||||
b[left], b[right] = b[right],b[left]
|
||||
left++
|
||||
right--
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -129,4 +151,4 @@ Go:
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
140
problems/动态规划总结篇.md
Normal file
140
problems/动态规划总结篇.md
Normal file
@ -0,0 +1,140 @@
|
||||
|
||||
<p align="center">
|
||||
<a href="https://mp.weixin.qq.com/s/RsdcQ9umo09R6cfnwXZlrQ"><img src="https://img.shields.io/badge/PDF下载-代码随想录-blueviolet" alt=""></a>
|
||||
<a href="https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw"><img src="https://img.shields.io/badge/刷题-微信群-green" alt=""></a>
|
||||
<a href="https://space.bilibili.com/525438321"><img src="https://img.shields.io/badge/B站-代码随想录-orange" alt=""></a>
|
||||
<a href="https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ"><img src="https://img.shields.io/badge/知识星球-代码随想录-blue" alt=""></a>
|
||||
</p>
|
||||
<p align="center"><strong>欢迎大家<a href="https://mp.weixin.qq.com/s/tqCxrMEU-ajQumL1i8im9A">参与本项目</a>,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!</strong></p>
|
||||
|
||||
|
||||
如今动态规划已经讲解了42道经典题目,共50篇文章,是时候做一篇总结了。
|
||||
|
||||
关于动态规划,在专题第一篇[关于动态规划,你该了解这些!](https://mp.weixin.qq.com/s/ocZwfPlCWrJtVGACqFNAag)就说了动规五部曲,**而且强调了五部对解动规题目至关重要!**
|
||||
|
||||
这是Carl做过一百多道动规题目总结出来的经验结晶啊,如果大家跟着「代码随想哦」刷过动规专题,一定会对这动规五部曲的作用感受极其深刻。
|
||||
|
||||
动规五部曲分别为:
|
||||
|
||||
1. 确定dp数组(dp table)以及下标的含义
|
||||
2. 确定递推公式
|
||||
3. dp数组如何初始化
|
||||
4. 确定遍历顺序
|
||||
5. 举例推导dp数组
|
||||
|
||||
动规专题刚开始的时候,讲的题目比较简单,不少录友和我反应:这么简单的题目 讲的复杂了,不用那么多步骤分析,想出递推公式直接就AC这道题目了。
|
||||
|
||||
**Carl的观点一直都是 简单题是用来 巩固方法论的**。 简单题目是可以靠感觉,但后面稍稍难一点的题目,估计感觉就不好使了。
|
||||
|
||||
在动规专题讲解中,也充分体现出,这动规五部曲的重要性。
|
||||
|
||||
还有不少录友对动规的理解是:递推公式是才是最难最重要的,只要想出递归公式,其他都好办。
|
||||
|
||||
**其实这么想的同学基本对动规理解的不到位的**。
|
||||
|
||||
动规五部曲里,哪一部没想清楚,这道题目基本就做不出来,即使做出来了也没有想清楚,而是朦朦胧胧的就把题目过了。
|
||||
|
||||
* 如果想不清楚dp数组的具体含义,递归公式从何谈起,甚至初始化的时候就写错了。
|
||||
* 例如[动态规划:不同路径还不够,要有障碍!](https://mp.weixin.qq.com/s/lhqF0O4le9-wvalptOVOww) 在这道题目中,初始化才是重头戏
|
||||
* 如果看过背包系列,特别是完全背包,那么两层for循环先后顺序绝对可以搞懵很多人,反而递归公式是简单的。
|
||||
* 至于推导dp数组的重要性,动规专题里几乎每篇Carl都反复强调,当程序结果不对的时候,一定要自己推导公式,看看和程序打印的日志是否一样。
|
||||
|
||||
好啦,我们再一起回顾一下,动态规划专题中我们都讲了哪些内容。
|
||||
|
||||
## 动划基础
|
||||
|
||||
* [关于动态规划,你该了解这些!](https://mp.weixin.qq.com/s/ocZwfPlCWrJtVGACqFNAag)
|
||||
* [动态规划:斐波那契数](https://mp.weixin.qq.com/s/ko0zLJplF7n_4TysnPOa_w)
|
||||
* [动态规划:爬楼梯](https://mp.weixin.qq.com/s/Ohop0jApSII9xxOMiFhGIw)
|
||||
* [动态规划:使用最小花费爬楼梯](https://mp.weixin.qq.com/s/djZB9gkyLFAKcQcSvKDorA)
|
||||
* [动态规划:不同路径](https://mp.weixin.qq.com/s/MGgGIt4QCpFMROE9X9he_A)
|
||||
* [动态规划:不同路径还不够,要有障碍!](https://mp.weixin.qq.com/s/lhqF0O4le9-wvalptOVOww)
|
||||
* [动态规划:整数拆分,你要怎么拆?](https://mp.weixin.qq.com/s/cVbyHrsWH_Rfzlj-ESr01A)
|
||||
* [动态规划:不同的二叉搜索树](https://mp.weixin.qq.com/s/8VE8pDrGxTf8NEVYBDwONw)
|
||||
|
||||
## 背包问题系列
|
||||
|
||||
<img src='https://code-thinking.cdn.bcebos.com/pics/动态规划-背包问题总结.png' width=500 alt='背包问题大纲'> </img></div>
|
||||
|
||||
* [动态规划:关于01背包问题,你该了解这些!](https://mp.weixin.qq.com/s/FwIiPPmR18_AJO5eiidT6w)
|
||||
* [动态规划:关于01背包问题,你该了解这些!(滚动数组)](https://mp.weixin.qq.com/s/M4uHxNVKRKm5HPjkNZBnFA)
|
||||
* [动态规划:分割等和子集可以用01背包!](https://mp.weixin.qq.com/s/sYw3QtPPQ5HMZCJcT4EaLQ)
|
||||
* [动态规划:最后一块石头的重量 II](https://mp.weixin.qq.com/s/WbwAo3jaUaNJjvhHgq0BGg)
|
||||
* [动态规划:目标和!](https://mp.weixin.qq.com/s/2pWmaohX75gwxvBENS-NCw)
|
||||
* [动态规划:一和零!](https://mp.weixin.qq.com/s/x-u3Dsp76DlYqtCe0xEKJw)
|
||||
* [动态规划:关于完全背包,你该了解这些!](https://mp.weixin.qq.com/s/akwyxlJ4TLvKcw26KB9uJw)
|
||||
* [动态规划:给你一些零钱,你要怎么凑?](https://mp.weixin.qq.com/s/PlowDsI4WMBOzf3q80AksQ)
|
||||
* [动态规划:Carl称它为排列总和!](https://mp.weixin.qq.com/s/Iixw0nahJWQgbqVNk8k6gA)
|
||||
* [动态规划:以前我没得选,现在我选择再爬一次!](https://mp.weixin.qq.com/s/e_wacnELo-2PG76EjrUakA)
|
||||
* [动态规划: 给我个机会,我再兑换一次零钱](https://mp.weixin.qq.com/s/dyk-xNilHzNtVdPPLObSeQ)
|
||||
* [动态规划:一样的套路,再求一次完全平方数](https://mp.weixin.qq.com/s/VfJT78p7UGpDZsapKF_QJQ)
|
||||
* [动态规划:单词拆分](https://mp.weixin.qq.com/s/3Spx1B6MbIYjS8YkVbByzA)
|
||||
* [动态规划:关于多重背包,你该了解这些!](https://mp.weixin.qq.com/s/b-UUUmbvG7URWyCjQkiuuQ)
|
||||
* [听说背包问题很难? 这篇总结篇来拯救你了](https://mp.weixin.qq.com/s/ZOehl3U1mDiyOQjFG1wNJA)
|
||||
|
||||
## 打家劫舍系列
|
||||
|
||||
* [动态规划:开始打家劫舍!](https://mp.weixin.qq.com/s/UZ31WdLEEFmBegdgLkJ8Dw)
|
||||
* [动态规划:继续打家劫舍!](https://mp.weixin.qq.com/s/kKPx4HpH3RArbRcxAVHbeQ)
|
||||
* [动态规划:还要打家劫舍!](https://mp.weixin.qq.com/s/BOJ1lHsxbQxUZffXlgglEQ)
|
||||
|
||||
## 股票系列
|
||||
|
||||
<img src='https://code-thinking.cdn.bcebos.com/pics/股票问题总结.jpg' width=500 alt='股票问题总结'> </img></div>
|
||||
|
||||
* [动态规划:买卖股票的最佳时机](https://mp.weixin.qq.com/s/keWo5qYJY4zmHn3amfXdfQ)
|
||||
* [动态规划:本周我们都讲了这些(系列六)](https://mp.weixin.qq.com/s/GVu-6eF0iNkpVDKRXTPOTA)
|
||||
* [动态规划:买卖股票的最佳时机II](https://mp.weixin.qq.com/s/d4TRWFuhaY83HPa6t5ZL-w)
|
||||
* [动态规划:买卖股票的最佳时机III](https://mp.weixin.qq.com/s/Sbs157mlVDtAR0gbLpdKzg)
|
||||
* [动态规划:买卖股票的最佳时机IV](https://mp.weixin.qq.com/s/jtxZJWAo2y5sUsW647Z5cw)
|
||||
* [动态规划:最佳买卖股票时机含冷冻期](https://mp.weixin.qq.com/s/TczJGFAPnkjH9ET8kwH1OA)
|
||||
* [动态规划:本周我们都讲了这些(系列七)](https://mp.weixin.qq.com/s/vdzDlrEvhXWRzblTnOnzKg)
|
||||
* [动态规划:买卖股票的最佳时机含手续费](https://mp.weixin.qq.com/s/2Cd_uINjerZ25VHH0K2IBQ)
|
||||
* [动态规划:股票系列总结篇](https://mp.weixin.qq.com/s/sC5XyEtDQWkonKnbCvZhDw)
|
||||
|
||||
## 子序列系列
|
||||
|
||||
<img src='https://code-thinking.cdn.bcebos.com/pics/动态规划-子序列问题总结.jpg' width=500 alt=''> </img></div>
|
||||
|
||||
* [动态规划:最长递增子序列](https://mp.weixin.qq.com/s/f8nLO3JGfgriXep_gJQpqQ)
|
||||
* [动态规划:最长连续递增序列](https://mp.weixin.qq.com/s/c0Nn0TtjkTISVdqRsyMmyA)
|
||||
* [动态规划:最长重复子数组](https://mp.weixin.qq.com/s/U5WaWqBwdoxzQDotOdWqZg)
|
||||
* [动态规划:最长公共子序列](https://mp.weixin.qq.com/s/Qq0q4HaE4TyasCTj2WGFOg)
|
||||
* [动态规划:不相交的线](https://mp.weixin.qq.com/s/krfYzSYEO8jIoVfyHzR0rw)
|
||||
* [动态规划:最大子序和](https://mp.weixin.qq.com/s/2Xtyi2L4r8sM-BcxgUKmcA)
|
||||
* [动态规划:判断子序列](https://mp.weixin.qq.com/s/2pjT4B4fjfOx5iB6N6xyng)
|
||||
* [动态规划:不同的子序列](https://mp.weixin.qq.com/s/1SULY2XVSROtk_hsoVLu8A)
|
||||
* [动态规划:两个字符串的删除操作](https://mp.weixin.qq.com/s/a8BerpqSf76DCqkPDJrpYg)
|
||||
* [动态规划:编辑距离](https://mp.weixin.qq.com/s/8aG71XjSgZG6kZbiAdkJnQ)
|
||||
* [为了绝杀编辑距离,我做了三步铺垫,你都知道么?](https://mp.weixin.qq.com/s/kbs4kCUzg8gPFttF9H3Yyw)
|
||||
* [动态规划:回文子串](https://mp.weixin.qq.com/s/2WetyP6IYQ6VotegepVpEw)
|
||||
* [动态规划:最长回文子序列](https://mp.weixin.qq.com/s/jbd3p4QPm5Kh1s2smTzWag)
|
||||
|
||||
|
||||
## 动规结束语
|
||||
|
||||
关于动规,还有 树形DP(打家劫舍系列里有一道),数位DP,区间DP ,概率型DP,博弈型DP,状态压缩dp等等等,这些我就不去做讲解了,面试中出现的概率非常低。
|
||||
|
||||
能把本篇中列举的题目都研究通透的话,你的动规水平就已经非常高了。 对付面试已经足够!
|
||||
|
||||
这已经是全网对动规最深刻的讲解系列了。
|
||||
|
||||
**其实大家去网上搜一搜也可以发现,能把动态规划讲清楚的资料挺少的,因为动规确实很难!要给别人讲清楚更难!**
|
||||
|
||||
《剑指offer》上 动规的题目很少,经典的算法书籍《算法4》 没有讲 动规,而《算法导论》讲的动规基本属于劝退级别的。
|
||||
|
||||
讲清楚一道题容易,讲清楚两道题也容易,但把整个动态规划的各个分支讲清楚,每道题目讲通透,并用一套方法论把整个动规贯彻始终就非常难了。
|
||||
|
||||
所以Carl花费的这么大精力,把自己对动规算法理解 一五一十的全部分享给了录友们,帮助大家少走弯路!
|
||||
|
||||
**至于动态规划PDF,即将在公众号「代码随想录」全网首发!**
|
||||
|
||||
最后感谢录友们的一路支持,Carl才有继续更下去的动力[玫瑰],[撒花]
|
||||
|
||||
|
||||
-----------------------
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
|
@ -55,7 +55,7 @@
|
||||
|
||||
dp[j]为 容量为j的背包所背的最大价值,那么如何推导dp[j]呢?
|
||||
|
||||
dp[j]可以通过dp[j - weight[j]]推导出来,dp[j - weight[i]]表示容量为j - weight[i]的背包所背的最大价值。
|
||||
dp[j]可以通过dp[j - weight[i]]推导出来,dp[j - weight[i]]表示容量为j - weight[i]的背包所背的最大价值。
|
||||
|
||||
dp[j - weight[i]] + value[i] 表示 容量为 j - 物品i重量 的背包 加上 物品i的价值。(也就是容量为j的背包,放入物品i了之后的价值即:dp[j])
|
||||
|
||||
|
@ -1,231 +0,0 @@
|
||||
<p align="center">
|
||||
<a href="https://mp.weixin.qq.com/s/RsdcQ9umo09R6cfnwXZlrQ"><img src="https://img.shields.io/badge/PDF下载-代码随想录-blueviolet" alt=""></a>
|
||||
<a href="https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw"><img src="https://img.shields.io/badge/刷题-微信群-green" alt=""></a>
|
||||
<a href="https://space.bilibili.com/525438321"><img src="https://img.shields.io/badge/B站-代码随想录-orange" alt=""></a>
|
||||
<a href="https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ"><img src="https://img.shields.io/badge/知识星球-代码随想录-blue" alt=""></a>
|
||||
</p>
|
||||
<p align="center"><strong>欢迎大家<a href="https://mp.weixin.qq.com/s/tqCxrMEU-ajQumL1i8im9A">参与本项目</a>,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!</strong></p>
|
||||
|
||||
# 动态规划:关于01背包问题,你该了解这些!(滚动数组)
|
||||
|
||||
昨天[动态规划:关于01背包问题,你该了解这些!](https://mp.weixin.qq.com/s/FwIiPPmR18_AJO5eiidT6w)中是用二维dp数组来讲解01背包。
|
||||
|
||||
今天我们就来说一说滚动数组,其实在前面的题目中我们已经用到过滚动数组了,就是把二维dp降为一维dp,一些录友当时还表示比较困惑。
|
||||
|
||||
那么我们通过01背包,来彻底讲一讲滚动数组!
|
||||
|
||||
接下来还是用如下这个例子来进行讲解
|
||||
|
||||
背包最大重量为4。
|
||||
|
||||
物品为:
|
||||
|
||||
| | 重量 | 价值 |
|
||||
| --- | --- | --- |
|
||||
| 物品0 | 1 | 15 |
|
||||
| 物品1 | 3 | 20 |
|
||||
| 物品2 | 4 | 30 |
|
||||
|
||||
问背包能背的物品最大价值是多少?
|
||||
|
||||
## 一维dp数组(滚动数组)
|
||||
|
||||
对于背包问题其实状态都是可以压缩的。
|
||||
|
||||
在使用二维数组的时候,递推公式:dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);
|
||||
|
||||
**其实可以发现如果把dp[i - 1]那一层拷贝到dp[i]上,表达式完全可以是:dp[i][j] = max(dp[i][j], dp[i][j - weight[i]] + value[i]);**
|
||||
|
||||
**于其把dp[i - 1]这一层拷贝到dp[i]上,不如只用一个一维数组了**,只用dp[j](一维数组,也可以理解是一个滚动数组)。
|
||||
|
||||
这就是滚动数组的由来,需要满足的条件是上一层可以重复利用,直接拷贝到当前层。
|
||||
|
||||
读到这里估计大家都忘了 dp[i][j]里的i和j表达的是什么了,i是物品,j是背包容量。
|
||||
|
||||
**dp[i][j] 表示从下标为[0-i]的物品里任意取,放进容量为j的背包,价值总和最大是多少**。
|
||||
|
||||
一定要时刻记住这里i和j的含义,要不然很容易看懵了。
|
||||
|
||||
动规五部曲分析如下:
|
||||
|
||||
1. 确定dp数组的定义
|
||||
|
||||
在一维dp数组中,dp[j]表示:容量为j的背包,所背的物品价值可以最大为dp[j]。
|
||||
|
||||
2. 一维dp数组的递推公式
|
||||
|
||||
dp[j]为 容量为j的背包所背的最大价值,那么如何推导dp[j]呢?
|
||||
|
||||
dp[j]可以通过dp[j - weight[j]]推导出来,dp[j - weight[i]]表示容量为j - weight[i]的背包所背的最大价值。
|
||||
|
||||
dp[j - weight[i]] + value[i] 表示 容量为 j - 物品i重量 的背包 加上 物品i的价值。(也就是容量为j的背包,放入物品i了之后的价值即:dp[j])
|
||||
|
||||
此时dp[j]有两个选择,一个是取自己dp[j],一个是取dp[j - weight[i]] + value[i],指定是取最大的,毕竟是求最大价值,
|
||||
|
||||
所以递归公式为:
|
||||
|
||||
```
|
||||
dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);
|
||||
```
|
||||
|
||||
可以看出相对于二维dp数组的写法,就是把dp[i][j]中i的维度去掉了。
|
||||
|
||||
3. 一维dp数组如何初始化
|
||||
|
||||
**关于初始化,一定要和dp数组的定义吻合,否则到递推公式的时候就会越来越乱**。
|
||||
|
||||
dp[j]表示:容量为j的背包,所背的物品价值可以最大为dp[j],那么dp[0]就应该是0,因为背包容量为0所背的物品的最大价值就是0。
|
||||
|
||||
那么dp数组除了下标0的位置,初始为0,其他下标应该初始化多少呢?
|
||||
|
||||
看一下递归公式:dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);
|
||||
|
||||
dp数组在推导的时候一定是取价值最大的数,如果题目给的价值都是正整数那么非0下标都初始化为0就可以了,如果题目给的价值有负数,那么非0下标就要初始化为负无穷。
|
||||
|
||||
**这样才能让dp数组在递归公式的过程中取的最大的价值,而不是被初始值覆盖了**。
|
||||
|
||||
那么我假设物品价值都是大于0的,所以dp数组初始化的时候,都初始为0就可以了。
|
||||
|
||||
4. 一维dp数组遍历顺序
|
||||
|
||||
代码如下:
|
||||
|
||||
```
|
||||
for(int i = 0; i < weight.size(); i++) { // 遍历物品
|
||||
for(int j = bagWeight; j >= weight[i]; j--) { // 遍历背包容量
|
||||
dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**这里大家发现和二维dp的写法中,遍历背包的顺序是不一样的!**
|
||||
|
||||
二维dp遍历的时候,背包容量是从小到大,而一维dp遍历的时候,背包是从大到小。
|
||||
|
||||
为什么呢?
|
||||
|
||||
**倒叙遍历是为了保证物品i只被放入一次!**,在[动态规划:关于01背包问题,你该了解这些!](https://mp.weixin.qq.com/s/FwIiPPmR18_AJO5eiidT6w)中讲解二维dp数组初始化dp[0][j]时候已经讲解到过一次。
|
||||
|
||||
举一个例子:物品0的重量weight[0] = 1,价值value[0] = 15
|
||||
|
||||
如果正序遍历
|
||||
|
||||
dp[1] = dp[1 - weight[0]] + value[0] = 15
|
||||
|
||||
dp[2] = dp[2 - weight[0]] + value[0] = 30
|
||||
|
||||
此时dp[2]就已经是30了,意味着物品0,被放入了两次,所以不能正序遍历。
|
||||
|
||||
为什么倒叙遍历,就可以保证物品只放入一次呢?
|
||||
|
||||
倒叙就是先算dp[2]
|
||||
|
||||
dp[2] = dp[2 - weight[0]] + value[0] = 15 (dp数组已经都初始化为0)
|
||||
|
||||
dp[1] = dp[1 - weight[0]] + value[0] = 15
|
||||
|
||||
所以从后往前循环,每次取得状态不会和之前取得状态重合,这样每种物品就只取一次了。
|
||||
|
||||
**那么问题又来了,为什么二维dp数组历的时候不用倒叙呢?**
|
||||
|
||||
因为对于二维dp,dp[i][j]都是通过上一层即dp[i - 1][j]计算而来,本层的dp[i][j]并不会被覆盖!
|
||||
|
||||
(如何这里读不懂,大家就要动手试一试了,空想还是不靠谱的,实践出真知!)
|
||||
|
||||
**再来看看两个嵌套for循环的顺序,代码中是先遍历物品嵌套遍历背包容量,那可不可以先遍历背包容量嵌套遍历物品呢?**
|
||||
|
||||
不可以!
|
||||
|
||||
因为一维dp的写法,背包容量一定是要倒序遍历(原因上面已经讲了),如果遍历背包容量放在上一层,那么每个dp[j]就只会放入一个物品,即:背包里只放入了一个物品。
|
||||
|
||||
(这里如果读不懂,就在回想一下dp[j]的定义,或者就把两个for循环顺序颠倒一下试试!)
|
||||
|
||||
**所以一维dp数组的背包在遍历顺序上和二维其实是有很大差异的!**,这一点大家一定要注意。
|
||||
|
||||
5. 举例推导dp数组
|
||||
|
||||
一维dp,分别用物品0,物品1,物品2 来遍历背包,最终得到结果如下:
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
## 一维dp01背包完整C++测试代码
|
||||
|
||||
```
|
||||
void test_1_wei_bag_problem() {
|
||||
vector<int> weight = {1, 3, 4};
|
||||
vector<int> value = {15, 20, 30};
|
||||
int bagWeight = 4;
|
||||
|
||||
// 初始化
|
||||
vector<int> dp(bagWeight + 1, 0);
|
||||
for(int i = 0; i < weight.size(); i++) { // 遍历物品
|
||||
for(int j = bagWeight; j >= weight[i]; j--) { // 遍历背包容量
|
||||
dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);
|
||||
}
|
||||
}
|
||||
cout << dp[bagWeight] << endl;
|
||||
}
|
||||
|
||||
int main() {
|
||||
test_1_wei_bag_problem();
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
可以看出,一维dp 的01背包,要比二维简洁的多! 初始化 和 遍历顺序相对简单了。
|
||||
|
||||
**所以我倾向于使用一维dp数组的写法,比较直观简洁,而且空间复杂度还降了一个数量级!**
|
||||
|
||||
**在后面背包问题的讲解中,我都直接使用一维dp数组来进行推导**。
|
||||
|
||||
## 总结
|
||||
|
||||
以上的讲解可以开发一道面试题目(毕竟力扣上没原题)。
|
||||
|
||||
就是本文中的题目,要求先实现一个纯二维的01背包,如果写出来了,然后再问为什么两个for循环的嵌套顺序这么写?反过来写行不行?再讲一讲初始化的逻辑。
|
||||
|
||||
然后要求实现一个一维数组的01背包,最后再问,一维数组的01背包,两个for循环的顺序反过来写行不行?为什么?
|
||||
|
||||
注意以上问题都是在候选人把代码写出来的情况下才问的。
|
||||
|
||||
就是纯01背包的题目,都不用考01背包应用类的题目就可以看出候选人对算法的理解程度了。
|
||||
|
||||
**相信大家读完这篇文章,应该对以上问题都有了答案!**
|
||||
|
||||
此时01背包理论基础就讲完了,我用了两篇文章把01背包的dp数组定义、递推公式、初始化、遍历顺序从二维数组到一维数组统统深度剖析了一遍,没有放过任何难点。
|
||||
|
||||
大家可以发现其实信息量还是挺大的。
|
||||
|
||||
如果把[动态规划:关于01背包问题,你该了解这些!](https://mp.weixin.qq.com/s/FwIiPPmR18_AJO5eiidT6w)和本篇的内容都理解了,后面我们在做01背包的题目,就会发现非常简单了。
|
||||
|
||||
不用再凭感觉或者记忆去写背包,而是有自己的思考,了解其本质,代码的方方面面都在自己的掌控之中。
|
||||
|
||||
即使代码没有通过,也会有自己的逻辑去debug,这样就思维清晰了。
|
||||
|
||||
接下来就要开始用这两天的理论基础去做力扣上的背包面试题目了,录友们握紧扶手,我们要上高速啦!
|
||||
|
||||
就酱,学算法,认准「代码随想录」,值得你推荐给身边每一位朋友同学们。
|
||||
|
||||
|
||||
## 其他语言版本
|
||||
|
||||
|
||||
Java:
|
||||
|
||||
|
||||
Python:
|
||||
|
||||
|
||||
Go:
|
||||
|
||||
|
||||
|
||||
|
||||
-----------------------
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
@ -1,318 +0,0 @@
|
||||
<p align="center">
|
||||
<a href="https://mp.weixin.qq.com/s/RsdcQ9umo09R6cfnwXZlrQ"><img src="https://img.shields.io/badge/PDF下载-代码随想录-blueviolet" alt=""></a>
|
||||
<a href="https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw"><img src="https://img.shields.io/badge/刷题-微信群-green" alt=""></a>
|
||||
<a href="https://space.bilibili.com/525438321"><img src="https://img.shields.io/badge/B站-代码随想录-orange" alt=""></a>
|
||||
<a href="https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ"><img src="https://img.shields.io/badge/知识星球-代码随想录-blue" alt=""></a>
|
||||
</p>
|
||||
<p align="center"><strong>欢迎大家<a href="https://mp.weixin.qq.com/s/tqCxrMEU-ajQumL1i8im9A">参与本项目</a>,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!</strong></p>
|
||||
|
||||
# 背包问题理论基础
|
||||
|
||||
|
||||
这周我们正式开始讲解背包问题!
|
||||
|
||||
背包问题的经典资料当然是:背包九讲。在公众号「代码随想录」后台回复:背包九讲,就可以获得背包九讲的PDF。
|
||||
|
||||
但说实话,背包九讲对于小白来说确实不太友好,看起来还是有点费劲的,而且都是伪代码理解起来也吃力。
|
||||
|
||||
对于面试的话,其实掌握01背包,和完全背包,就够用了,最多可以再来一个多重背包。
|
||||
|
||||
如果这几种背包,分不清,我这里画了一个图,如下:
|
||||
|
||||

|
||||
|
||||
至于背包九讲其其他背包,面试几乎不会问,都是竞赛级别的了,leetcode上连多重背包的题目都没有,所以题库也告诉我们,01背包和完全背包就够用了。
|
||||
|
||||
而完全背包又是也是01背包稍作变化而来,即:完全背包的物品数量是无限的。
|
||||
|
||||
**所以背包问题的理论基础重中之重是01背包,一定要理解透!**
|
||||
|
||||
leetcode上没有纯01背包的问题,都是01背包应用方面的题目,也就是需要转化为01背包问题。
|
||||
|
||||
**所以我先通过纯01背包问题,把01背包原理讲清楚,后续再讲解leetcode题目的时候,重点就是讲解如何转化为01背包问题了**。
|
||||
|
||||
之前可能有些录友已经可以熟练写出背包了,但只要把这个文章仔细看完,相信你会意外收获!
|
||||
|
||||
## 01 背包
|
||||
|
||||
有N件物品和一个最多能被重量为W 的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。**每件物品只能用一次**,求解将哪些物品装入背包里物品价值总和最大。
|
||||
|
||||

|
||||
|
||||
这是标准的背包问题,以至于很多同学看了这个自然就会想到背包,甚至都不知道暴力的解法应该怎么解了。
|
||||
|
||||
这样其实是没有从底向上去思考,而是习惯性想到了背包,那么暴力的解法应该是怎么样的呢?
|
||||
|
||||
每一件物品其实只有两个状态,取或者不取,所以可以使用回溯法搜索出所有的情况,那么时间复杂度就是O(2^n),这里的n表示物品数量。
|
||||
|
||||
**所以暴力的解法是指数级别的时间复杂度。进而才需要动态规划的解法来进行优化!**
|
||||
|
||||
在下面的讲解中,我举一个例子:
|
||||
|
||||
背包最大重量为4。
|
||||
|
||||
物品为:
|
||||
|
||||
| | 重量 | 价值 |
|
||||
| --- | --- | --- |
|
||||
| 物品0 | 1 | 15 |
|
||||
| 物品1 | 3 | 20 |
|
||||
| 物品2 | 4 | 30 |
|
||||
|
||||
问背包能背的物品最大价值是多少?
|
||||
|
||||
以下讲解和图示中出现的数字都是以这个例子为例。
|
||||
|
||||
## 二维dp数组01背包
|
||||
|
||||
依然动规五部曲分析一波。
|
||||
|
||||
1. 确定dp数组以及下标的含义
|
||||
|
||||
对于背包问题,有一种写法, 是使用二维数组,即**dp[i][j] 表示从下标为[0-i]的物品里任意取,放进容量为j的背包,价值总和最大是多少**。
|
||||
|
||||
只看这个二维数组的定义,大家一定会有点懵,看下面这个图:
|
||||
|
||||

|
||||
|
||||
**要时刻记着这个dp数组的含义,下面的一些步骤都围绕这dp数组的含义进行的**,如果哪里看懵了,就来回顾一下i代表什么,j又代表什么。
|
||||
|
||||
2. 确定递推公式
|
||||
|
||||
再回顾一下dp[i][j]的含义:从下标为[0-i]的物品里任意取,放进容量为j的背包,价值总和最大是多少。
|
||||
|
||||
那么可以有两个方向推出来dp[i][j],
|
||||
|
||||
* 由dp[i - 1][j]推出,即背包容量为j,里面不放物品i的最大价值,此时dp[i][j]就是dp[i - 1][j]
|
||||
* 由dp[i - 1][j - weight[i]]推出,dp[i - 1][j - weight[i]] 为背包容量为j - weight[i]的时候不放物品i的最大价值,那么dp[i - 1][j - weight[i]] + value[i] (物品i的价值),就是背包放物品i得到的最大价值
|
||||
|
||||
所以递归公式: dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);
|
||||
|
||||
3. dp数组如何初始化
|
||||
|
||||
**关于初始化,一定要和dp数组的定义吻合,否则到递推公式的时候就会越来越乱**。
|
||||
|
||||
首先从dp[i][j]的定义触发,如果背包容量j为0的话,即dp[i][0],无论是选取哪些物品,背包价值总和一定为0。如图:
|
||||
|
||||

|
||||
|
||||
在看其他情况。
|
||||
|
||||
状态转移方程 dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]); 可以看出i 是由 i-1 推导出来,那么i为0的时候就一定要初始化。
|
||||
|
||||
dp[0][j],即:i为0,存放编号0的物品的时候,各个容量的背包所能存放的最大价值。
|
||||
|
||||
代码如下:
|
||||
|
||||
```
|
||||
// 倒叙遍历
|
||||
for (int j = bagWeight; j >= weight[0]; j--) {
|
||||
dp[0][j] = dp[0][j - weight[0]] + value[0]; // 初始化i为0时候的情况
|
||||
}
|
||||
```
|
||||
|
||||
**大家应该发现,这个初始化为什么是倒叙的遍历的?正序遍历就不行么?**
|
||||
|
||||
正序遍历还真就不行,dp[0][j]表示容量为j的背包存放物品0时候的最大价值,物品0的价值就是15,因为题目中说了**每个物品只有一个!**所以dp[0][j]如果不是初始值的话,就应该都是物品0的价值,也就是15。
|
||||
|
||||
但如果一旦正序遍历了,那么物品0就会被重复加入多次! 例如代码如下:
|
||||
```
|
||||
// 正序遍历
|
||||
for (int j = weight[0]; j <= bagWeight; j++) {
|
||||
dp[0][j] = dp[0][j - weight[0]] + value[0];
|
||||
}
|
||||
```
|
||||
|
||||
例如dp[0][1] 是15,到了dp[0][2] = dp[0][2 - 1] + 15; 也就是dp[0][2] = 30 了,那么就是物品0被重复放入了。
|
||||
|
||||
**所以一定要倒叙遍历,保证物品0只被放入一次!这一点对01背包很重要,后面在讲解滚动数组的时候,还会用到倒叙遍历来保证物品使用一次!**
|
||||
|
||||
|
||||
此时dp数组初始化情况如图所示:
|
||||
|
||||

|
||||
|
||||
dp[0][j] 和 dp[i][0] 都已经初始化了,那么其他下标应该初始化多少呢?
|
||||
|
||||
|
||||
dp[i][j]在推导的时候一定是取价值最大的数,如果题目给的价值都是正整数那么非0下标都初始化为0就可以了,因为0就是最小的了,不会影响取最大价值的结果。
|
||||
|
||||
如果题目给的价值有负数,那么非0下标就要初始化为负无穷了。例如:一个物品的价值是-2,但对应的位置依然初始化为0,那么取最大值的时候,就会取0而不是-2了,所以要初始化为负无穷。
|
||||
|
||||
**这样才能让dp数组在递归公式的过程中取最大的价值,而不是被初始值覆盖了**。
|
||||
|
||||
最后初始化代码如下:
|
||||
|
||||
```
|
||||
// 初始化 dp
|
||||
vector<vector<int>> dp(weight.size() + 1, vector<int>(bagWeight + 1, 0));
|
||||
for (int j = bagWeight; j >= weight[0]; j--) {
|
||||
dp[0][j] = dp[0][j - weight[0]] + value[0];
|
||||
}
|
||||
```
|
||||
|
||||
**费了这么大的功夫,才把如何初始化讲清楚,相信不少同学平时初始化dp数组是凭感觉来的,但有时候感觉是不靠谱的**。
|
||||
|
||||
4. 确定遍历顺序
|
||||
|
||||
|
||||
在如下图中,可以看出,有两个遍历的维度:物品与背包重量
|
||||
|
||||

|
||||
|
||||
那么问题来了,**先遍历 物品还是先遍历背包重量呢?**
|
||||
|
||||
**其实都可以!! 但是先遍历物品更好理解**。
|
||||
|
||||
那么我先给出先遍历物品,然后遍历背包重量的代码。
|
||||
|
||||
```
|
||||
// weight数组的大小 就是物品个数
|
||||
for(int i = 1; i < weight.size(); i++) { // 遍历物品
|
||||
for(int j = 0; j <= bagWeight; j++) { // 遍历背包容量
|
||||
if (j < weight[i]) dp[i][j] = dp[i - 1][j]; // 这个是为了展现dp数组里元素的变化
|
||||
else dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**先遍历背包,再遍历物品,也是可以的!(注意我这里使用的二维dp数组)**
|
||||
|
||||
例如这样:
|
||||
|
||||
```
|
||||
// weight数组的大小 就是物品个数
|
||||
for(int j = 0; j <= bagWeight; j++) { // 遍历背包容量
|
||||
for(int i = 1; i < weight.size(); i++) { // 遍历物品
|
||||
if (j < weight[i]) dp[i][j] = dp[i - 1][j];
|
||||
else dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
为什么也是可以的呢?
|
||||
|
||||
**要理解递归的本质和递推的方向**。
|
||||
|
||||
dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]); 递归公式中可以看出dp[i][j]是靠dp[i-1][j]和dp[i - 1][j - weight[i]]推导出来的。
|
||||
|
||||
dp[i-1][j]和dp[i - 1][j - weight[i]] 都在dp[i][j]的左上角方向(包括正左和正上两个方向),那么先遍历物品,再遍历背包的过程如图所示:
|
||||
|
||||

|
||||
|
||||
再来看看先遍历背包,再遍历物品呢,如图:
|
||||
|
||||

|
||||
|
||||
**大家可以看出,虽然两个for循环遍历的次序不同,但是dp[i][j]所需要的数据就是左上角,根本不影响dp[i][j]公式的推导!**
|
||||
|
||||
但先遍历物品再遍历背包这个顺序更好理解。
|
||||
|
||||
**其实背包问题里,两个for循环的先后循序是非常有讲究的,理解遍历顺序其实比理解推导公式难多了**。
|
||||
|
||||
5. 举例推导dp数组
|
||||
|
||||
来看一下对应的dp数组的数值,如图:
|
||||
|
||||

|
||||
|
||||
最终结果就是dp[2][4]。
|
||||
|
||||
建议大家此时自己在纸上推导一遍,看看dp数组里每一个数值是不是这样的。
|
||||
|
||||
**做动态规划的题目,最好的过程就是自己在纸上举一个例子把对应的dp数组的数值推导一下,然后在动手写代码!**
|
||||
|
||||
很多同学做dp题目,遇到各种问题,然后凭感觉东改改西改改,怎么改都不对,或者稀里糊涂就改过了。
|
||||
|
||||
主要就是自己没有动手推导一下dp数组的演变过程,如果推导明白了,代码写出来就算有问题,只要把dp数组打印出来,对比一下和自己推导的有什么差异,很快就可以发现问题了。
|
||||
|
||||
|
||||
## 完整C++测试代码
|
||||
|
||||
```C++
|
||||
void test_2_wei_bag_problem1() {
|
||||
vector<int> weight = {1, 3, 4};
|
||||
vector<int> value = {15, 20, 30};
|
||||
int bagWeight = 4;
|
||||
|
||||
// 二维数组
|
||||
vector<vector<int>> dp(weight.size() + 1, vector<int>(bagWeight + 1, 0));
|
||||
|
||||
// 初始化
|
||||
for (int j = bagWeight; j >= weight[0]; j--) {
|
||||
dp[0][j] = dp[0][j - weight[0]] + value[0];
|
||||
}
|
||||
|
||||
// weight数组的大小 就是物品个数
|
||||
for(int i = 1; i < weight.size(); i++) { // 遍历物品
|
||||
for(int j = 0; j <= bagWeight; j++) { // 遍历背包容量
|
||||
if (j < weight[i]) dp[i][j] = dp[i - 1][j];
|
||||
else dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
cout << dp[weight.size() - 1][bagWeight] << endl;
|
||||
}
|
||||
|
||||
int main() {
|
||||
test_2_wei_bag_problem1();
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
以上遍历的过程也可以这么写:
|
||||
|
||||
```
|
||||
// 遍历过程
|
||||
for(int i = 1; i < weight.size(); i++) { // 遍历物品
|
||||
for(int j = 0; j <= bagWeight; j++) { // 遍历背包容量
|
||||
if (j - weight[i] >= 0) {
|
||||
dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
这么写打印出来的dp数据这就是这样:
|
||||
|
||||

|
||||
|
||||
空出来的0其实是用不上的,版本一 能把完整的dp数组打印出来,出来我用版本一来讲解。
|
||||
|
||||
|
||||
## 总结
|
||||
|
||||
讲了这么多才刚刚把二维dp的01背包讲完,**这里大家其实可以发现最简单的是推导公式了,推导公式估计看一遍就记下来了,但难就难在如何初始化和遍历顺序上**。
|
||||
|
||||
可能有的同学并没有注意到初始化 和 遍历顺序的重要性,我们后面做力扣上背包面试题目的时候,大家就会感受出来了。
|
||||
|
||||
下一篇 还是理论基础,我们再来讲一维dp数组实现的01背包(滚动数组),分析一下和二维有什么区别,在初始化和遍历顺序上又有什么差异,敬请期待!
|
||||
|
||||
就酱,学算法,认准「代码随想录」,值得推荐给身边的朋友同学们,关注后都会发现相见恨晚!
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## 其他语言版本
|
||||
|
||||
|
||||
Java:
|
||||
|
||||
|
||||
Python:
|
||||
|
||||
|
||||
Go:
|
||||
|
||||
|
||||
|
||||
|
||||
-----------------------
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
@ -179,9 +179,45 @@ int main() {
|
||||
|
||||
Java:
|
||||
|
||||
|
||||
Python:
|
||||
|
||||
```python3
|
||||
# 先遍历物品,再遍历背包
|
||||
def test_complete_pack1():
|
||||
weight = [1, 3, 4]
|
||||
value = [15, 20, 30]
|
||||
bag_weight = 4
|
||||
|
||||
dp = [0]*(bag_weight + 1)
|
||||
|
||||
for i in range(len(weight)):
|
||||
for j in range(weight[i], bag_weight + 1):
|
||||
dp[j] = max(dp[j], dp[j - weight[i]] + value[i])
|
||||
|
||||
print(dp[bag_weight])
|
||||
|
||||
# 先遍历背包,再遍历物品
|
||||
def test_complete_pack2():
|
||||
weight = [1, 3, 4]
|
||||
value = [15, 20, 30]
|
||||
bag_weight = 4
|
||||
|
||||
dp = [0]*(bag_weight + 1)
|
||||
|
||||
for j in range(bag_weight + 1):
|
||||
for i in range(len(weight)):
|
||||
if j >= weight[i]: dp[j] = max(dp[j], dp[j - weight[i]] + value[i])
|
||||
|
||||
print(dp[bag_weight])
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
test_complete_pack1()
|
||||
test_complete_pack2()
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
Go:
|
||||
|
||||
|
@ -83,7 +83,7 @@ struct ListNode {
|
||||
|
||||
有同学说了,我不定义构造函数行不行,答案是可以的,C++默认生成一个构造函数。
|
||||
|
||||
但是这个构造函数不会初始化任何成员变化,下面我来举两个例子:
|
||||
但是这个构造函数不会初始化任何成员变量,下面我来举两个例子:
|
||||
|
||||
通过自己定义构造函数初始化节点:
|
||||
|
||||
|
@ -151,10 +151,83 @@ public class Solution {
|
||||
```
|
||||
|
||||
Python:
|
||||
```python
|
||||
# Definition for singly-linked list.
|
||||
# class ListNode:
|
||||
# def __init__(self, x):
|
||||
# self.val = x
|
||||
# self.next = None
|
||||
|
||||
class Solution:
|
||||
def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
|
||||
lengthA,lengthB = 0,0
|
||||
curA,curB = headA,headB
|
||||
while(curA!=None): #求链表A的长度
|
||||
curA = curA.next
|
||||
lengthA +=1
|
||||
|
||||
while(curB!=None): #求链表B的长度
|
||||
curB = curB.next
|
||||
lengthB +=1
|
||||
|
||||
curA, curB = headA, headB
|
||||
|
||||
if lengthB>lengthA: #让curA为最长链表的头,lenA为其长度
|
||||
lengthA, lengthB = lengthB, lengthA
|
||||
curA, curB = curB, curA
|
||||
|
||||
gap = lengthA - lengthB #求长度差
|
||||
while(gap!=0):
|
||||
curA = curA.next #让curA和curB在同一起点上
|
||||
gap -= 1
|
||||
|
||||
while(curA!=None):
|
||||
if curA == curB:
|
||||
return curA
|
||||
else:
|
||||
curA = curA.next
|
||||
curB = curB.next
|
||||
return None
|
||||
```
|
||||
|
||||
Go:
|
||||
|
||||
```go
|
||||
func getIntersectionNode(headA, headB *ListNode) *ListNode {
|
||||
curA := headA
|
||||
curB := headB
|
||||
lenA, lenB := 0, 0
|
||||
// 求A,B的长度
|
||||
for curA != nil {
|
||||
curA = curA.Next
|
||||
lenA++
|
||||
}
|
||||
for curB != nil {
|
||||
curB = curB.Next
|
||||
lenB++
|
||||
}
|
||||
var step int
|
||||
var fast, slow *ListNode
|
||||
// 请求长度差,并且让更长的链表先走相差的长度
|
||||
if lenA > lenB {
|
||||
step = lenA - lenB
|
||||
fast, slow = headA, headB
|
||||
} else {
|
||||
step = lenB - lenA
|
||||
fast, slow = headB, headA
|
||||
}
|
||||
for i:=0; i < step; i++ {
|
||||
fast = fast.Next
|
||||
}
|
||||
// 遍历两个链表遇到相同则跳出遍历
|
||||
for fast != slow {
|
||||
fast = fast.Next
|
||||
slow = slow.Next
|
||||
}
|
||||
return fast
|
||||
}
|
||||
```
|
||||
|
||||
javaScript:
|
||||
|
||||
```js
|
||||
|
Reference in New Issue
Block a user