update change

This commit is contained in:
Logen
2023-05-05 13:54:28 -05:00
78 changed files with 1864 additions and 1073 deletions

View File

@@ -62,7 +62,7 @@
如果你是算法老手,这篇攻略也是复习的最佳资料,如果把每个系列对应的总结篇,快速过一遍,整个算法知识体系以及各种解法就重现脑海了。
目前「代码随想录」刷题攻略更新了:**200多篇文章精讲了200道经典算法题目共60w字的详细图解部分难点题目搭配了20分钟左右的视频讲解**。
目前「代码随想录」刷题攻略更新了:**200多篇文章精讲了200道经典算法题目共60w字的详细图解部分题目搭配了20分钟左右的视频讲解**,视频质量很好,口碑很好,大家可以去看看,视频列表:[代码随想录视频讲解](https://www.bilibili.com/video/BV1fA4y1o715)
**这里每一篇题解,都是精品,值得仔细琢磨**

View File

@@ -127,21 +127,29 @@ Python:
# 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+1步
# 创建一个虚拟节点,并将其下一个指针设置为链表的头部
dummy_head = ListNode(0, head)
# 创建两个指针,慢指针和快指针,并将它们初始化为虚拟节点
slow = fast = dummy_head
# 快指针比慢指针快 n+1 步
for i in range(n+1):
fast = fast.next
n -= 1
while(fast!=None):
# 移动两个指针,直到快速指针到达链表的末尾
while fast:
slow = slow.next
fast = fast.next
#fast 走到结尾后slow的下一个节点为倒数第N个节点
slow.next = slow.next.next #删除
return head_dummy.next
# 通过更新第 (n-1) 个节点的 next 指针删除第 n 个节点
slow.next = slow.next.next
return dummy_head.next
```
Go:
```Go

View File

@@ -186,21 +186,20 @@ Python
class Solution:
def swapPairs(self, head: ListNode) -> ListNode:
res = ListNode(next=head)
pre = res
dummy_head = ListNode(next=head)
current = dummy_head
# 必须有pre的下一个和下下个才能交换,否则说明已经交换结束了
while pre.next and pre.next.next:
cur = pre.next
post = pre.next.next
# 必须有cur的下一个和下下个才能交换,否则说明已经交换结束了
while current.next and current.next.next:
temp = current.next # 防止节点修改
temp1 = current.next.next.next
# precurpost对应最左中间的最右边的节点
cur.next = post.next
post.next = cur
pre.next = post
current.next = current.next.next
current.next.next = temp
temp.next = temp1
current = current.next.next
return dummy_head.next
pre = pre.next.next
return res.next
```
Go

View File

@@ -198,6 +198,7 @@ Python
``` python 3
(版本一)快慢指针法
class Solution:
def removeElement(self, nums: List[int], val: int) -> int:
# 快慢指针
@@ -213,7 +214,21 @@ class Solution:
return slow
```
``` python 3
(版本二)暴力法
class Solution:
def removeElement(self, nums: List[int], val: int) -> int:
i, l = 0, len(nums)
while i < l:
if nums[i] == val: # 找到等于目标值的节点
for j in range(i+1, l): # 移除该元素,并将后面元素向前平移
nums[j - 1] = nums[j]
l -= 1
i -= 1
i += 1
return l
```
Go

View File

@@ -76,11 +76,9 @@ public:
for (int i = 0; i < nums.size(); i++) {
nextDistance = max(nums[i] + i, nextDistance); // 更新下一步覆盖最远距离下标
if (i == curDistance) { // 遇到当前覆盖最远距离下标
if (curDistance < nums.size() - 1) { // 如果当前覆盖最远距离下标不是终点
ans++; // 需要走下一步
curDistance = nextDistance; // 更新当前覆盖最远距离下标(相当于加油了)
if (nextDistance >= nums.size() - 1) break; // 下一步的覆盖范围已经可以达到终点,结束循环
} else break; // 当前覆盖最远距到达集合终点不用做ans++操作了,直接结束
ans++; // 需要走下一步
curDistance = nextDistance; // 更新当前覆盖最远距离下标(相当于加油了)
if (nextDistance >= nums.size() - 1) break; // 当前覆盖最远距到达集合终点不用做ans++操作了,直接结束
}
}
return ans;
@@ -88,6 +86,10 @@ public:
};
```
* 时间复杂度: O(n)
* 空间复杂度: O(1)
## 方法二
依然是贪心,思路和方法一差不多,代码可以简洁一些。
@@ -127,6 +129,11 @@ public:
};
```
* 时间复杂度: O(n)
* 空间复杂度: O(1)
可以看出版本二的代码相对于版本一简化了不少!
**其精髓在于控制移动下标 i 只移动到 nums.size() - 2 的位置**,所以移动下标只要遇到当前覆盖最远距离的下标,直接步数加一,不用考虑别的了。
@@ -231,6 +238,21 @@ class Solution:
step += 1
return step
```
```python
# 贪心版本三 - 类似55-跳跃游戏’写法
class Solution:
def jump(self, nums) -> int:
if len(nums)==1: return 0
i = 0
count = 0
cover = 0
while i<=cover:
for i in range(i,cover+1):
cover = max(nums[i]+i,cover)
if cover>=len(nums)-1: return count+1
count+=1
```
```python
# 动态规划做法

View File

@@ -158,6 +158,19 @@ if (i > 0 && nums[i] == nums[i - 1] && used[i - 1] == true) {
所以我通过举[1,1,1]的例子,把这两个去重的逻辑分别抽象成树形结构,大家可以一目了然:为什么两种写法都可以以及哪一种效率更高!
这里可能大家又有疑惑,既然 `used[i - 1] == false`也行而`used[i - 1] == true`也行,那为什么还要写这个条件呢?
直接这样写 不就完事了?
```cpp
if (i > 0 && nums[i] == nums[i - 1]) {
continue;
}
```
其实并不行,一定要加上 `used[i - 1] == false`或者`used[i - 1] == true`,因为 used[i - 1] 要一直是 true 或者一直是false 才可以,而不是 一会是true 一会又是false 所以这个条件要写上。
是不是豁然开朗了!!
## 其他语言版本

View File

@@ -24,8 +24,6 @@
暴力解法的思路,第一层 for 就是设置起始位置,第二层 for 循环遍历数组寻找最大值
- 时间复杂度O(n^2)
- 空间复杂度O(1)
```CPP
class Solution {
@@ -44,6 +42,9 @@ public:
}
};
```
* 时间复杂度O(n^2)
* 空间复杂度O(1)
以上暴力的解法 C++勉强可以过,其他语言就不确定了。
@@ -98,7 +99,6 @@ public:
}
};
```
- 时间复杂度O(n)
- 空间复杂度O(1)

View File

@@ -50,7 +50,8 @@
如图:
![55.跳跃游戏](https://code-thinking-1253855093.file.myqcloud.com/pics/20201124154758229-20230310135019977.png)
![](https://code-thinking-1253855093.file.myqcloud.com/pics/20230203105634.png)
i 每次移动只能在 cover 的范围内移动每移动一个元素cover 得到该元素数值(新的覆盖范围)的补充,让 i 继续移动下去。
@@ -75,6 +76,10 @@ public:
};
```
* 时间复杂度: O(n)
* 空间复杂度: O(1)
## 总结
这道题目关键点在于:不用拘泥于每次究竟跳几步,而是看覆盖范围,覆盖范围内一定是可以跳过来的,不用管是怎么跳的。

View File

@@ -73,6 +73,10 @@ public:
};
```
* 时间复杂度: O(nlogn)
* 空间复杂度: O(logn),排序需要的空间开销
## 其他语言版本
@@ -109,7 +113,6 @@ class Solution {
}
}
}
```
```java
// 版本2
@@ -308,24 +311,22 @@ object Solution {
```Rust
impl Solution {
fn max(a: i32, b: i32) -> i32 {
if a > b { a } else { b }
}
pub fn merge(intervals: Vec<Vec<i32>>) -> Vec<Vec<i32>> {
let mut intervals = intervals;
let mut result = Vec::new();
if intervals.len() == 0 { return result; }
intervals.sort_by(|a, b| a[0].cmp(&b[0]));
result.push(intervals[0].clone());
for i in 1..intervals.len() {
if result.last_mut().unwrap()[1] >= intervals[i][0] {
result.last_mut().unwrap()[1] = Self::max(result.last_mut().unwrap()[1], intervals[i][1]);
pub fn merge(mut intervals: Vec<Vec<i32>>) -> Vec<Vec<i32>> {
let mut res = vec![];
if intervals.is_empty() {
return res;
}
intervals.sort_by_key(|a| a[0]);
res.push(intervals[0].clone());
for interval in intervals.into_iter().skip(1) {
let res_last_ele = res.last_mut().unwrap();
if res_last_ele[1] >= interval[0] {
res_last_ele[1] = interval[1].max(res_last_ele[1]);
} else {
result.push(intervals[i].clone());
res.push(interval);
}
}
result
res
}
}
```

View File

@@ -395,21 +395,14 @@ function uniquePaths(m: number, n: number): number {
```Rust
impl Solution {
pub fn unique_paths(m: i32, n: i32) -> i32 {
let m = m as usize;
let n = n as usize;
let mut dp = vec![vec![0; n]; m];
for i in 0..m {
dp[i][0] = 1;
let (m, n) = (m as usize, n as usize);
let mut dp = vec![vec![1; n]; m];
for i in 1..m {
for j in 1..n {
dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
}
for j in 0..n {
dp[0][j] = 1;
}
for i in 1..m {
for j in 1..n {
dp[i][j] = dp[i-1][j] + dp[i][j-1];
}
}
dp[m-1][n-1]
}
dp[m - 1][n - 1]
}
}
```

View File

@@ -135,7 +135,7 @@ for (int i = 1; i < m; i++) {
![63.不同路径II2](https://code-thinking-1253855093.file.myqcloud.com/pics/20210104114610256.png)
如果这个图看不,建议理解一下递归公式,然后照着文章中说的遍历顺序,自己推导一下!
如果这个图看不,建议理解一下递归公式,然后照着文章中说的遍历顺序,自己推导一下!
动规五部分分析完毕对应C++代码如下:

View File

@@ -72,7 +72,7 @@ dp[i] 爬到第i层楼梯有dp[i]种方法
3. dp数组如何初始化
再回顾一下dp[i]的定义爬到第i层楼梯有dp[i]方法。
再回顾一下dp[i]的定义爬到第i层楼梯有dp[i]方法。
那么i为0dp[i]应该是多少呢,这个可以有很多解释,但基本都是直接奔着答案去解释的。
@@ -454,18 +454,32 @@ public class Solution {
```rust
impl Solution {
pub fn climb_stairs(n: i32) -> i32 {
if n <= 2 {
if n <= 1 {
return n;
}
let mut a = 1;
let mut b = 2;
let mut f = 0;
for i in 2..n {
let (mut a, mut b, mut f) = (1, 1, 0);
for _ in 2..=n {
f = a + b;
a = b;
b = f;
}
return f;
f
}
```
dp 数组
```rust
impl Solution {
pub fn climb_stairs(n: i32) -> i32 {
let n = n as usize;
let mut dp = vec![0; n + 1];
dp[0] = 1;
dp[1] = 1;
for i in 2..=n {
dp[i] = dp[i - 1] + dp[i - 2];
}
dp[n]
}
}
```

View File

@@ -101,6 +101,11 @@ public:
};
```
* 时间复杂度: O(nm)
* 空间复杂度: O(n)
代码中m表示最多可以爬m个台阶代码中把m改成2就是本题70.爬楼梯可以AC的代码了。
## 总结

View File

@@ -218,6 +218,10 @@ public:
}
};
```
* 时间复杂度: O(n * m)
* 空间复杂度: O(n * m)
## 其他语言版本

View File

@@ -307,6 +307,33 @@ class Solution {
}
}
```
单调栈精简
```java
class Solution {
public int largestRectangleArea(int[] heights) {
int[] newHeight = new int[heights.length + 2];
System.arraycopy(heights, 0, newHeight, 1, heights.length);
newHeight[heights.length+1] = 0;
newHeight[0] = 0;
Stack<Integer> stack = new Stack<>();
stack.push(0);
int res = 0;
for (int i = 1; i < newHeight.length; i++) {
while (newHeight[i] < newHeight[stack.peek()]) {
int mid = stack.pop();
int w = i - stack.peek() - 1;
int h = newHeight[mid];
res = Math.max(res, w * h);
}
stack.push(i);
}
return res;
}
}
```
Python3:

View File

@@ -442,25 +442,31 @@ class Solution:
层次遍历
```python
class Solution:
def isSymmetric(self, root: Optional[TreeNode]) -> bool:
def isSymmetric(self, root: TreeNode) -> bool:
if not root:
return True
que = [root]
while que:
this_level_length = len(que)
for i in range(this_level_length // 2):
# 要么其中一个是None但另外一个不是
if (not que[i] and que[this_level_length - 1 - i]) or (que[i] and not que[this_level_length - 1 - i]):
return False
# 要么两个都不是None
if que[i] and que[i].val != que[this_level_length - 1 - i].val:
return False
for i in range(this_level_length):
if not que[i]: continue
que.append(que[i].left)
que.append(que[i].right)
que = que[this_level_length:]
queue = collections.deque([root.left, root.right])
while queue:
level_size = len(queue)
if level_size % 2 != 0:
return False
level_vals = []
for i in range(level_size):
node = queue.popleft()
if node:
level_vals.append(node.val)
queue.append(node.left)
queue.append(node.right)
else:
level_vals.append(None)
if level_vals != level_vals[::-1]:
return False
return True
```

View File

@@ -171,47 +171,59 @@ python3代码
```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]]:
results = []
def levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]:
if not root:
return results
from collections import deque
que = deque([root])
while que:
size = len(que)
result = []
for _ in range(size):
cur = que.popleft()
result.append(cur.val)
return []
queue = collections.deque([root])
result = []
while queue:
level = []
for _ in range(len(queue)):
cur = queue.popleft()
level.append(cur.val)
if cur.left:
que.append(cur.left)
queue.append(cur.left)
if cur.right:
que.append(cur.right)
results.append(result)
return results
queue.append(cur.right)
result.append(level)
return result
```
```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]]:
res = []
def helper(root, depth):
if not root: return []
if len(res) == depth: res.append([]) # start the current depth
res[depth].append(root.val) # fulfil the current depth
if root.left: helper(root.left, depth + 1) # process child nodes for the next depth
if root.right: helper(root.right, depth + 1)
helper(root, 0)
return res
def levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]:
levels = []
self.helper(root, 0, levels)
return levels
def helper(self, node, level, levels):
if not node:
return
if len(levels) == level:
levels.append([])
levels[level].append(node.val)
self.helper(node.left, level + 1, levels)
self.helper(node.right, level + 1, levels)
```
go:
```go
@@ -500,27 +512,29 @@ python代码
class Solution:
"""二叉树层序遍历II迭代解法"""
# 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]]:
results = []
if not root:
return results
from collections import deque
que = deque([root])
while que:
result = []
for _ in range(len(que)):
cur = que.popleft()
result.append(cur.val)
return []
queue = collections.deque([root])
result = []
while queue:
level = []
for _ in range(len(queue)):
cur = queue.popleft()
level.append(cur.val)
if cur.left:
que.append(cur.left)
queue.append(cur.left)
if cur.right:
que.append(cur.right)
results.append(result)
results.reverse()
return results
queue.append(cur.right)
result.append(level)
return result[::-1]
```
Java
@@ -821,35 +835,35 @@ 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()
queue = collections.deque([root])
right_view = []
while queue:
level_size = len(queue)
for i in range(level_size):
node = queue.popleft()
if i == level_size - 1:
right_view.append(node.val)
if node.left:
quene.append(node.left)
queue.append(node.left)
if node.right:
quene.append(node.right)
return out_list
# 执行用时36 ms, 在所有 Python3 提交中击败了89.47%的用户
# 内存消耗14.6 MB, 在所有 Python3 提交中击败了96.65%的用户
queue.append(node.right)
return right_view
```
@@ -1107,27 +1121,38 @@ python代码
class Solution:
"""二叉树层平均值迭代解法"""
# 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]:
results = []
if not root:
return results
return []
from collections import deque
que = deque([root])
while que:
size = len(que)
sum_ = 0
for _ in range(size):
cur = que.popleft()
sum_ += cur.val
if cur.left:
que.append(cur.left)
if cur.right:
que.append(cur.right)
results.append(sum_ / size)
return results
queue = collections.deque([root])
averages = []
while queue:
size = len(queue)
level_sum = 0
for i in range(size):
node = queue.popleft()
level_sum += node.val
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
averages.append(level_sum / size)
return averages
```
java:
@@ -1401,28 +1426,36 @@ public:
python代码
```python
"""
# Definition for a Node.
class Node:
def __init__(self, val=None, children=None):
self.val = val
self.children = children
"""
class Solution:
"""N叉树的层序遍历迭代法"""
def levelOrder(self, root: 'Node') -> List[List[int]]:
results = []
if not root:
return results
return []
from collections import deque
que = deque([root])
result = []
queue = collections.deque([root])
while que:
result = []
for _ in range(len(que)):
cur = que.popleft()
result.append(cur.val)
# cur.children 是 Node 对象组成的列表,也可能为 None
if cur.children:
que.extend(cur.children)
results.append(result)
while queue:
level_size = len(queue)
level = []
return results
for _ in range(level_size):
node = queue.popleft()
level.append(node.val)
for child in node.children:
queue.append(child)
result.append(level)
return result
```
```python
@@ -1728,22 +1761,37 @@ 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 largestValues(self, root: TreeNode) -> List[int]:
if root is None:
if not root:
return []
queue = [root]
out_list = []
result = []
queue = collections.deque([root])
while queue:
length = len(queue)
in_list = []
for _ in range(length):
curnode = queue.pop(0)
in_list.append(curnode.val)
if curnode.left: queue.append(curnode.left)
if curnode.right: queue.append(curnode.right)
out_list.append(max(in_list))
return out_list
level_size = len(queue)
max_val = float('-inf')
for _ in range(level_size):
node = queue.popleft()
max_val = max(max_val, node.val)
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
result.append(max_val)
return result
```
java代码
@@ -2048,36 +2096,40 @@ class Solution {
python代码
```python
# 层序遍历解法
"""
# Definition for a Node.
class Node:
def __init__(self, val: int = 0, left: 'Node' = None, right: 'Node' = None, next: 'Node' = None):
self.val = val
self.left = left
self.right = right
self.next = next
"""
class Solution:
def connect(self, root: 'Node') -> 'Node':
if not root:
return None
queue = [root]
return root
queue = collections.deque([root])
while queue:
n = len(queue)
for i in range(n):
node = queue.pop(0)
level_size = len(queue)
prev = None
for i in range(level_size):
node = queue.popleft()
if prev:
prev.next = node
prev = node
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
if i == n - 1:
break
node.next = queue[0]
return root
# 链表解法
class Solution:
def connect(self, root: 'Node') -> 'Node':
first = root
while first:
cur = first
while cur: # 遍历每一层的节点
if cur.left: cur.left.next = cur.right # 找左节点的next
if cur.right and cur.next: cur.right.next = cur.next.left # 找右节点的next
cur = cur.next # cur同层移动到下一节点
first = first.left # 从本层扩展到下一层
return root
```
@@ -2329,21 +2381,41 @@ python代码
```python
# 层序遍历解法
"""
# Definition for a Node.
class Node:
def __init__(self, val: int = 0, left: 'Node' = None, right: 'Node' = None, next: 'Node' = None):
self.val = val
self.left = left
self.right = right
self.next = next
"""
class Solution:
def connect(self, root: 'Node') -> 'Node':
if not root:
return None
queue = [root]
while queue: # 遍历每一层
length = len(queue)
tail = None # 每一层维护一个尾节点
for i in range(length): # 遍历当前层
curnode = queue.pop(0)
if tail:
tail.next = curnode # 让尾节点指向当前节点
tail = curnode # 让当前节点成为尾节点
if curnode.left : queue.append(curnode.left)
if curnode.right: queue.append(curnode.right)
return root
queue = collections.deque([root])
while queue:
level_size = len(queue)
prev = None
for i in range(level_size):
node = queue.popleft()
if prev:
prev.next = node
prev = node
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
return root
```
@@ -2592,24 +2664,31 @@ class Solution {
Python
```python 3
# 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 maxDepth(self, root: TreeNode) -> int:
if root == None:
if not root:
return 0
queue_ = [root]
depth = 0
while queue_:
length = len(queue_)
for i in range(length):
cur = queue_.pop(0)
sub.append(cur.val)
#子节点入队列
if cur.left: queue_.append(cur.left)
if cur.right: queue_.append(cur.right)
queue = collections.deque([root])
while queue:
depth += 1
for _ in range(len(queue)):
node = queue.popleft()
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
return depth
```
Go
@@ -2859,23 +2938,26 @@ Python 3
# self.right = right
class Solution:
def minDepth(self, root: TreeNode) -> int:
if root == None:
if not root:
return 0
depth = 0
queue = collections.deque([root])
while queue:
depth += 1
for _ in range(len(queue)):
node = queue.popleft()
if not node.left and not node.right:
return depth
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
#根节点的深度为1
queue_ = [(root,1)]
while queue_:
cur, depth = queue_.pop(0)
if cur.left == None and cur.right == None:
return depth
#先左子节点,由于左子节点没有孩子,则就是这一层了
if cur.left:
queue_.append((cur.left,depth + 1))
if cur.right:
queue_.append((cur.right,depth + 1))
return 0
return depth
```
Go

View File

@@ -38,7 +38,7 @@
本题可以使用前序(中左右),也可以使用后序遍历(左右中),使用前序求的就是深度,使用后序求的是高度。
* 二叉树节点的深度指从根节点到该节点的最长简单路径边的条数或者节点数取决于深度从0开始还是从1开始
* 二叉树节点的高度:指从该节点到叶子节点的最长简单路径边的条数者节点数取决于高度从0开始还是从1开始
* 二叉树节点的高度:指从该节点到叶子节点的最长简单路径边的条数者节点数取决于高度从0开始还是从1开始
**而根节点的高度就是二叉树的最大深度**,所以本题中我们通过后序求的根节点高度来求的二叉树最大深度。
@@ -50,7 +50,7 @@
代码如下:
```CPP
int getdepth(treenode* node)
int getdepth(TreeNode* node)
```
2. 确定终止条件如果为空节点的话就返回0表示高度为0。
@@ -76,14 +76,14 @@ return depth;
```CPP
class solution {
public:
int getdepth(treenode* node) {
int getdepth(TreeNode* node) {
if (node == NULL) return 0;
int leftdepth = getdepth(node->left); // 左
int rightdepth = getdepth(node->right); // 右
int depth = 1 + max(leftdepth, rightdepth); // 中
return depth;
}
int maxdepth(treenode* root) {
int maxDepth(TreeNode* root) {
return getdepth(root);
}
};
@@ -93,9 +93,9 @@ public:
```CPP
class solution {
public:
int maxdepth(treenode* root) {
int maxDepth(TreeNode* root) {
if (root == null) return 0;
return 1 + max(maxdepth(root->left), maxdepth(root->right));
return 1 + max(maxDepth(root->left), maxDepth(root->right));
}
};
@@ -110,7 +110,7 @@ public:
class solution {
public:
int result;
void getdepth(treenode* node, int depth) {
void getdepth(TreeNode* node, int depth) {
result = depth > result ? depth : result; // 中
if (node->left == NULL && node->right == NULL) return ;
@@ -127,7 +127,7 @@ public:
}
return ;
}
int maxdepth(treenode* root) {
int maxDepth(TreeNode* root) {
result = 0;
if (root == NULL) return result;
getdepth(root, 1);
@@ -144,7 +144,7 @@ public:
class solution {
public:
int result;
void getdepth(treenode* node, int depth) {
void getdepth(TreeNode* node, int depth) {
result = depth > result ? depth : result; // 中
if (node->left == NULL && node->right == NULL) return ;
if (node->left) { // 左
@@ -155,7 +155,7 @@ public:
}
return ;
}
int maxdepth(treenode* root) {
int maxDepth(TreeNode* root) {
result = 0;
if (root == 0) return result;
getdepth(root, 1);
@@ -182,16 +182,16 @@ c++代码如下:
```CPP
class solution {
public:
int maxdepth(treenode* root) {
int maxDepth(TreeNode* root) {
if (root == NULL) return 0;
int depth = 0;
queue<treenode*> que;
queue<TreeNode*> que;
que.push(root);
while(!que.empty()) {
int size = que.size();
depth++; // 记录深度
for (int i = 0; i < size; i++) {
treenode* node = que.front();
TreeNode* node = que.front();
que.pop();
if (node->left) que.push(node->left);
if (node->right) que.push(node->right);
@@ -230,11 +230,11 @@ c++代码:
```CPP
class solution {
public:
int maxdepth(node* root) {
int maxDepth(Node* root) {
if (root == 0) return 0;
int depth = 0;
for (int i = 0; i < root->children.size(); i++) {
depth = max (depth, maxdepth(root->children[i]));
depth = max (depth, maxDepth(root->children[i]));
}
return depth + 1;
}
@@ -247,15 +247,15 @@ public:
```CPP
class solution {
public:
int maxdepth(node* root) {
queue<node*> que;
int maxDepth(Node* root) {
queue<Node*> que;
if (root != NULL) que.push(root);
int depth = 0;
while (!que.empty()) {
int size = que.size();
depth++; // 记录深度
for (int i = 0; i < size; i++) {
node* node = que.front();
Node* node = que.front();
que.pop();
for (int j = 0; j < node->children.size(); j++) {
if (node->children[j]) que.push(node->children[j]);
@@ -419,86 +419,107 @@ class solution:
return 1 + max(self.maxdepth(root.left), self.maxdepth(root.right))
```
迭代法:
层序遍历迭代法:
```python
import collections
class solution:
def maxdepth(self, root: treenode) -> int:
# 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 maxDepth(self, root: TreeNode) -> int:
if not root:
return 0
depth = 0 #记录深度
queue = collections.deque()
queue.append(root)
depth = 0
queue = collections.deque([root])
while queue:
size = len(queue)
depth += 1
for i in range(size):
for _ in range(len(queue)):
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:
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
max_depth = 1
for child in root.children:
max_depth = max(max_depth, self.maxDepth(child) + 1)
return max_depth
```
迭代法:
```python
import collections
class solution:
def maxdepth(self, root: 'node') -> int:
queue = collections.deque()
if root:
queue.append(root)
depth = 0 #记录深度
"""
# Definition for a Node.
class Node:
def __init__(self, val=None, children=None):
self.val = val
self.children = children
"""
class Solution:
def maxDepth(self, root: TreeNode) -> int:
if not root:
return 0
depth = 0
queue = collections.deque([root])
while queue:
size = len(queue)
depth += 1
for i in range(size):
for _ in range(len(queue)):
node = queue.popleft()
for j in range(len(node.children)):
if node.children[j]:
queue.append(node.children[j])
for child in node.children:
queue.append(child)
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
"""
# Definition for a Node.
class Node:
def __init__(self, val=None, children=None):
self.val = val
self.children = children
"""
class Solution:
def maxDepth(self, root: 'Node') -> int:
if not root:
return 0
max_depth = 0
stack = [(root, 1)]
while stack:
node, depth = stack.pop()
max_depth = max(max_depth, depth)
for child in node.children:
stack.append((child, depth + 1))
return max_depth
```

View File

@@ -622,7 +622,42 @@ class Solution {
}
}
```
```java
class Solution {
public TreeNode buildTree(int[] inorder, int[] postorder) {
if(postorder.length == 0 || inorder.length == 0)
return null;
return buildHelper(inorder, 0, inorder.length, postorder, 0, postorder.length);
}
private TreeNode buildHelper(int[] inorder, int inorderStart, int inorderEnd, int[] postorder, int postorderStart, int postorderEnd){
if(postorderStart == postorderEnd)
return null;
int rootVal = postorder[postorderEnd - 1];
TreeNode root = new TreeNode(rootVal);
int middleIndex;
for (middleIndex = inorderStart; middleIndex < inorderEnd; middleIndex++){
if(inorder[middleIndex] == rootVal)
break;
}
int leftInorderStart = inorderStart;
int leftInorderEnd = middleIndex;
int rightInorderStart = middleIndex + 1;
int rightInorderEnd = inorderEnd;
int leftPostorderStart = postorderStart;
int leftPostorderEnd = postorderStart + (middleIndex - inorderStart);
int rightPostorderStart = leftPostorderEnd;
int rightPostorderEnd = postorderEnd - 1;
root.left = buildHelper(inorder, leftInorderStart, leftInorderEnd, postorder, leftPostorderStart, leftPostorderEnd);
root.right = buildHelper(inorder, rightInorderStart, rightInorderEnd, postorder, rightPostorderStart, rightPostorderEnd);
return root;
}
}
```
105.从前序与中序遍历序列构造二叉树
```java

View File

@@ -170,11 +170,14 @@ class Solution {
private:
int result;
void getdepth(TreeNode* node, int depth) {
if (node->left == NULL && node->right == NULL) {
result = min(depth, result);
// 函数递归终止条件
if (root == nullptr) {
return;
}
// 中 只不过中没有处理逻辑
// 中处理逻辑:判断是不是叶子结点
if (root -> left == nullptr && root->right == nullptr) {
res = min(res, depth);
}
if (node->left) { // 左
getdepth(node->left, depth + 1);
}
@@ -186,7 +189,9 @@ private:
public:
int minDepth(TreeNode* root) {
if (root == NULL) return 0;
if (root == nullptr) {
return 0;
}
result = INT_MAX;
getdepth(root, 1);
return result;
@@ -300,46 +305,98 @@ class Solution {
递归法:
```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 minDepth(self, root: TreeNode) -> int:
if not root:
return 0
if not root.left and not root.right:
return 1
min_depth = 10**9
left_depth = float('inf')
right_depth = float('inf')
if root.left:
min_depth = min(self.minDepth(root.left), min_depth) # 获得左子树的最小高度
left_depth = self.minDepth(root.left)
if root.right:
min_depth = min(self.minDepth(root.right), min_depth) # 获得右子树的最小高度
return min_depth + 1
right_depth = self.minDepth(root.right)
return 1 + min(left_depth, right_depth)
```
迭代法:
```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 minDepth(self, root: TreeNode) -> int:
if not root:
return 0
que = deque()
que.append(root)
res = 1
while que:
for _ in range(len(que)):
node = que.popleft()
# 当左右孩子都为空的时候,说明是最低点的一层了,退出
depth = 0
queue = collections.deque([root])
while queue:
depth += 1
for _ in range(len(queue)):
node = queue.popleft()
if not node.left and not node.right:
return res
if node.left is not None:
que.append(node.left)
if node.right is not None:
que.append(node.right)
res += 1
return res
return depth
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
return depth
```
迭代法:
```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 minDepth(self, root: TreeNode) -> int:
if not root:
return 0
queue = collections.deque([(root, 1)])
while queue:
node, depth = queue.popleft()
# Check if the node is a leaf node
if not node.left and not node.right:
return depth
# Add left and right child to the queue
if node.left:
queue.append((node.left, depth+1))
if node.right:
queue.append((node.right, depth+1))
return 0
```
## Go

View File

@@ -17,7 +17,7 @@
示例:
给定如下二叉树,以及目标和 sum = 22
![112.路径总和1](https://img-blog.csdnimg.cn/20210203160355234.png)
![](https://code-thinking-1253855093.file.myqcloud.com/pics/20230407210247.png)
返回 true, 因为存在目标和为 22 的根节点到叶子节点的路径 5->4->11->2。
@@ -250,7 +250,7 @@ private:
vector<vector<int>> result;
vector<int> path;
// 递归函数不需要返回值,因为我们要遍历整个树
void traversal(treenode* cur, int count) {
void traversal(TreeNode* cur, int count) {
if (!cur->left && !cur->right && count == 0) { // 遇到了叶子节点且找到了和为sum的路径
result.push_back(path);
return;
@@ -276,10 +276,10 @@ private:
}
public:
vector<vector<int>> pathsum(treenode* root, int sum) {
vector<vector<int>> pathSum(TreeNode* root, int sum) {
result.clear();
path.clear();
if (root == null) return result;
if (root == NULL) return result;
path.push_back(root->val); // 把根节点放进路径
traversal(root, sum - root->val);
return result;
@@ -385,6 +385,42 @@ class solution {
}
}
```
```Java 統一迭代法
public boolean hasPathSum(TreeNode root, int targetSum) {
Stack<TreeNode> treeNodeStack = new Stack<>();
Stack<Integer> sumStack = new Stack<>();
if(root == null)
return false;
treeNodeStack.add(root);
sumStack.add(root.val);
while(!treeNodeStack.isEmpty()){
TreeNode curr = treeNodeStack.peek();
int tempsum = sumStack.pop();
if(curr != null){
treeNodeStack.pop();
treeNodeStack.add(curr);
treeNodeStack.add(null);
sumStack.add(tempsum);
if(curr.right != null){
treeNodeStack.add(curr.right);
sumStack.add(tempsum + curr.right.val);
}
if(curr.left != null){
treeNodeStack.add(curr.left);
sumStack.add(tempsum + curr.left.val);
}
}else{
treeNodeStack.pop();
TreeNode temp = treeNodeStack.pop();
if(temp.left == null && temp.right == null && tempsum == targetSum)
return true;
}
}
return false;
}
```
### 0113.路径总和-ii

View File

@@ -149,6 +149,11 @@ public:
};
```
* 时间复杂度: O(n * m)
* 空间复杂度: O(n * m)
## 其他语言版本

View File

@@ -289,7 +289,45 @@ class Solution:
## Go
```go
func minCut(s string) int {
isValid := make([][]bool, len(s))
for i := 0; i < len(isValid); i++ {
isValid[i] = make([]bool, len(s))
isValid[i][i] = true
}
for i := len(s) - 1; i >= 0; i-- {
for j := i + 1; j < len(s); j++ {
if s[i] == s[j] && (isValid[i + 1][j - 1] || j - i == 1) {
isValid[i][j] = true
}
}
}
dp := make([]int, len(s))
for i := 0; i < len(s); i++ {
dp[i] = math.MaxInt
}
for i := 0; i < len(s); i++ {
if isValid[0][i] {
dp[i] = 0
continue
}
for j := 0; j < i; j++ {
if isValid[j + 1][i] {
dp[i] = min(dp[i], dp[j] + 1)
}
}
}
return dp[len(s) - 1]
}
func min(i, j int) int {
if i < j {
return i
} else {
return j
}
}
```
## JavaScript

View File

@@ -121,6 +121,11 @@ public:
};
```
* 时间复杂度: O(n)
* 空间复杂度: O(n)
## 总结
这在leetcode上是一道困难的题目其难点就在于贪心的策略如果在考虑局部的时候想两边兼顾就会顾此失彼。

View File

@@ -351,7 +351,17 @@ class Solution:
dp[j] = dp[j] or (dp[j - len(word)] and word == s[j - len(word):j])
return dp[len(s)]
```
```python
class Solution: # 和视频中写法一致和最上面C++写法一致)
def wordBreak(self, s: str, wordDict: List[str]) -> bool:
dp = [False]*(len(s)+1)
dp[0]=True
for j in range(1,len(s)+1):
for i in range(j):
word = s[i:j]
if word in wordDict and dp[i]: dp[j]=True
return dp[-1]
```

View File

@@ -221,25 +221,55 @@ 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 detectCycle(self, head: ListNode) -> ListNode:
slow, fast = head, head
slow = head
fast = head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
# 如果相遇
# If there is a cycle, the slow and fast pointers will eventually meet
if slow == fast:
p = head
q = slow
while p!=q:
p = p.next
q = q.next
#你也可以return q
return p
# Move one of the pointers back to the start of the list
slow = head
while slow != fast:
slow = slow.next
fast = fast.next
return slow
# If there is no cycle, return None
return None
```
```python
版本二集合法
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def detectCycle(self, head: ListNode) -> ListNode:
visited = set()
while head:
if head in visited:
return head
visited.add(head)
head = head.next
return None
```
Go
```go

View File

@@ -970,6 +970,49 @@ pub fn remove_extra_spaces(s: &mut Vec<char>) {
}
}
```
C:
```C
// 翻转字符串中指定范围的字符
void reverse(char* s, int start, int end) {
for (int i = start, j = end; i < j; i++, j--) {
int tmp = s[i];
s[i] = s[j];
s[j] = tmp;
}
}
// 删除字符串两端和中间多余的空格
void removeExtraSpace(char* s) {
int start = 0; // 指向字符串开头的指针
int end = strlen(s) - 1; // 指向字符串结尾的指针
while (s[start] == ' ') start++; // 移动指针 start直到找到第一个非空格字符
while (s[end] == ' ') end--; // 移动指针 end直到找到第一个非空格字符
int slow = 0; // 指向新字符串的下一个写入位置的指针
for (int i = start; i <= end; i++) { // 遍历整个字符串
if (s[i] == ' ' && s[i+1] == ' ') { // 如果当前字符是空格,并且下一个字符也是空格,则跳过
continue;
}
s[slow] = s[i]; // 否则,将当前字符复制到新字符串的 slow 位置
slow++; // 将 slow 指针向后移动
}
s[slow] = '\0'; // 在新字符串的末尾添加一个空字符
}
// 翻转字符串中的单词
char * reverseWords(char * s){
removeExtraSpace(s); // 先删除字符串两端和中间的多余空格
reverse(s, 0, strlen(s) - 1); // 翻转整个字符串
int slow = 0; // 指向每个单词的开头位置的指针
for (int i = 0; i <= strlen(s); i++) { // 遍历整个字符串
if (s[i] ==' ' || s[i] == '\0') { // 如果当前字符是空格或空字符,说明一个单词结束了
reverse(s, slow, i-1); // 翻转单词
slow = i + 1; // 将 slow 指针指向下一个单词的开头位置
}
}
return s; // 返回处理后的字符串
}
```
<p align="center">
<a href="https://programmercarl.com/other/kstar.html" target="_blank">

View File

@@ -156,6 +156,11 @@ public:
};
```
* 时间复杂度: O(n * k),其中 n 为 prices 的长度
* 空间复杂度: O(n * k)
当然有的解法是定义一个三维数组dp[i][j][k]第i天第j次买卖k表示买还是卖的状态从定义上来讲是比较直观。
但感觉三维数组操作起来有些麻烦,我是直接用二维数组来模拟三维数组的情况,代码看起来也清爽一些。
@@ -323,6 +328,42 @@ func max(a, b int) int {
}
```
版本二: 三维 dp数组
```go
func maxProfit(k int, prices []int) int {
length := len(prices)
if length == 0 {
return 0
}
// [天数][交易次数][是否持有股票]
// 1表示不持有/卖出, 0表示持有/买入
dp := make([][][]int, length)
for i := 0; i < length; i++ {
dp[i] = make([][]int, k+1)
for j := 0; j <= k; j++ {
dp[i][j] = make([]int, 2)
}
}
for j := 0; j <= k; j++ {
dp[0][j][0] = -prices[0]
}
for i := 1; i < length; i++ {
for j := 1; j <= k; j++ {
dp[i][j][0] = max188(dp[i-1][j][0], dp[i-1][j-1][1]-prices[i])
dp[i][j][1] = max188(dp[i-1][j][1], dp[i-1][j][0]+prices[i])
}
}
return dp[length-1][k][1]
}
func max188(a, b int) int {
if a > b {
return a
}
return b
}
```
Javascript:
```javascript

View File

@@ -108,6 +108,9 @@ public:
};
```
* 时间复杂度: O(n)
* 空间复杂度: O(n)
## 总结
打家劫舍是DP解决的经典题目这道题也是打家劫舍入门级题目后面我们还会变种方式来打劫的。
@@ -150,7 +153,17 @@ class Solution:
dp[i] = max(dp[i-2]+nums[i], dp[i-1])
return dp[-1]
```
```python
class Solution: # 二维dp数组写法
def rob(self, nums: List[int]) -> int:
dp = [[0,0] for _ in range(len(nums))]
dp[0][1] = nums[0]
for i in range(1,len(nums)):
dp[i][0] = max(dp[i-1][1],dp[i-1][0])
dp[i][1] = dp[i-1][0]+nums[i]
print(dp)
return max(dp[-1])
```
Go
```Go
func rob(nums []int) int {

View File

@@ -307,21 +307,27 @@ 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节点
def removeElements(self, head: Optional[ListNode], val: int) -> Optional[ListNode]:
# 创建虚拟头部节点以简化删除过程
dummy_head = ListNode(next = head)
# 遍历列表并删除值为val的节点
current = dummy_head
while current.next:
if current.next.val == val:
current.next = current.next.next
else:
cur = cur.next
current = current.next
return dummy_head.next
```
Go

View File

@@ -193,9 +193,9 @@ class Solution {
}
```
Python迭代法:
Python
```python
#双指针
版本一双指针
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
@@ -205,7 +205,7 @@ class Solution:
def reverseList(self, head: ListNode) -> ListNode:
cur = head
pre = None
while(cur!=None):
while cur:
temp = cur.next # 保存一下 cur的下一个节点因为接下来要改变cur->next
cur.next = pre #反转
#更新pre、cur指针
@@ -217,6 +217,7 @@ class Solution:
Python递归法
```python
版本二递归法
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
@@ -224,36 +225,17 @@ Python递归法
# self.next = next
class Solution:
def reverseList(self, head: ListNode) -> ListNode:
def reverse(pre,cur):
if not cur:
return pre
tmp = cur.next
cur.next = pre
return reverse(cur,tmp)
return reverse(None,head)
return self.reverse(head, None)
def reverse(self, cur: ListNode, pre: ListNode) -> ListNode:
if cur == None:
return pre
temp = cur.next
cur.next = pre
return self.reverse(temp, cur)
```
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: Optional[ListNode]) -> Optional[ListNode]:
if not head or not head.next: return head
p = self.reverseList(head.next)
head.next.next = head
head.next = None
return p
```
Go

View File

@@ -173,18 +173,44 @@ class Solution {
Python
```python
版本一滑动窗口法
class Solution:
def minSubArrayLen(self, s: int, nums: List[int]) -> int:
res = float("inf") # 定义一个无限大的数
Sum = 0 # 滑动窗口数值之和
i = 0 # 滑动窗口起始位置
for j in range(len(nums)):
Sum += nums[j]
while Sum >= s:
res = min(res, j-i+1)
Sum -= nums[i]
i += 1
return 0 if res == float("inf") else res
l = len(nums)
left = 0
right = 0
min_len = float('inf')
cur_sum = 0 #当前的累加值
while right < l:
cur_sum += nums[right]
while cur_sum >= s: # 当前累加值大于目标值
min_len = min(min_len, right - left + 1)
cur_sum -= nums[left]
left += 1
right += 1
return min_len if min_len != float('inf') else 0
```
```python
版本二暴力法
class Solution:
def minSubArrayLen(self, s: int, nums: List[int]) -> int:
l = len(nums)
min_len = float('inf')
for i in range(l):
cur_sum = 0
for j in range(i, l):
cur_sum += nums[j]
if cur_sum >= s:
min_len = min(min_len, j - i + 1)
break
return min_len if min_len != float('inf') else 0
```
Go

View File

@@ -82,6 +82,11 @@ public:
};
```
* 时间复杂度: O(n)
* 空间复杂度: O(n)
## 总结
成环之后还是难了一些的, 不少题解没有把“考虑房间”和“偷房间”说清楚。
@@ -142,7 +147,20 @@ class Solution:
dp[i]=max(dp[i-1],dp[i-2]+nums[i])
return dp[-1]
```
```python
class Solution: # 二维dp数组写法
def rob(self, nums: List[int]) -> int:
if len(nums)<3: return max(nums)
return max(self.default(nums[:-1]),self.default(nums[1:]))
def default(self,nums):
dp = [[0,0] for _ in range(len(nums))]
dp[0][1] = nums[0]
for i in range(1,len(nums)):
dp[i][0] = max(dp[i-1])
dp[i][1] = dp[i-1][0] + nums[i]
return max(dp[-1])
```
Go
```go

View File

@@ -393,6 +393,20 @@ class Solution: # 利用完全二叉树特性
return 2**count-1
return 1+self.countNodes(root.left)+self.countNodes(root.right)
```
完全二叉树写法3
```python
class Solution: # 利用完全二叉树特性
def countNodes(self, root: TreeNode) -> int:
if not root: return 0
count = 0
left = root.left; right = root.right
while left and right:
count+=1
left = left.left; right = right.right
if not left and not right: # 如果同时到底说明是满二叉树,反之则不是
return (2<<count)-1
return 1+self.countNodes(root.left)+self.countNodes(root.right)
```
## Go

View File

@@ -314,81 +314,158 @@ class Solution {
递归法:前序遍历:
```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 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) #右
root.left, root.right = root.right, root.left
self.invertTree(root.left)
self.invertTree(root.right)
return root
```
递归法:序遍历:
迭代法:序遍历:
```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 invertTree(self, root: TreeNode) -> TreeNode:
if root is None:
if not root:
return None
stack = [root]
while stack:
node = stack.pop()
node.left, node.right = node.right, node.left
if node.left:
stack.append(node.left)
if node.right:
stack.append(node.right)
return root
```
递归法:中序遍历:
```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 invertTree(self, root: TreeNode) -> TreeNode:
if not root:
return None
self.invertTree(root.left)
root.left, root.right = root.right, root.left
self.invertTree(root.left)
return root
```
迭代法:中序遍历:
```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 invertTree(self, root: TreeNode) -> TreeNode:
if not root:
return None
stack = [root]
while stack:
node = stack.pop()
if node.left:
stack.append(node.left)
node.left, node.right = node.right, node.left
if node.left:
stack.append(node.left)
return root
```
递归法:后序遍历:
```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 invertTree(self, root: TreeNode) -> TreeNode:
if not root:
return None
self.invertTree(root.left)
self.invertTree(root.right)
root.left, root.right = root.right, root.left
return root
return root
```
迭代法:深度优先遍历(前序遍历
迭代法:序遍历:
```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 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) #右
return None
stack = [root]
while stack:
node = stack.pop()
if node.left:
st.append(node.left) #左
stack.append(node.left)
if node.right:
stack.append(node.right)
node.left, node.right = node.right, node.left
return root
```
迭代法:广度优先遍历(层序遍历):
```python
import collections
# 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 invertTree(self, root: TreeNode) -> TreeNode:
queue = collections.deque() #使用deque()
if root:
queue.append(root)
if not root:
return None
queue = collections.deque([root])
while queue:
size = len(queue)
for i in range(size):
for i in range(len(queue)):
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
```
迭代法:广度优先遍历(层序遍历),和之前的层序遍历写法一致:
```python
class Solution:
def invertTree(self, root: Optional[TreeNode]) -> Optional[TreeNode]:
if not root: return root
from collections import deque
que=deque([root])
while que:
size=len(que)
for i in range(size):
cur=que.popleft()
cur.left, cur.right = cur.right, cur.left
if cur.left: que.append(cur.left)
if cur.right: que.append(cur.right)
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
递归版本的前序遍历

View File

@@ -205,6 +205,19 @@ var isAnagram = function(s, t) {
}
return true;
};
var isAnagram = function(s, t) {
if(s.length !== t.length) return false;
let char_count = new Map();
for(let item of s) {
char_count.set(item, (char_count.get(item) || 0) + 1) ;
}
for(let item of t) {
if(!char_count.get(item)) return false;
char_count.set(item, char_count.get(item)-1);
}
return true;
};
```
TypeScript

View File

@@ -127,6 +127,10 @@ public:
};
```
* 时间复杂度: O(n * √n)
* 空间复杂度: O(n)
同样我在给出先遍历物品在遍历背包的代码一样的可以AC的。
```CPP
@@ -145,6 +149,8 @@ public:
}
};
```
* 同上
## 总结

View File

@@ -106,6 +106,10 @@ public:
}
};
```
* 时间复杂度: O(n^2)
* 空间复杂度: O(n)
## 总结

View File

@@ -133,6 +133,11 @@ public:
};
```
* 时间复杂度: O(n * amount),其中 n 为 coins 的长度
* 空间复杂度: O(amount)
对于遍历方式遍历背包放在外循环,遍历物品放在内循环也是可以的,我就直接给出代码了
```CPP
@@ -154,6 +159,8 @@ public:
}
};
```
* 同上
## 总结

View File

@@ -380,7 +380,33 @@ class Solution:
backtracking("JFK")
return path
```
python - 使用used数组 - 神似之前几题写法
```python
class Solution:
def findItinerary(self, tickets: List[List[str]]) -> List[str]:
global used,path,results
used = [0]*len(tickets)
path = ['JFK']
results = []
tickets.sort() # 先排序,这样一旦找到第一个可行路径,一定是字母排序最小的
self.backtracking(tickets,'JFK')
return results[0]
def backtracking(self,tickets,cur):
if sum(used) == len(tickets):
results.append(path[:])
return True # 只要找到就返回
for i in range(len(tickets)):
if tickets[i][0]==cur and used[i]==0:
used[i]=1
path.append(tickets[i][1])
state = self.backtracking(tickets,tickets[i][1])
path.pop()
used[i]=0
if state: return True # 只要找到就返回,不继续搜索了
```
### GO
```go
type pair struct {

View File

@@ -89,7 +89,7 @@
### 情况二:数组首尾两端
所以本题统计峰值的时候,数组最左面和最右面如统计呢?
所以本题统计峰值的时候,数组最左面和最右面如统计呢?
题目中说了,如果只有两个不同的元素,那摆动序列也是 2。
@@ -655,3 +655,4 @@ object Solution {
<a href="https://programmercarl.com/other/kstar.html" target="_blank">
<img src="../pics/网站星球宣传海报.jpg" width="1000"/>
</a>

View File

@@ -1,145 +0,0 @@
# 完全背包的排列问题模拟
#### Problem
1. 排列问题是完全背包中十分棘手的问题。
2. 其在迭代过程中需要先迭代背包容量,再迭代物品个数,使得其在代码理解上较难入手。
#### Contribution
本文档以力扣上[组合总和IV](https://leetcode.cn/problems/combination-sum-iv/)为例提供一个二维dp的代码例子并提供模拟过程以便于理解
#### Code
```cpp
int combinationSum4(vector<int>& nums, int target) {
// 定义背包容量为target物品个数为nums.size()的dp数组
// dp[i][j]表示将第0-i个物品添加入排列中和为j的排列方式
vector<vector<int>> dp (nums.size(), vector(target+1,0));
// 表示有0,1,...,n个物品可选择的情况下和为0的选择方法为1什么都不取
for(int i = 0; i < nums.size(); i++) dp[i][0] = 1;
// 必须按列遍历,因为右边数组需要知道左边数组最低部的信息(排列问题)
// 后面的模拟可以更清楚的表现这么操作的原因
for(int i = 1; i <= target; i++){
for(int j = 0; j < nums.size(); j++){
// 只有nums[j]可以取的情况
if(j == 0){
if(nums[j] > i) dp[j][i] = 0;
// 如果背包容量放不下 那么此时没有排列方式
else dp[j][i] = dp[nums.size()-1][i-nums[j]];
// 如果背包容量放的下 全排列方式为dp[最底层][容量-该物品容量]排列方式后面放一个nums[j]
}
// 有多个nums数可以取
else{
// 如果背包容量放不下 那么沿用0-j-1个物品的排列方式
if(nums[j] > i) dp[j][i] = dp[j-1][i];
// 如果背包容量放得下 在dp[最底层][容量-该物品容量]排列方式后面放一个nums[j]后面放个nums[j]
// INT_MAX避免溢出
else if(i >= nums[j] && dp[j-1][i] < INT_MAX - dp[nums.size()-1][i-nums[j]])
dp[j][i] = dp[j-1][i] + dp[nums.size()-1][i-nums[j]];
}
}
}
// 打印dp数组
for(int i = 0; i < nums.size(); i++){
for(int j = 0; j <= target; j++){
cout<<dp[i][j]<<" ";
}
cout<<endl;
}
return dp[nums.size()-1][target];
}
```
#### Simulation
##### 样例 nums = [2,3,4], target = 6
##### 1. 初始化一个3x7的dp数组
1 0 0 0 0 0 0
1 0 0 0 0 0 0
1 0 0 0 0 0 0
dp\[0-2\]\[0\] = 1含义是有nums[0-2]物品时使得背包容量为0的取法为1作用是在取到nums[i]物品使得背包容量为nums[i]时取法为1。
##### 2.迭代方式
必须列优先,因为右边的数组在迭代时需要最左下的数组最终结果。
##### 3. 模拟过程
i = 1, j = 0 dp\[0\]\[1\] = 0表示在物品集合{2}中无法组成和为1
i = 1, j = 1 dp\[1\]\[1\] = 0表示在物品集合{2,3}中无法组成和为1
i = 1, j = 2 dp\[2\]\[1\] = 0表示在物品集合{2,3,4}中无法组成和为1
1 0 0 0 0 0 0
1 0 0 0 0 0 0
1 **0** 0 0 0 0 0
此时dp\[2\]\[1\]作为第1列最底部的元素表示所有物品都有的情况下组成和为1的排列方式为0
————————————————————————————
i = 2, j = 0 dp\[0\]\[2\] = 1表示在物品集合{2}中取出和为2的排列有{2}
i = 2, j = 1 dp\[1\]\[2\] = 1表示在物品集合{2,3}中取出和为2的排列有{2}
i = 2, j = 2 dp\[2\]\[2\] = 1表示在物品集合{2,3,4}中取出和为2的排列有{2}
1 0 1 0 0 0 0
1 0 1 0 0 0 0
1 0 **1** 0 0 0 0
此时dp\[2\]\[2\]作为第2列最底部的元素表示所有物品都有的情况下和为2的排列方式有1个 类比成一维dp即dp[2]=dp[0]
————————————————————————————
i = 3, j = 0 dp\[0\]\[3\] = 0表示在物品集合{2}中无法取出和为3
i = 3, j = 1 dp\[1\]\[3\] = 1表示在物品集合{2,3}中取出和为3的排列有{3}
i = 3, j = 2 dp\[2\]\[3\] = 1表示在物品集合{2,3,4}中取出和为3的排列有{3}
1 0 1 0 0 0 0
1 0 1 1 0 0 0
1 0 1 **1** 0 0 0
此时dp\[2\]\[3\]作为第3列最底部的元素表示所有物品都有的情况下和为3的排列方式有1个类比成一维dp即dp[3]=dp[0]
————————————————————————————
i = 4, j = 0 dp\[0\]\[4\] = 1表示在物品集合{2}中取出和为4的排列有在原有的排列{2}后添加一个2成为{2,2}从第2列底部信息继承获得
i = 4, j = 1 dp\[1\]\[4\] = 1表示在物品集合{2,3}中取出和为4的排列有{2,2}
i = 4, j = 2 dp\[2\]\[4\] = 2表示在物品集合{2,3,4}中取出和为4的排列有{{2,2},{4}}{2,2}的信息从该列头上获得)
1 0 1 0 1 0 0
1 0 1 1 1 0 0
1 0 1 1 **2** 0 0
此时dp\[2\]\[4\]作为第4列最底部的元素表示所有物品都有的情况下和为4的排列方式有2个
————————————————————————————
i = 5, j = 0 dp\[0\]\[5\] = 1表示在物品集合{2}中取出和为5的排列有{3,2} **(3的信息由dp[2]\[3]获得即将2放在3的右边)**
i = 5, j = 1 dp\[1\]\[5\] = 2表示在物品集合{2,3}中取出和为5的排列有{{2,3},{3,2}} **({3,2}由上一行信息继承,{2,3}是从dp[2] [2]获得将3放在2的右边)**
i = 5, j = 2 dp\[2\]\[5\] = 2表示在物品集合{2,3,4}中取出和为5的排列有{{2,3},{3,2}}
1 0 1 0 1 1 0
1 0 1 1 1 2 0
1 0 1 1 2 **2** 0
此时dp\[2\]\[5\]作为第5列最底部的元素表示所有物品都有的情况下和为5的排列方式有2个
————————————————————————————
i = 6, j = 0 dp\[0\]\[6\] = 2表示在物品集合{2}中取出和为6的排列有{{2,2,2},{4,2}} **(信息由dp[2]\[4]获得即将2放在{2,2}和{4}的右边)**
i = 6, j = 1 dp\[1\]\[6\] = 3表示在物品集合{2,3}中取出和为6的排列有{{2,2,2},{4,2},{3,3}} **({2,2,2},{4,2}由上一行信息继承,{3,3}是从dp[2] [3]获得将3放在3的右边)**
i = 6, j = 2 dp\[2\]\[6\] = 4表示在物品集合{2,3,4}中取出和为6的排列有{{2,2,2},{4,2},{3,3},{2,4}} **({2,2,2},{4,2},{3,3}由上一行继承,{2,4}从dp[2]获得将4放在2的右边)**
1 0 1 0 1 1 2
1 0 1 1 1 2 3
1 0 1 1 2 2 **4**
此时dp\[2\]\[6\]作为第6列最底部的元素表示所有物品都有的情况下和为6的排列方式有4个为{2,2,2}{4,2}{3,3}{2,4}。

View File

@@ -1,145 +0,0 @@
# 完全背包的排列问题模拟
#### Problem
1. 排列问题是完全背包中十分棘手的问题。
2. 其在迭代过程中需要先迭代背包容量,再迭代物品个数,使得其在代码理解上较难入手。
#### Contribution
本文档以力扣上[组合总和IV](https://leetcode.cn/problems/combination-sum-iv/)为例提供一个二维dp的代码例子并提供模拟过程以便于理解
#### Code
```cpp
int combinationSum4(vector<int>& nums, int target) {
// 定义背包容量为target物品个数为nums.size()的dp数组
// dp[i][j]表示将第0-i个物品添加入排列中和为j的排列方式
vector<vector<int>> dp (nums.size(), vector(target+1,0));
// 表示有0,1,...,n个物品可选择的情况下和为0的选择方法为1什么都不取
for(int i = 0; i < nums.size(); i++) dp[i][0] = 1;
// 必须按列遍历,因为右边数组需要知道左边数组最低部的信息(排列问题)
// 后面的模拟可以更清楚的表现这么操作的原因
for(int i = 1; i <= target; i++){
for(int j = 0; j < nums.size(); j++){
// 只有nums[j]可以取的情况
if(j == 0){
if(nums[j] > i) dp[j][i] = 0;
// 如果背包容量放不下 那么此时没有排列方式
else dp[j][i] = dp[nums.size()-1][i-nums[j]];
// 如果背包容量放的下 全排列方式为dp[最底层][容量-该物品容量]排列方式后面放一个nums[j]
}
// 有多个nums数可以取
else{
// 如果背包容量放不下 那么沿用0-j-1个物品的排列方式
if(nums[j] > i) dp[j][i] = dp[j-1][i];
// 如果背包容量放得下 在dp[最底层][容量-该物品容量]排列方式后面放一个nums[j]后面放个nums[j]
// INT_MAX避免溢出
else if(i >= nums[j] && dp[j-1][i] < INT_MAX - dp[nums.size()-1][i-nums[j]])
dp[j][i] = dp[j-1][i] + dp[nums.size()-1][i-nums[j]];
}
}
}
// 打印dp数组
for(int i = 0; i < nums.size(); i++){
for(int j = 0; j <= target; j++){
cout<<dp[i][j]<<" ";
}
cout<<endl;
}
return dp[nums.size()-1][target];
}
```
#### Simulation
##### 样例 nums = [2,3,4], target = 6
##### 1. 初始化一个3x7的dp数组
1 0 0 0 0 0 0
1 0 0 0 0 0 0
1 0 0 0 0 0 0
dp\[0-2\]\[0\] = 1含义是有nums[0-2]物品时使得背包容量为0的取法为1作用是在取到nums[i]物品使得背包容量为nums[i]时取法为1。
##### 2.迭代方式
必须列优先,因为右边的数组在迭代时需要最左下的数组最终结果。
##### 3. 模拟过程
i = 1, j = 0 dp\[0\]\[1\] = 0表示在物品集合{2}中无法组成和为1
i = 1, j = 1 dp\[1\]\[1\] = 0表示在物品集合{2,3}中无法组成和为1
i = 1, j = 2 dp\[2\]\[1\] = 0表示在物品集合{2,3,4}中无法组成和为1
1 0 0 0 0 0 0
1 0 0 0 0 0 0
1 **0** 0 0 0 0 0
此时dp\[2\]\[1\]作为第1列最底部的元素表示所有物品都有的情况下组成和为1的排列方式为0
————————————————————————————
i = 2, j = 0 dp\[0\]\[2\] = 1表示在物品集合{2}中取出和为2的排列有{2}
i = 2, j = 1 dp\[1\]\[2\] = 1表示在物品集合{2,3}中取出和为2的排列有{2}
i = 2, j = 2 dp\[2\]\[2\] = 1表示在物品集合{2,3,4}中取出和为2的排列有{2}
1 0 1 0 0 0 0
1 0 1 0 0 0 0
1 0 **1** 0 0 0 0
此时dp\[2\]\[2\]作为第2列最底部的元素表示所有物品都有的情况下和为2的排列方式有1个 类比成一维dp即dp[2]=dp[0]
————————————————————————————
i = 3, j = 0 dp\[0\]\[3\] = 0表示在物品集合{2}中无法取出和为3
i = 3, j = 1 dp\[1\]\[3\] = 1表示在物品集合{2,3}中取出和为3的排列有{3}
i = 3, j = 2 dp\[2\]\[3\] = 1表示在物品集合{2,3,4}中取出和为3的排列有{3}
1 0 1 0 0 0 0
1 0 1 1 0 0 0
1 0 1 **1** 0 0 0
此时dp\[2\]\[3\]作为第3列最底部的元素表示所有物品都有的情况下和为3的排列方式有1个类比成一维dp即dp[3]=dp[0]
————————————————————————————
i = 4, j = 0 dp\[0\]\[4\] = 1表示在物品集合{2}中取出和为4的排列有在原有的排列{2}后添加一个2成为{2,2}从第2列底部信息继承获得
i = 4, j = 1 dp\[1\]\[4\] = 1表示在物品集合{2,3}中取出和为4的排列有{2,2}
i = 4, j = 2 dp\[2\]\[4\] = 2表示在物品集合{2,3,4}中取出和为4的排列有{{2,2},{4}}{2,2}的信息从该列头上获得)
1 0 1 0 1 0 0
1 0 1 1 1 0 0
1 0 1 1 **2** 0 0
此时dp\[2\]\[4\]作为第4列最底部的元素表示所有物品都有的情况下和为4的排列方式有2个
————————————————————————————
i = 5, j = 0 dp\[0\]\[5\] = 1表示在物品集合{2}中取出和为5的排列有{3,2} **(3的信息由dp[2]\[3]获得即将2放在3的右边)**
i = 5, j = 1 dp\[1\]\[5\] = 2表示在物品集合{2,3}中取出和为5的排列有{{2,3},{3,2}} **({3,2}由上一行信息继承,{2,3}是从dp[2] [2]获得将3放在2的右边)**
i = 5, j = 2 dp\[2\]\[5\] = 2表示在物品集合{2,3,4}中取出和为5的排列有{{2,3},{3,2}}
1 0 1 0 1 1 0
1 0 1 1 1 2 0
1 0 1 1 2 **2** 0
此时dp\[2\]\[5\]作为第5列最底部的元素表示所有物品都有的情况下和为5的排列方式有2个
————————————————————————————
i = 6, j = 0 dp\[0\]\[6\] = 2表示在物品集合{2}中取出和为6的排列有{{2,2,2},{4,2}} **(信息由dp[2]\[4]获得即将2放在{2,2}和{4}的右边)**
i = 6, j = 1 dp\[1\]\[6\] = 3表示在物品集合{2,3}中取出和为6的排列有{{2,2,2},{4,2},{3,3}} **({2,2,2},{4,2}由上一行信息继承,{3,3}是从dp[2] [3]获得将3放在3的右边)**
i = 6, j = 2 dp\[2\]\[6\] = 4表示在物品集合{2,3,4}中取出和为6的排列有{{2,2,2},{4,2},{3,3},{2,4}} **({2,2,2},{4,2},{3,3}由上一行继承,{2,4}从dp[2]获得将4放在2的右边)**
1 0 1 0 1 1 2
1 0 1 1 1 2 3
1 0 1 1 2 2 **4**
此时dp\[2\]\[6\]作为第6列最底部的元素表示所有物品都有的情况下和为6的排列方式有4个为{2,2,2}{4,2}{3,3}{2,4}。

View File

@@ -130,6 +130,11 @@ public:
```
* 时间复杂度: O(target * n),其中 n 为 nums 的长度
* 空间复杂度: O(target)
C++测试用例有两个数相加超过int的数据所以需要在if里加上dp[i] < INT_MAX - dp[i - num]。
但java就不用考虑这个限制java里的int也是四个字节吧也有可能leetcode后台对不同语言的测试数据不一样

View File

@@ -117,6 +117,10 @@ Java
```Java
class Solution {
public boolean canConstruct(String ransomNote, String magazine) {
// shortcut
if (ransomNote.length() > magazine.length()) {
return false;
}
// 定义一个哈希映射数组
int[] record = new int[26];

View File

@@ -399,18 +399,20 @@ object Solution {
```Rust
impl Solution {
pub fn erase_overlap_intervals(intervals: Vec<Vec<i32>>) -> i32 {
if intervals.len() == 0 { return 0; }
let mut intervals = intervals;
intervals.sort_by(|a, b| a[1].cmp(&b[1]));
if intervals.is_empty() {
return 0;
}
intervals.sort_by_key(|interval| interval[1]);
let mut count = 1;
let mut end = intervals[0][1];
for i in 1..intervals.len() {
if end <= intervals[i][0] {
end = intervals[i][1];
for v in intervals.iter().skip(1) {
if end <= v[0] {
end = v[1];
count += 1;
}
}
intervals.len() as i32 - count
(intervals.len() - count) as i32
}
}
```

View File

@@ -177,7 +177,23 @@ class Solution:
points[i][1] = min(points[i - 1][1], points[i][1]) # 更新重叠气球最小右边界
return result
```
```python
class Solution: # 不改变原数组
def findMinArrowShots(self, points: List[List[int]]) -> int:
points.sort(key = lambda x: x[0])
sl,sr = points[0][0],points[0][1]
count = 1
for i in points:
if i[0]>sr:
count+=1
sl,sr = i[0],i[1]
else:
sl = max(sl,i[0])
sr = min(sr,i[1])
return count
```
### Go
```go
func findMinArrowShots(points [][]int) int {

View File

@@ -102,21 +102,14 @@ class Solution {
//统计两个数组中的元素之和同时统计出现的次数放入map
for (int i : nums1) {
for (int j : nums2) {
int tmp = map.getOrDefault(i + j, 0);
if (tmp == 0) {
map.put(i + j, 1);
} else {
map.replace(i + j, tmp + 1);
}
int sum = i + j;
map.put(sum, map.getOrDefault(sum, 0) + 1);
}
}
//统计剩余的两个元素的和在map中找是否存在相加为0的情况同时记录次数
for (int i : nums3) {
for (int j : nums4) {
int tmp = map.getOrDefault(0 - i - j, 0);
if (tmp != 0) {
res += tmp;
}
res += map.getOrDefault(0 - i - j, 0);
}
}
return res;

View File

@@ -48,7 +48,7 @@
如图:
![](https://code-thinking-1253855093.file.myqcloud.com/pics/20230203105634.png)
![](https://code-thinking-1253855093.file.myqcloud.com/pics/20230405225628.png)
这个例子可以看出饼干 9 只有喂给胃口为 7 的小孩,这样才是整体最优解,并想不出反例,那么就可以撸代码了。
@@ -56,8 +56,6 @@ C++代码整体如下:
```CPP
// 版本一
// 时间复杂度O(nlogn)
// 空间复杂度O(1)
class Solution {
public:
int findContentChildren(vector<int>& g, vector<int>& s) {
@@ -75,6 +73,9 @@ public:
}
};
```
* 时间复杂度O(nlogn)
* 空间复杂度O(1)
从代码中可以看出我用了一个 index 来控制饼干数组的遍历,遍历饼干并没有再起一个 for 循环,而是采用自减的方式,这也是常用的技巧。
@@ -118,6 +119,9 @@ public:
}
};
```
* 时间复杂度O(nlogn)
* 空间复杂度O(1)
细心的录友可以发现,这种写法,两个循环的顺序改变了,先遍历的饼干,在遍历的胃口,这是因为遍历顺序变了,我们是从小到大遍历。

View File

@@ -156,6 +156,11 @@ public:
};
```
* 时间复杂度: O(kmn)k 为strs的长度
* 空间复杂度: O(mn)
## 总结
不少同学刷过这道题,可能没有总结这究竟是什么背包。

View File

@@ -347,26 +347,32 @@ int fib(int n){
### Rust
动态规划:
```Rust
pub fn fib(n: i32) -> i32 {
let n = n as usize;
let mut dp = vec![0; 31];
dp[1] = 1;
for i in 2..=n {
dp[i] = dp[i - 1] + dp[i - 2];
impl Solution {
pub fn fib(n: i32) -> i32 {
if n <= 1 {
return n;
}
let n = n as usize;
let mut dp = vec![0; n + 1];
dp[1] = 1;
for i in 2..=n {
dp[i] = dp[i - 2] + dp[i - 1];
}
dp[n]
}
dp[n]
}
```
递归实现:
```Rust
pub fn fib(n: i32) -> i32 {
//若n小于等于1返回n
f n <= 1 {
return n;
impl Solution {
pub fn fib(n: i32) -> i32 {
if n <= 1 {
n
} else {
Self::fib(n - 1) + Self::fib(n - 2)
}
}
//否则返回fib(n-1) + fib(n-2)
return fib(n - 1) + fib(n - 2);
}
```

View File

@@ -144,6 +144,11 @@ public:
}
};
```
* 时间复杂度: O(n^2)
* 空间复杂度: O(n^2)
## 其他语言版本

View File

@@ -179,6 +179,11 @@ public:
}
};
```
* 时间复杂度: O(mn),其中 m 是amountn 是 coins 的长度
* 空间复杂度: O(m)
是不是发现代码如此精简,哈哈
## 总结
@@ -215,6 +220,27 @@ class Solution {
}
}
```
```Java
// 二维dp数组版本方便理解
class Solution {
public int change(int amount, int[] coins) {
int[][] dp = new int[coins.length][amount + 1];
// 只有一种硬币的情况
for (int i = 0; i <= amount; i += coins[0]) {
dp[0][i] = 1;
}
for (int i = 1; i < coins.length; i++) {
for (int j = 0; j <= amount; j++) {
// 第i种硬币使用0~k次求和
for (int k = 0; k * coins[i] <= j; k++) {
dp[i][j] += dp[i - 1][j - k * coins[i]];
}
}
}
return dp[coins.length - 1][amount];
}
}
```
Python

View File

@@ -234,6 +234,26 @@ class Solution:
return root
```
**迭代**
```python
class Solution:
def convertBST(self, root: Optional[TreeNode]) -> Optional[TreeNode]:
if not root: return root
stack = []
result = []
cur = root
pre = 0
while cur or stack:
if cur:
stack.append(cur)
cur = cur.right
else:
cur = stack.pop()
cur.val+= pre
pre = cur.val
cur =cur.left
return root
```
## Go

View File

@@ -104,6 +104,10 @@ public:
};
```
* 时间复杂度: O(n * m)
* 空间复杂度: O(n * m)
### 动态规划二
@@ -127,6 +131,10 @@ public:
};
```
* 时间复杂度: O(n * m)
* 空间复杂度: O(n * m)
## 其他语言版本

View File

@@ -267,6 +267,27 @@ class Solution {
return ans;
}
}
```
动态规划:简洁版
```java
class Solution {
public int countSubstrings(String s) {
boolean[][] dp = new boolean[s.length()][s.length()];
int res = 0;
for (int i = s.length() - 1; i >= 0; i--) {
for (int j = i; j < s.length(); j++) {
if (s.charAt(i) == s.charAt(j) && (j - i <= 1 || dp[i + 1][j - 1])) {
res++;
dp[i][j] = true;
}
}
}
return res;
}
}
```
中心扩散法:

View File

@@ -485,177 +485,176 @@ class MyLinkedList {
Python
```python
# 单链表
class Node(object):
def __init__(self, x=0):
self.val = x
self.next = None
class MyLinkedList(object):
def __init__(self):
self.head = Node()
self.size = 0 # 设置一个链表长度的属性,便于后续操作,注意每次增和删的时候都要更新
def get(self, index):
"""
:type index: int
:rtype: int
"""
if index < 0 or index >= self.size:
return -1
cur = self.head.next
while(index):
cur = cur.next
index -= 1
return cur.val
def addAtHead(self, val):
"""
:type val: int
:rtype: None
"""
new_node = Node(val)
new_node.next = self.head.next
self.head.next = new_node
self.size += 1
def addAtTail(self, val):
"""
:type val: int
:rtype: None
"""
new_node = Node(val)
cur = self.head
while(cur.next):
cur = cur.next
cur.next = new_node
self.size += 1
def addAtIndex(self, index, val):
"""
:type index: int
:type val: int
:rtype: None
"""
if index < 0:
self.addAtHead(val)
return
elif index == self.size:
self.addAtTail(val)
return
elif index > self.size:
return
node = Node(val)
pre = self.head
while(index):
pre = pre.next
index -= 1
node.next = pre.next
pre.next = node
self.size += 1
def deleteAtIndex(self, index):
"""
:type index: int
:rtype: None
"""
if index < 0 or index >= self.size:
return
pre = self.head
while(index):
pre = pre.next
index -= 1
pre.next = pre.next.next
self.size -= 1
# 双链表
# 相对于单链表, Node新增了prev属性
class Node:
def __init__(self, val):
版本一单链表
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.prev = None
self.next = None
self.next = next
class MyLinkedList:
def __init__(self):
self._head, self._tail = Node(0), Node(0) # 虚拟节点
self._head.next, self._tail.prev = self._tail, self._head
self._count = 0 # 添加的节点数
def _get_node(self, index: int) -> Node:
# 当index小于_count//2时, 使用_head查找更快, 反之_tail更快
if index >= self._count // 2:
# 使用prev往前找
node = self._tail
for _ in range(self._count - index):
node = node.prev
else:
# 使用next往后找
node = self._head
for _ in range(index + 1):
node = node.next
return node
self.dummy_head = ListNode()
self.size = 0
def get(self, index: int) -> int:
"""
Get the value of the index-th node in the linked list. If the index is invalid, return -1.
"""
if 0 <= index < self._count:
node = self._get_node(index)
return node.val
else:
if index < 0 or index >= self.size:
return -1
current = self.dummy_head.next
for i in range(index):
current = current.next
return current.val
def addAtHead(self, val: int) -> None:
"""
Add a node of value val before the first element of the linked list. After the insertion, the new node will be the first node of the linked list.
"""
self._update(self._head, self._head.next, val)
self.dummy_head.next = ListNode(val, self.dummy_head.next)
self.size += 1
def addAtTail(self, val: int) -> None:
"""
Append a node of value val to the last element of the linked list.
"""
self._update(self._tail.prev, self._tail, val)
current = self.dummy_head
while current.next:
current = current.next
current.next = ListNode(val)
self.size += 1
def addAtIndex(self, index: int, val: int) -> None:
"""
Add a node of value val before the index-th node in the linked list. If index equals to the length of linked list, the node will be appended to the end of linked list. If index is greater than the length, the node will not be inserted.
"""
if index < 0:
index = 0
elif index > self._count:
if index < 0 or index > self.size:
return
node = self._get_node(index)
self._update(node.prev, node, val)
def _update(self, prev: Node, next: Node, val: int) -> None:
"""
更新节点
:param prev: 相对于更新的前一个节点
:param next: 相对于更新的后一个节点
:param val: 要添加的节点值
"""
# 计数累加
self._count += 1
node = Node(val)
prev.next, next.prev = node, node
node.prev, node.next = prev, next
current = self.dummy_head
for i in range(index):
current = current.next
current.next = ListNode(val, current.next)
self.size += 1
def deleteAtIndex(self, index: int) -> None:
"""
Delete the index-th node in the linked list, if the index is valid.
"""
if 0 <= index < self._count:
node = self._get_node(index)
# 计数-1
self._count -= 1
node.prev.next, node.next.prev = node.next, node.prev
if index < 0 or index >= self.size:
return
current = self.dummy_head
for i in range(index):
current = current.next
current.next = current.next.next
self.size -= 1
# Your MyLinkedList object will be instantiated and called as such:
# obj = MyLinkedList()
# param_1 = obj.get(index)
# obj.addAtHead(val)
# obj.addAtTail(val)
# obj.addAtIndex(index,val)
# obj.deleteAtIndex(index)
```
```python
版本二双链表法
class ListNode:
def __init__(self, val=0, prev=None, next=None):
self.val = val
self.prev = prev
self.next = next
class MyLinkedList:
def __init__(self):
self.head = None
self.tail = None
self.size = 0
def get(self, index: int) -> int:
if index < 0 or index >= self.size:
return -1
if index < self.size // 2:
current = self.head
for i in range(index):
current = current.next
else:
current = self.tail
for i in range(self.size - index - 1):
current = current.prev
return current.val
def addAtHead(self, val: int) -> None:
new_node = ListNode(val, None, self.head)
if self.head:
self.head.prev = new_node
else:
self.tail = new_node
self.head = new_node
self.size += 1
def addAtTail(self, val: int) -> None:
new_node = ListNode(val, self.tail, None)
if self.tail:
self.tail.next = new_node
else:
self.head = new_node
self.tail = new_node
self.size += 1
def addAtIndex(self, index: int, val: int) -> None:
if index < 0 or index > self.size:
return
if index == 0:
self.addAtHead(val)
elif index == self.size:
self.addAtTail(val)
else:
if index < self.size // 2:
current = self.head
for i in range(index - 1):
current = current.next
else:
current = self.tail
for i in range(self.size - index):
current = current.prev
new_node = ListNode(val, current, current.next)
current.next.prev = new_node
current.next = new_node
self.size += 1
def deleteAtIndex(self, index: int) -> None:
if index < 0 or index >= self.size:
return
if index == 0:
self.head = self.head.next
if self.head:
self.head.prev = None
else:
self.tail = None
elif index == self.size - 1:
self.tail = self.tail.prev
if self.tail:
self.tail.next = None
else:
self.head = None
else:
if index < self.size // 2:
current = self.head
for i in range(index):
current = current.next
else:
current = self.tail
for i in range(self.size - index - 1):
current = current.prev
current.prev.next = current.next
current.next.prev = current.prev
self.size -= 1
# Your MyLinkedList object will be instantiated and called as such:
# obj = MyLinkedList()
# param_1 = obj.get(index)
# obj.addAtHead(val)
# obj.addAtTail(val)
# obj.addAtIndex(index,val)
# obj.deleteAtIndex(index)
```
Go

View File

@@ -280,18 +280,20 @@ object Solution {
```Rust
impl Solution {
pub fn monotone_increasing_digits(n: i32) -> i32 {
let mut str_num = n.to_string().chars().map(|x| x.to_digit(10).unwrap() as i32).collect::<Vec<i32>>();
let mut flag = str_num.len();
for i in (1..str_num.len()).rev() {
if str_num[i - 1] > str_num[i] {
let mut n_bytes = n.to_string().into_bytes();
let mut flag = n_bytes.len();
for i in (1..n_bytes.len()).rev() {
if n_bytes[i - 1] > n_bytes[i] {
flag = i;
str_num[i - 1] -= 1;
n_bytes[i - 1] -= 1;
}
}
for i in flag..str_num.len() {
str_num[i] = 9;
for v in n_bytes.iter_mut().skip(flag) {
*v = 57;
}
str_num.iter().fold(0, |acc, x| acc * 10 + x)
n_bytes
.into_iter()
.fold(0, |acc, x| acc * 10 + x as i32 - 48)
}
}
```

View File

@@ -271,6 +271,7 @@ class Solution {
```
Python
> 未精简版本
```python
class Solution:
@@ -291,6 +292,21 @@ class Solution:
return answer
```
> 精简版本
```python
class Solution:
def dailyTemperatures(self, temperatures: List[int]) -> List[int]:
answer = [0]*len(temperatures)
stack = []
for i in range(len(temperatures)):
while len(stack)>0 and temperatures[i] > temperatures[stack[-1]]:
answer[stack[-1]] = i - stack[-1]
stack.pop()
stack.append(i)
return answer
```
Go
> 暴力法

View File

@@ -92,7 +92,7 @@ dp[i - 2] 跳到 dp[i] 需要花费 dp[i - 2] + cost[i - 2]。
这里就要说明本题力扣为什么改题意,而且修改题意之后 就清晰很多的原因了。
新题目描述中明确说了 “你可以选择从下标为 0 或下标为 1 的台阶开始爬楼梯。” 也就是说 到达 第 0 个台阶是不花费的,但从 第0 个台阶 往上跳的话,需要花费 cost[0]。
新题目描述中明确说了 “你可以选择从下标为 0 或下标为 1 的台阶开始爬楼梯。” 也就是说 到达 第 0 个台阶是不花费的,但从 第0 个台阶 往上跳的话,需要花费 cost[0]。
所以初始化 dp[0] = 0dp[1] = 0;
@@ -312,17 +312,30 @@ func min(a, b int) int {
```
### Javascript
```Javascript
### JavaScript
```JavaScript
var minCostClimbingStairs = function(cost) {
const n = cost.length;
const dp = new Array(n + 1);
dp[0] = dp[1] = 0;
for (let i = 2; i <= n; ++i) {
dp[i] = Math.min(dp[i -1] + cost[i - 1], dp[i - 2] + cost[i - 2])
const dp = [0, 0]
for (let i = 2; i <= cost.length; ++i) {
dp[i] = Math.min(dp[i - 1] + cost[i - 1], dp[i - 2] + cost[i - 2])
}
return dp[cost.length]
};
```
不使用 dp 数组
```JavaScript
var minCostClimbingStairs = function(cost) {
let dpBefore = 0,
dpAfter = 0
for(let i = 2;i <= cost.length;i++){
let dpi = Math.min(dpBefore + cost[i - 2],dpAfter + cost[i - 1])
dpBefore = dpAfter
dpAfter = dpi
}
return dp[n]
return dpAfter
};
```
@@ -330,38 +343,55 @@ var minCostClimbingStairs = function(cost) {
```typescript
function minCostClimbingStairs(cost: number[]): number {
/**
dp[i]: 走到第i阶需要花费的最少金钱
dp[0]: 0;
dp[1]: 0;
...
dp[i]: min(dp[i - 1] + cost[i - 1], dp[i - 2] + cost[i - 2]);
*/
const dp = [];
const length = cost.length;
dp[0] = 0;
dp[1] = 0;
for (let i = 2; i <= length; i++) {
dp[i] = Math.min(dp[i - 1] + cost[i - 1], dp[i - 2] + cost[i - 2]);
}
return dp[length];
};
const dp = [0, 0]
for (let i = 2; i <= cost.length; i++) {
dp[i] = Math.min(dp[i - 1] + cost[i - 1], dp[i - 2] + cost[i - 2])
}
return dp[cost.length]
}
```
不使用 dp 数组
```typescript
function minCostClimbingStairs(cost: number[]): number {
let dpBefore = 0,
dpAfter = 0
for (let i = 2; i <= cost.length; i++) {
let dpi = Math.min(dpBefore + cost[i - 2], dpAfter + cost[i - 1])
dpBefore = dpAfter
dpAfter = dpi
}
return dpAfter
}
```
### Rust
```Rust
use std::cmp::min;
impl Solution {
pub fn min_cost_climbing_stairs(cost: Vec<i32>) -> i32 {
let len = cost.len();
let mut dp = vec![0; len];
dp[0] = cost[0];
dp[1] = cost[1];
for i in 2..len {
dp[i] = min(dp[i-1], dp[i-2]) + cost[i];
let mut dp = vec![0; cost.len() + 1];
for i in 2..=cost.len() {
dp[i] = (dp[i - 1] + cost[i - 1]).min(dp[i - 2] + cost[i - 2]);
}
min(dp[len-1], dp[len-2])
dp[cost.len()]
}
}
```
不使用 dp 数组
```rust
impl Solution {
pub fn min_cost_climbing_stairs(cost: Vec<i32>) -> i32 {
let (mut dp_before, mut dp_after) = (0, 0);
for i in 2..=cost.len() {
let dpi = (dp_before + cost[i - 2]).min(dp_after + cost[i - 1]);
dp_before = dp_after;
dp_after = dpi;
}
dp_after
}
}
```
@@ -369,18 +399,29 @@ impl Solution {
### C
```c
int minCostClimbingStairs(int* cost, int costSize){
//开辟dp数组大小为costSize
int *dp = (int *)malloc(sizeof(int) * costSize);
//初始化dp[0] = cost[0], dp[1] = cost[1]
dp[0] = cost[0], dp[1] = cost[1];
#include <math.h>
int minCostClimbingStairs(int *cost, int costSize) {
int dp[costSize + 1];
dp[0] = dp[1] = 0;
for (int i = 2; i <= costSize; i++) {
dp[i] = fmin(dp[i - 2] + cost[i - 2], dp[i - 1] + cost[i - 1]);
}
return dp[costSize];
}
```
int i;
for(i = 2; i < costSize; ++i) {
dp[i] = (dp[i-1] < dp[i-2] ? dp[i-1] : dp[i-2]) + cost[i];
}
//选出倒数2层楼梯中较小的
return dp[i-1] < dp[i-2] ? dp[i-1] : dp[i-2];
不使用 dp 数组
```c
#include <math.h>
int minCostClimbingStairs(int *cost, int costSize) {
int dpBefore = 0, dpAfter = 0;
for (int i = 2; i <= costSize; i++) {
int dpi = fmin(dpBefore + cost[i - 2], dpAfter + cost[i - 1]);
dpBefore = dpAfter;
dpAfter = dpi;
}
return dpAfter;
}
```

View File

@@ -412,28 +412,22 @@ object Solution {
### Rust
```Rust
use std::collections::HashMap;
impl Solution {
fn max (a: usize, b: usize) -> usize {
if a > b { a } else { b }
}
pub fn partition_labels(s: String) -> Vec<i32> {
let s = s.chars().collect::<Vec<char>>();
let mut hash: HashMap<char, usize> = HashMap::new();
for i in 0..s.len() {
hash.insert(s[i], i);
let mut hash = vec![0; 26];
for (i, &c) in s.as_bytes().iter().enumerate() {
hash[(c - b'a') as usize] = i;
}
let mut result: Vec<i32> = Vec::new();
let mut left: usize = 0;
let mut right: usize = 0;
for i in 0..s.len() {
right = Self::max(right, hash[&s[i]]);
let mut res = vec![];
let (mut left, mut right) = (0, 0);
for (i, &c) in s.as_bytes().iter().enumerate() {
right = right.max(hash[(c - b'a') as usize]);
if i == right {
result.push((right - left + 1) as i32);
res.push((right - left + 1) as i32);
left = i + 1;
}
}
result
res
}
}
```

View File

@@ -535,8 +535,57 @@ function getIndexAfterDel(s: string, startIndex: number): number {
}
```
### Rust
> 双指针
```rust
impl Solution {
pub fn backspace_compare(s: String, t: String) -> bool {
let (s, t) = (
s.chars().collect::<Vec<char>>(),
t.chars().collect::<Vec<char>>(),
);
Self::get_string(s) == Self::get_string(t)
}
pub fn get_string(mut chars: Vec<char>) -> Vec<char> {
let mut slow = 0;
for i in 0..chars.len() {
if chars[i] == '#' {
slow = (slow as u32).saturating_sub(1) as usize;
} else {
chars[slow] = chars[i];
slow += 1;
}
}
chars.truncate(slow);
chars
}
}
```
> 双栈法
```rust
impl Solution {
pub fn backspace_compare(s: String, t: String) -> bool {
Self::get_string(s) == Self::get_string(t)
}
pub fn get_string(string: String) -> String {
let mut s = String::new();
for c in string.chars() {
if c != '#' {
s.push(c);
} else if !s.is_empty() {
s.pop();
}
}
s
}
}
```
<p align="center">
<a href="https://programmercarl.com/other/kstar.html" target="_blank">

View File

@@ -115,6 +115,9 @@ public:
}
};
```
* 时间复杂度: O(n)
* 空间复杂度: O(1)
## 总结

View File

@@ -303,6 +303,11 @@ public:
```
* 时间复杂度: O(n),需要遍历二叉树上的每个节点
* 空间复杂度: O(n)
大家可能会惊讶,居然可以这么简短,**其实就是在版本一的基础上使用else把一些情况直接覆盖掉了**。
在网上关于这道题解可以搜到很多这种神级别的代码,但都没讲不清楚,如果直接看代码的话,指定越看越晕,**所以建议大家对着版本一的代码一步一步来,版本二中看不中用!**。

View File

@@ -140,22 +140,37 @@ class Solution {
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
l, r, i = 0, len(nums)-1, len(nums)-1
res = [float('inf')] * len(nums) # 需要提前定义列表,存放结果
while l <= r:
if nums[l] ** 2 < nums[r] ** 2: # 左右边界进行对比,找出最大值
res[i] = nums[r] ** 2
r -= 1 # 右指针往左移动
else:
ans[k] = rm
j -= 1
k -= 1
return ans
res[i] = nums[l] ** 2
l += 1 # 左指针往右移动
i -= 1 # 存放结果的指针需要往前平移一位
return res
```
```Python
版本二暴力排序法
class Solution:
def sortedSquares(self, nums: List[int]) -> List[int]:
for i in range(len(nums)):
nums[i] *= nums[i]
nums.sort()
return nums
```
```Python
版本三暴力排序法+列表推导法
class Solution:
def sortedSquares(self, nums: List[int]) -> List[int]:
return sorted(x*x for x in nums)
```
Go

View File

@@ -252,6 +252,36 @@ var commonChars = function (words) {
}
return res
};
// 方法二map()
var commonChars = function(words) {
let min_count = new Map()
// 统计字符串中字符出现的最小频率,以第一个字符串初始化
for(let str of words[0]) {
min_count.set(str, ((min_count.get(str) || 0) + 1))
}
// 从第二个单词开始统计字符出现次数
for(let i = 1; i < words.length; i++) {
let char_count = new Map()
for(let str of words[i]) { // 遍历字母
char_count.set(str, (char_count.get(str) || 0) + 1)
}
// 比较出最小的字符次数
for(let value of min_count) { // 注意这里遍历min_count!而不是单词
min_count.set(value[0], Math.min((min_count.get(value[0]) || 0), (char_count.get(value[0]) || 0)))
}
}
// 遍历map
let res = []
min_count.forEach((value, key) => {
if(value) {
for(let i=0; i<value; i++) {
res.push(key)
}
}
})
return res
}
```
TypeScript

View File

@@ -85,6 +85,10 @@ public:
};
```
* 时间复杂度: O(nlogn)
* 空间复杂度: O(1)
## 总结
贪心的题目如果简单起来,会让人简单到开始怀疑:本来不就应该这么做么?这也算是算法?我认为这不是贪心?

View File

@@ -62,6 +62,10 @@ public:
}
};
```
* 时间复杂度: O(n * m)
* 空间复杂度: O(n * m)
## 总结

View File

@@ -124,6 +124,10 @@ public:
}
};
```
* 时间复杂度: O(n * m),其中 n 和 m 分别为 text1 和 text2 的长度
* 空间复杂度: O(n * m)
## 其他语言版本

View File

@@ -258,7 +258,9 @@ class Solution:
if node.left:
stack.append(node.left)
return result
```
```python
# 中序遍历-迭代-LC94_二叉树的中序遍历
class Solution:
def inorderTraversal(self, root: TreeNode) -> List[int]:
@@ -279,7 +281,9 @@ class Solution:
# 取栈顶元素右结点
cur = cur.right
return result
```
```python
# 后序遍历-迭代-LC145_二叉树的后序遍历
class Solution:
def postorderTraversal(self, root: TreeNode) -> List[int]:

View File

@@ -174,50 +174,49 @@ class Solution {
Python
```python
# 前序遍历-递归-LC144_二叉树的前序遍历
# 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 preorderTraversal(self, root: TreeNode) -> List[int]:
# 保存结果
result = []
def traversal(root: TreeNode):
if root == None:
return
result.append(root.val) # 前序
traversal(root.left) # 左
traversal(root.right) # 右
if not root:
return []
traversal(root)
return result
left = self.preorderTraversal(root.left)
right = self.preorderTraversal(root.right)
return [root.val] + left + right
```
```python
# 中序遍历-递归-LC94_二叉树的中序遍历
class Solution:
def inorderTraversal(self, root: TreeNode) -> List[int]:
result = []
if root is None:
return []
def traversal(root: TreeNode):
if root == None:
return
traversal(root.left) # 左
result.append(root.val) # 中序
traversal(root.right) # 右
left = self.inorderTraversal(root.left)
right = self.inorderTraversal(root.right)
return left + [root.val] + right
```
```python
traversal(root)
return result
# 后序遍历-递归-LC145_二叉树的后序遍历
class Solution:
def postorderTraversal(self, root: TreeNode) -> List[int]:
result = []
if not root:
return []
def traversal(root: TreeNode):
if root == None:
return
traversal(root.left) # 左
traversal(root.right) # 右
result.append(root.val) # 后序
left = self.postorderTraversal(root.left)
right = self.postorderTraversal(root.right)
traversal(root)
return result
return left + right + [root.val]
```
Go

View File

@@ -24,5 +24,5 @@
例如for循环里套一个字符串的inserterase之类的操作你说时间复杂度是多少呢很明显是O(n^2)的时间复杂度了。
在刷题的时候本着我说的标准来使用库函数,详细对大家有所帮助!
在刷题的时候本着我说的标准来使用库函数,相信对大家有所帮助!

View File

@@ -54,9 +54,9 @@ int function2(int x, int n) {
```CPP
int function3(int x, int n) {
if (n == 0) {
return 1;
}
if (n == 0) return 1;
if (n == 1) return x;
if (n % 2 == 1) {
return function3(x, n / 2) * function3(x, n / 2)*x;
}
@@ -93,9 +93,8 @@ int function3(int x, int n) {
```CPP
int function4(int x, int n) {
if (n == 0) {
return 1;
}
if (n == 0) return 1;
if (n == 1) return x;
int t = function4(x, n / 2);// 这里相对于function3是把这个递归操作抽取出来
if (n % 2 == 1) {
return t * t * x;
@@ -124,9 +123,8 @@ int function4(int x, int n) {
```CPP
int function3(int x, int n) {
if (n == 0) {
return 1;
}
if (n == 0) return 1;
if (n == 1) return x;
if (n % 2 == 1) {
return function3(x, n / 2) * function3(x, n / 2)*x;
}

View File

@@ -146,7 +146,7 @@ public:
**这也体现了刷题顺序的重要性**
先遍历背包,遍历物品:
先遍历背包,遍历物品:
```CPP
// 版本一
@@ -165,7 +165,7 @@ public:
};
```
先遍历物品,遍历背包:
先遍历物品,遍历背包:
```CPP
// 版本二

View File

@@ -89,7 +89,7 @@
在C++中set 和 map 分别提供以下三种数据结构,其底层实现以及优劣如下表所示:
| 集合 | 底层实现 | 是否有序 | 数值是否可以重复 | 能否更改数值 | 查询效率 | 增删效率 |
| ------------------ | -------- | -------- | ---------------- | ------------ | -------- | -------- |
| --- | --- | ---- | --- | --- | --- | --- |
| std::set | 红黑树 | 有序 | 否 | 否 | O(log n) | O(log n) |
| std::multiset | 红黑树 | 有序 | 是 | 否 | O(logn) | O(logn) |
| std::unordered_set | 哈希表 | 无序 | 否 | 否 | O(1) | O(1) |
@@ -97,11 +97,12 @@
std::unordered_set底层实现为哈希表std::set 和std::multiset 的底层实现是红黑树红黑树是一种平衡二叉搜索树所以key值是有序的但key不可以修改改动key值会导致整棵树的错乱所以只能删除和增加。
| 映射 | 底层实现 | 是否有序 | 数值是否可以重复 | 能否更改数值 | 查询效率 | 增删效率 |
| ------------------ | -------- | -------- | ---------------- | ------------ | -------- | -------- |
| --- | --- | --- | --- | --- | --- | --- |
| std::map | 红黑树 | key有序 | key不可重复 | key不可修改 | O(logn) | O(logn) |
| std::multimap | 红黑树 | key有序 | key可重复 | key不可修改 | O(log n) | O(log n) |
| std::unordered_map | 哈希表 | key无序 | key不可重复 | key不可修改 | O(1) | O(1) |
std::unordered_map 底层实现为哈希表std::map 和std::multimap 的底层实现是红黑树。同理std::map 和std::multimap 的key也是有序的这个问题也经常作为面试题考察对语言容器底层的理解
当我们要使用集合来解决哈希问题的时候优先使用unordered_set因为它的查询和增删效率是最优的如果需要集合是有序的那么就用set如果要求不仅有序还要有重复数据的话那么就用multiset。

View File

@@ -177,6 +177,35 @@ Java
Python
Rust
```rust
// 版本二使用list链表
use std::collections::LinkedList;
impl Solution{
pub fn reconstruct_queue(mut people: Vec<Vec<i32>>) -> Vec<Vec<i32>> {
let mut queue = LinkedList::new();
people.sort_by(|a, b| {
if a[0] == b[0] {
return a[1].cmp(&b[1]);
}
b[0].cmp(&a[0])
});
queue.push_back(people[0].clone());
for v in people.iter().skip(1) {
if queue.len() > v[1] as usize {
let mut back_link = queue.split_off(v[1] as usize);
queue.push_back(v.clone());
queue.append(&mut back_link);
} else {
queue.push_back(v.clone());
}
}
queue.into_iter().collect()
}
}
```
Go

View File

@@ -41,7 +41,7 @@
* [动态规划关于01背包问题你该了解这些](https://programmercarl.com/背包理论基础01背包-1.html)
* [动态规划关于01背包问题你该了解这些滚动数组](https://programmercarl.com/背包理论基础01背包-2.html)
首先回顾一下01背包的核心代码
首先回顾一下01背包的核心代码
```cpp
for(int i = 0; i < weight.size(); i++) { // 遍历物品
for(int j = bagWeight; j >= weight[i]; j--) { // 遍历背包容量
@@ -173,7 +173,7 @@ int main() {
别急,下一篇就是了!哈哈
最后,**又可以出一道面试题了就是纯完全背包要求先用二维dp数组实现然后再用一维dp数组实现最后两个for循环的先后是否可以颠倒为什么**
最后,**又可以出一道面试题了就是纯完全背包要求先用二维dp数组实现然后再用一维dp数组实现最后两个for循环的先后是否可以颠倒为什么**
这个简单的完全背包问题,估计就可以难住不少候选人了。

View File

@@ -152,7 +152,7 @@ public class Solution {
### Python
```python
版本一求长度同时出发
class Solution:
def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
lenA, lenB = 0, 0
@@ -178,7 +178,68 @@ class Solution:
curB = curB.next
return None
```
```python
版本二求长度同时出发 代码复用
class Solution:
def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
lenA = self.getLength(headA)
lenB = self.getLength(headB)
# 通过移动较长的链表,使两链表长度相等
if lenA > lenB:
headA = self.moveForward(headA, lenA - lenB)
else:
headB = self.moveForward(headB, lenB - lenA)
# 将两个头向前移动,直到它们相交
while headA and headB:
if headA == headB:
return headA
headA = headA.next
headB = headB.next
return None
def getLength(self, head: ListNode) -> int:
length = 0
while head:
length += 1
head = head.next
return length
def moveForward(self, head: ListNode, steps: int) -> ListNode:
while steps > 0:
head = head.next
steps -= 1
return head
```
```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:
# 处理边缘情况
if not headA or not headB:
return None
# 在每个链表的头部初始化两个指针
pointerA = headA
pointerB = headB
# 遍历两个链表直到指针相交
while pointerA != pointerB:
# 将指针向前移动一个节点
pointerA = pointerA.next if pointerA else headB
pointerB = pointerB.next if pointerB else headA
# 如果相交指针将位于交点节点如果没有交点值为None
return pointerA
```
### Go
```go