Merge branch 'master' of github.com:youngyangyang04/leetcode-master

This commit is contained in:
programmercarl
2023-01-06 12:36:48 +08:00
31 changed files with 725 additions and 405 deletions

View File

@ -455,42 +455,31 @@ function letterCombinations(digits: string): string[] {
## Rust ## Rust
```Rust ```Rust
const map: [&str; 10] = [
"", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz",
];
impl Solution { impl Solution {
fn backtracking(result: &mut Vec<String>, s: &mut String, map: &[&str; 10], digits: &String, index: usize) { fn back_trace(result: &mut Vec<String>, s: &mut String, digits: &String, index: usize) {
let len = digits.len(); let len = digits.len();
if len == index { if len == index {
result.push(s.to_string()); result.push(s.to_string());
return; return;
} }
// 在保证不会越界的情况下使用unwrap()将Some()中的值提取出来 let digit = (digits.as_bytes()[index] - b'0') as usize;
let digit= digits.chars().nth(index).unwrap().to_digit(10).unwrap() as usize; for i in map[digit].chars() {
let letters = map[digit];
for i in letters.chars() {
s.push(i); s.push(i);
Self::backtracking(result, s, &map, &digits, index+1); Self::back_trace(result, s, digits, index + 1);
s.pop(); s.pop();
} }
} }
pub fn letter_combinations(digits: String) -> Vec<String> { pub fn letter_combinations(digits: String) -> Vec<String> {
if digits.len() == 0 { if digits.is_empty() {
return vec![]; return vec![];
} }
const MAP: [&str; 10] = [ let mut res = vec![];
"", let mut s = String::new();
"", Self::back_trace(&mut res, &mut s, &digits, 0);
"abc", res
"def",
"ghi",
"jkl",
"mno",
"pqrs",
"tuv",
"wxyz"
];
let mut result: Vec<String> = Vec::new();
let mut s: String = String::new();
Self::backtracking(&mut result, &mut s, &MAP, &digits, 0);
result
} }
} }
``` ```

View File

@ -154,24 +154,25 @@ class Solution {
``` ```
```java ```java
// 虚拟头结点
class Solution { class Solution {
public ListNode swapPairs(ListNode head) { public ListNode swapPairs(ListNode head) {
ListNode dumyhead = new ListNode(-1); // 设置一个虚拟头结点
ListNode dummyNode = new ListNode(0); dumyhead.next = head; // 将虚拟头结点指向head这样方面后面做删除操作
dummyNode.next = head; ListNode cur = dumyhead;
ListNode prev = dummyNode; ListNode temp; // 临时节点,保存两个节点后面的节点
ListNode firstnode; // 临时节点,保存两个节点之中的第一个节点
while (prev.next != null && prev.next.next != null) { ListNode secondnode; // 临时节点,保存两个节点之中的第二个节点
ListNode temp = head.next.next; // 缓存 next while (cur.next != null && cur.next.next != null) {
prev.next = head.next; // 将 prev 的 next 改为 head 的 next temp = cur.next.next.next;
head.next.next = head; // 将 head.next(prev.next) 的next指向 head firstnode = cur.next;
head.next = temp; // 将head 的 next 接上缓存的temp secondnode = cur.next.next;
prev = head; // 步进1位 cur.next = secondnode; // 步骤一
head = head.next; // 步进1位 secondnode.next = firstnode; // 步骤二
firstnode.next = temp; // 步骤三
cur = firstnode; // cur移动准备下一轮交换
}
return dumyhead.next;
} }
return dummyNode.next;
}
} }
``` ```

View File

@ -267,22 +267,22 @@ Rust:
```Rust ```Rust
impl Solution { impl Solution {
fn backtracking(result: &mut Vec<Vec<i32>>, path: &mut Vec<i32>, n: i32, k: i32, startIndex: i32) { fn backtracking(result: &mut Vec<Vec<i32>>, path: &mut Vec<i32>, n: i32, k: i32, start_index: i32) {
let len= path.len() as i32; let len= path.len() as i32;
if len == k{ if len == k{
result.push(path.to_vec()); result.push(path.to_vec());
return; return;
} }
// 此处剪枝 // 此处剪枝
for i in startIndex..= n - (k - len) + 1 { for i in start_index..= n - (k - len) + 1 {
path.push(i); path.push(i);
Self::backtracking(result, path, n, k, i+1); Self::backtracking(result, path, n, k, i+1);
path.pop(); path.pop();
} }
} }
pub fn combine(n: i32, k: i32) -> Vec<Vec<i32>> { pub fn combine(n: i32, k: i32) -> Vec<Vec<i32>> {
let mut result: Vec<Vec<i32>> = Vec::new(); let mut result = vec![];
let mut path: Vec<i32> = Vec::new(); let mut path = vec![];
Self::backtracking(&mut result, &mut path, n, k, 1); Self::backtracking(&mut result, &mut path, n, k, 1);
result result
} }

View File

@ -392,7 +392,26 @@ class Solution:
return is_left_valid and is_right_valid return is_left_valid and is_right_valid
return __isValidBST(root) return __isValidBST(root)
``` ```
**递归** - 避免初始化最小值做法:
```python
class Solution:
def isValidBST(self, root: TreeNode) -> bool:
# 规律: BST的中序遍历节点数值是从小到大.
pre = None
def __isValidBST(root: TreeNode) -> bool:
nonlocal pre
if not root:
return True
is_left_valid = __isValidBST(root.left)
if pre and pre.val>=root.val: return False
pre = root
is_right_valid = __isValidBST(root.right)
return is_left_valid and is_right_valid
return __isValidBST(root)
```
```python ```python
迭代-中序遍历 迭代-中序遍历
class Solution: class Solution:

View File

@ -271,6 +271,36 @@ func levelOrder(root *TreeNode) [][]int {
return res return res
} }
/**
102. 二叉树的层序遍历:使用切片模拟队列,易理解
*/
func levelOrder(root *TreeNode) (res [][]int) {
if root == nil {
return
}
curLevel := []*TreeNode{root} // 存放当前层节点
for len(curLevel) > 0 {
nextLevel := []*TreeNode{} // 准备通过当前层生成下一层
vals := []int{}
for _, node := range curLevel {
vals = append(vals, node.Val) // 收集当前层的值
// 收集下一层的节点
if node.Left != nil {
nextLevel = append(nextLevel, node.Left)
}
if node.Right != nil {
nextLevel = append(nextLevel, node.Right)
}
}
res = append(res, vals)
curLevel = nextLevel // 将下一层变成当前层
}
return
}
``` ```
javascript代码 javascript代码
@ -1072,7 +1102,6 @@ public class N0637 {
que.offerLast(root); que.offerLast(root);
while (!que.isEmpty()) { while (!que.isEmpty()) {
TreeNode peek = que.peekFirst();
int levelSize = que.size(); int levelSize = que.size();
double levelSum = 0.0; double levelSum = 0.0;
@ -1346,6 +1375,22 @@ class Solution:
return results return results
``` ```
```python
# LeetCode 429. N-ary Tree Level Order Traversal
# 递归法
class Solution:
def levelOrder(self, root: 'Node') -> List[List[int]]:
if not root: return []
result=[]
def traversal(root,depth):
if len(result)==depth:result.append([])
result[depth].append(root.val)
if root.children:
for i in range(len(root.children)):traversal(root.children[i],depth+1)
traversal(root,0)
return result
```
java: java:
```java ```java
@ -2955,7 +3000,7 @@ impl Solution {
* 107.二叉树的层次遍历II * 107.二叉树的层次遍历II
* 199.二叉树的右视图 * 199.二叉树的右视图
* 637.二叉树的层平均值 * 637.二叉树的层平均值
* 429.N叉树的序遍历 * 429.N叉树的序遍历
* 515.在每个树行中找最大值 * 515.在每个树行中找最大值
* 116.填充每个节点的下一个右侧节点指针 * 116.填充每个节点的下一个右侧节点指针
* 117.填充每个节点的下一个右侧节点指针II * 117.填充每个节点的下一个右侧节点指针II
@ -2970,3 +3015,4 @@ impl Solution {
<a href="https://programmercarl.com/other/kstar.html" target="_blank"> <a href="https://programmercarl.com/other/kstar.html" target="_blank">
<img src="../pics/网站星球宣传海报.jpg" width="1000"/> <img src="../pics/网站星球宣传海报.jpg" width="1000"/>
</a> </a>

View File

@ -402,10 +402,10 @@ class solution:
def getdepth(self, node): def getdepth(self, node):
if not node: if not node:
return 0 return 0
leftdepth = self.getdepth(node.left) #左 leftheight = self.getdepth(node.left) #左
rightdepth = self.getdepth(node.right) #右 rightheight = self.getdepth(node.right) #右
depth = 1 + max(leftdepth, rightdepth) #中 height = 1 + max(leftheight, rightheight) #中
return depth return height
``` ```
递归法:精简代码 递归法:精简代码

View File

@ -486,6 +486,26 @@ object Solution {
} }
``` ```
## rust
递归:
```rust
impl Solution {
pub fn sorted_array_to_bst(nums: Vec<i32>) -> Option<Rc<RefCell<TreeNode>>> {
if nums.is_empty() {
return None;
}
let index = nums.len() / 2;
let mut root = TreeNode::new(nums[index]);
root.left = Self::sorted_array_to_bst(nums[..index].to_vec());
root.right = Self::sorted_array_to_bst(nums[index + 1..].to_vec());
Some(Rc::new(RefCell::new(root)))
}
}
```
<p align="center"> <p align="center">
<a href="https://programmercarl.com/other/kstar.html" target="_blank"> <a href="https://programmercarl.com/other/kstar.html" target="_blank">

View File

@ -89,7 +89,7 @@ dp[i][1] 表示第i天不持有股票所得最多现金
**注意这里说的是“持有”,“持有”不代表就是当天“买入”!也有可能是昨天就买入了,今天保持持有的状态** **注意这里说的是“持有”,“持有”不代表就是当天“买入”!也有可能是昨天就买入了,今天保持持有的状态**
很多同学把“持有”和“买入”没区分清楚。 很多同学把“持有”和“买入”没区分清楚。
在下面递推公式分析中,我会进一步讲解。 在下面递推公式分析中,我会进一步讲解。
@ -103,11 +103,11 @@ dp[i][1] 表示第i天不持有股票所得最多现金
如果第i天不持有股票即dp[i][1] 也可以由两个状态推出来 如果第i天不持有股票即dp[i][1] 也可以由两个状态推出来
* 第i-1天就不持有股票那么就保持现状所得现金就是昨天不持有股票的所得现金 即dp[i - 1][1] * 第i-1天就不持有股票那么就保持现状所得现金就是昨天不持有股票的所得现金 即dp[i - 1][1]
* 第i天卖出股票所得现金就是按照今天股票价格卖出后所得现金即prices[i] + dp[i - 1][0] * 第i天卖出股票所得现金就是按照今天股票价格卖出后所得现金即prices[i] + dp[i - 1][0]
同样dp[i][1]取最大的dp[i][1] = max(dp[i - 1][1], prices[i] + dp[i - 1][0]); 同样dp[i][1]取最大的dp[i][1] = max(dp[i - 1][1], prices[i] + dp[i - 1][0]);
这样递公式我们就分析完了 这样递公式我们就分析完了
3. dp数组如何初始化 3. dp数组如何初始化
@ -121,7 +121,7 @@ dp[0][1]表示第0天不持有股票不持有股票那么现金就是0
4. 确定遍历顺序 4. 确定遍历顺序
从递推公式可以看出dp[i]都是dp[i - 1]推导出来的,那么一定是从前向后遍历。 从递推公式可以看出dp[i]都是dp[i - 1]推导出来的,那么一定是从前向后遍历。
5. 举例推导dp数组 5. 举例推导dp数组
@ -326,53 +326,40 @@ Go:
> 贪心法: > 贪心法:
```Go ```Go
func maxProfit(prices []int) int { func maxProfit(prices []int) int {
low := math.MaxInt32 min := prices[0]
rlt := 0 res := 0
for i := range prices{ for i := 1; i < len(prices); i++ {
low = min(low, prices[i]) if prices[i] - min > res {
rlt = max(rlt, prices[i]-low) res = prices[i]-min
}
if min > prices[i] {
min = prices[i]
}
} }
return res
return rlt
}
func min(a, b int) int {
if a < b{
return a
}
return b
}
func max(a, b int) int {
if a > b{
return a
}
return b
} }
``` ```
> 动态规划:版本一 > 动态规划:版本一
```Go ```Go
func maxProfit(prices []int) int { func maxProfit(prices []int) int {
length:=len(prices) length := len(prices)
if length==0{return 0} if length == 0{return 0}
dp:=make([][]int,length) dp := make([][]int,length)
for i:=0;i<length;i++{ for i := 0; i < length; i++ {
dp[i]=make([]int,2) dp[i] = make([]int, 2)
} }
dp[0][0] = -prices[0]
dp[0][0]=-prices[0] dp[0][1] = 0
dp[0][1]=0 for i := 1; i < length; i++ {
for i:=1;i<length;i++{ dp[i][0] = max(dp[i-1][0], -prices[i])
dp[i][0]=max(dp[i-1][0],-prices[i]) dp[i][1] = max(dp[i-1][1], dp[i-1][0] + prices[i])
dp[i][1]=max(dp[i-1][1],dp[i-1][0]+prices[i])
} }
return dp[length-1][1] return dp[length-1][1]
} }
func max(a,b int)int { func max(a, b int) int {
if a>b{ if a > b {
return a return a
} }
return b return b
@ -385,7 +372,7 @@ func maxProfit(prices []int) int {
dp := [2][2]int{} dp := [2][2]int{}
dp[0][0] = -prices[0] dp[0][0] = -prices[0]
dp[0][1] = 0 dp[0][1] = 0
for i := 1; i < len(prices); i++{ for i := 1; i < len(prices); i++ {
dp[i%2][0] = max(dp[(i-1)%2][0], -prices[i]) dp[i%2][0] = max(dp[(i-1)%2][0], -prices[i])
dp[i%2][1] = max(dp[(i-1)%2][1], dp[(i-1)%2][0]+prices[i]) dp[i%2][1] = max(dp[(i-1)%2][1], dp[(i-1)%2][0]+prices[i])
} }

View File

@ -39,7 +39,7 @@
本题我们在讲解贪心专题的时候就已经讲解过了[贪心算法买卖股票的最佳时机II](https://programmercarl.com/0122.买卖股票的最佳时机II.html),只不过没有深入讲解动态规划的解法,那么这次我们再好好分析一下动规的解法。 本题我们在讲解贪心专题的时候就已经讲解过了[贪心算法买卖股票的最佳时机II](https://programmercarl.com/0122.买卖股票的最佳时机II.html),只不过没有深入讲解动态规划的解法,那么这次我们再好好分析一下动规的解法。
本题和[121. 买卖股票的最佳时机](https://programmercarl.com/0121.买卖股票的最佳时机.html)的唯一区别本题股票可以买卖多次了(注意只有一只股票,所以再次购买前要出售掉之前的股票) 本题和[121. 买卖股票的最佳时机](https://programmercarl.com/0121.买卖股票的最佳时机.html)的唯一区别本题股票可以买卖多次了(注意只有一只股票,所以再次购买前要出售掉之前的股票)
**在动规五部曲中,这个区别主要是体现在递推公式上,其他都和[121. 买卖股票的最佳时机](https://programmercarl.com/0121.买卖股票的最佳时机.html)一样一样的** **在动规五部曲中,这个区别主要是体现在递推公式上,其他都和[121. 买卖股票的最佳时机](https://programmercarl.com/0121.买卖股票的最佳时机.html)一样一样的**
@ -63,9 +63,9 @@
那么第i天持有股票即dp[i][0]如果是第i天买入股票所得现金就是昨天不持有股票的所得现金 减去 今天的股票价格 即dp[i - 1][1] - prices[i]。 那么第i天持有股票即dp[i][0]如果是第i天买入股票所得现金就是昨天不持有股票的所得现金 减去 今天的股票价格 即dp[i - 1][1] - prices[i]。
来看看如果第i天不持有股票即dp[i][1]的情况, 依然可以由两个状态推出来 来看看如果第i天不持有股票即dp[i][1]的情况, 依然可以由两个状态推出来
* 第i-1天就不持有股票那么就保持现状所得现金就是昨天不持有股票的所得现金 即dp[i - 1][1] * 第i-1天就不持有股票那么就保持现状所得现金就是昨天不持有股票的所得现金 即dp[i - 1][1]
* 第i天卖出股票所得现金就是按照今天股票价格卖出后所得现金即prices[i] + dp[i - 1][0] * 第i天卖出股票所得现金就是按照今天股票价格卖出后所得现金即prices[i] + dp[i - 1][0]
**注意这里和[121. 买卖股票的最佳时机](https://programmercarl.com/0121.买卖股票的最佳时机.html)就是一样的逻辑,卖出股票收获利润(可能是负值)天经地义!** **注意这里和[121. 买卖股票的最佳时机](https://programmercarl.com/0121.买卖股票的最佳时机.html)就是一样的逻辑,卖出股票收获利润(可能是负值)天经地义!**
@ -99,7 +99,7 @@ dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] - prices[i]);
**这正是因为本题的股票可以买卖多次!** 所以买入股票的时候可能会有之前买卖的利润即dp[i - 1][1]所以dp[i - 1][1] - prices[i]。 **这正是因为本题的股票可以买卖多次!** 所以买入股票的时候可能会有之前买卖的利润即dp[i - 1][1]所以dp[i - 1][1] - prices[i]。
想到到这一点,对这两道题理解的比较深刻了。 想到到这一点,对这两道题理解的比较深刻了。
这里我依然给出滚动数组的版本C++代码如下: 这里我依然给出滚动数组的版本C++代码如下:
@ -228,29 +228,6 @@ func max(a, b int) int {
} }
``` ```
```go
func maxProfit(prices []int) int {
//创建数组
dp:=make([][]int,len(prices))
for i:=0;i<len(prices);i++{
dp[i]=make([]int,2)
}
dp[0][0]=-prices[0]
dp[0][1]=0
for i:=1;i<len(prices);i++{
dp[i][0]=max(dp[i-1][0],dp[i-1][1]-prices[i])
dp[i][1]=max(dp[i-1][1],dp[i-1][0]+prices[i])
}
return dp[len(prices)-1][1]
}
func max(a,b int)int{
if a<b{
return b
}
return a
}
```
Javascript Javascript
```javascript ```javascript
// 方法一动态规划dp 数组) // 方法一动态规划dp 数组)

View File

@ -62,7 +62,7 @@ dp[i][j]中 i表示第i天j为 [0 - 4] 五个状态dp[i][j]表示第i天
需要注意dp[i][1]**表示的是第i天买入股票的状态并不是说一定要第i天买入股票这是很多同学容易陷入的误区**。 需要注意dp[i][1]**表示的是第i天买入股票的状态并不是说一定要第i天买入股票这是很多同学容易陷入的误区**。
例如 dp[i][1] ,并不是说 第i一定买入股票,有可能 第 i-1天 就买入了,那么 dp[i][1] 延续买入股票的这个状态。 例如 dp[i][1] ,并不是说 第i一定买入股票,有可能 第 i-1天 就买入了,那么 dp[i][1] 延续买入股票的这个状态。
2. 确定递推公式 2. 确定递推公式
@ -102,7 +102,7 @@ dp[i][4] = max(dp[i - 1][4], dp[i - 1][3] + prices[i]);
第0天第二次买入操作初始值应该是多少呢应该不少同学疑惑第一次还没买入呢怎么初始化第二次买入呢 第0天第二次买入操作初始值应该是多少呢应该不少同学疑惑第一次还没买入呢怎么初始化第二次买入呢
第二次买入依赖于第一次卖出的状态其实相当于第0天第一次买入了第一次卖出了然后买入一次(第二次买入),那么现在手头上没有现金,只要买入,现金就做相应的减少。 第二次买入依赖于第一次卖出的状态其实相当于第0天第一次买入了第一次卖出了然后买入一次(第二次买入),那么现在手头上没有现金,只要买入,现金就做相应的减少。
所以第二次买入操作初始化为dp[0][3] = -prices[0]; 所以第二次买入操作初始化为dp[0][3] = -prices[0];
@ -181,7 +181,7 @@ public:
dp[1] = max(dp[1], dp[0] - prices[i]); 如果dp[1]取dp[1],即保持买入股票的状态,那么 dp[2] = max(dp[2], dp[1] + prices[i]);中dp[1] + prices[i] 就是今天卖出。 dp[1] = max(dp[1], dp[0] - prices[i]); 如果dp[1]取dp[1],即保持买入股票的状态,那么 dp[2] = max(dp[2], dp[1] + prices[i]);中dp[1] + prices[i] 就是今天卖出。
如果dp[1]取dp[0] - prices[i]今天买入股票那么dp[2] = max(dp[2], dp[1] + prices[i]);中的dp[1] + prices[i]相当于是尽在再卖出股票一买一卖收益为0对所得现金没有影响。相当于今天买入股票又卖出股票等于没有操作保持昨天卖出股票的状态了。 如果dp[1]取dp[0] - prices[i]今天买入股票那么dp[2] = max(dp[2], dp[1] + prices[i]);中的dp[1] + prices[i]相当于是今天再卖出股票一买一卖收益为0对所得现金没有影响。相当于今天买入股票又卖出股票等于没有操作保持昨天卖出股票的状态了。
**这种写法看上去简单,其实思路很绕,不建议大家这么写,这么思考,很容易把自己绕进去!** **这种写法看上去简单,其实思路很绕,不建议大家这么写,这么思考,很容易把自己绕进去!**
@ -312,26 +312,26 @@ Go:
```go ```go
func maxProfit(prices []int) int { func maxProfit(prices []int) int {
dp:=make([][]int,len(prices)) dp := make([][]int, len(prices))
for i:=0;i<len(prices);i++{ for i := 0; i < len(prices); i++ {
dp[i]=make([]int,5) dp[i] = make([]int, 5)
} }
dp[0][0]=0 dp[0][0] = 0
dp[0][1]=-prices[0] dp[0][1] = -prices[0]
dp[0][2]=0 dp[0][2] = 0
dp[0][3]=-prices[0] dp[0][3] = -prices[0]
dp[0][4]=0 dp[0][4] = 0
for i:=1;i<len(prices);i++{ for i := 1; i < len(prices); i++ {
dp[i][0]=dp[i-1][0] dp[i][0] = dp[i-1][0]
dp[i][1]=max(dp[i-1][1],dp[i-1][0]-prices[i]) dp[i][1] = max(dp[i-1][1], dp[i-1][0] - prices[i])
dp[i][2]=max(dp[i-1][2],dp[i-1][1]+prices[i]) dp[i][2] = max(dp[i-1][2], dp[i-1][1] + prices[i])
dp[i][3]=max(dp[i-1][3],dp[i-1][2]-prices[i]) dp[i][3] = max(dp[i-1][3], dp[i-1][2] - prices[i])
dp[i][4]=max(dp[i-1][4],dp[i-1][3]+prices[i]) dp[i][4] = max(dp[i-1][4], dp[i-1][3] + prices[i])
} }
return dp[len(prices)-1][4] return dp[len(prices)-1][4]
} }
func max(a,b int)int{ func max(a, b int) int {
if a>b{ if a > b {
return a return a
} }
return b return b
@ -407,39 +407,6 @@ function maxProfit(prices: number[]): number {
}; };
``` ```
Go:
> 版本一:
```go
// 买卖股票的最佳时机III 动态规划
// 时间复杂度O(n) 空间复杂度O(n)
func maxProfit(prices []int) int {
dp := make([][]int, len(prices))
status := make([]int, len(prices) * 4)
for i := range dp {
dp[i] = status[:4]
status = status[4:]
}
dp[0][0], dp[0][2] = -prices[0], -prices[0]
for i := 1; i < len(prices); i++ {
dp[i][0] = max(dp[i - 1][0], -prices[i])
dp[i][1] = max(dp[i - 1][1], dp[i - 1][0] + prices[i])
dp[i][2] = max(dp[i - 1][2], dp[i - 1][1] - prices[i])
dp[i][3] = max(dp[i - 1][3], dp[i - 1][2] + prices[i])
}
return dp[len(prices) - 1][3]
}
func max(a, b int) int {
if a > b {
return a
}
return b
}
```

View File

@ -247,6 +247,55 @@ var ladderLength = function(beginWord, endWord, wordList) {
}; };
``` ```
## TypeScript
```typescript
function ladderLength(
beginWord: string,
endWord: string,
wordList: string[]
): number {
const words = new Set(wordList);
if (!words.has(endWord)) return 0;
if (beginWord.length === 1) return 2;
let current = new Set([beginWord]);
let rightcurrent = new Set([endWord]);
words.delete(endWord);
let step = 1;
while (current.size) {
if (current.size > rightcurrent.size) {
[current, rightcurrent] = [rightcurrent, current];
}
const temp: Set<string> = new Set();
for (const word of current) {
for (const right of rightcurrent) {
if (diffonechar(word, right)) {
return step + 1;
}
}
for (const other of words) {
if (diffonechar(other, word)) {
temp.add(other);
words.delete(other);
}
}
}
if (temp.size === 0) return 0;
current = temp;
step = step + 1;
}
return 0;
}
function diffonechar(word1: string, word2: string): boolean {
let changes = 0;
for (let i = 0; i < word1.length; i++) {
if (word1[i] != word2[i]) changes += 1;
}
return changes === 1;
}
```
<p align="center"> <p align="center">
<a href="https://programmercarl.com/other/kstar.html" target="_blank"> <a href="https://programmercarl.com/other/kstar.html" target="_blank">

View File

@ -490,15 +490,15 @@ var partition = function(s) {
const res = [], path = [], len = s.length; const res = [], path = [], len = s.length;
backtracking(0); backtracking(0);
return res; return res;
function backtracking(i) { function backtracking(startIndex) {
if(i >= len) { if(startIndex >= len) {
res.push(Array.from(path)); res.push(Array.from(path));
return; return;
} }
for(let j = i; j < len; j++) { for(let i = startIndex; i < len; i++) {
if(!isPalindrome(s, i, j)) continue; if(!isPalindrome(s, startIndex, i)) continue;
path.push(s.slice(i, j + 1)); path.push(s.slice(startIndex, i + 1));
backtracking(j + 1); backtracking(i + 1);
path.pop(); path.pop();
} }
} }

View File

@ -138,7 +138,7 @@ public:
3. dp数组如何初始化 3. dp数组如何初始化
从递公式中可以看出dp[i] 的状态依靠 dp[j]是否为true那么dp[0]就是递的根基dp[0]一定要为true否则递下去后面都都是false了 从递公式中可以看出dp[i] 的状态依靠 dp[j]是否为true那么dp[0]就是递的根基dp[0]一定要为true否则递下去后面都都是false了
那么dp[0]有没有意义呢 那么dp[0]有没有意义呢
@ -152,13 +152,13 @@ dp[0]表示如果字符串为空的话,说明出现在字典里。
题目中说是拆分为一个或多个在字典中出现的单词所以这是完全背包 题目中说是拆分为一个或多个在字典中出现的单词所以这是完全背包
还要讨论两层for循环的前后 还要讨论两层for循环的前后
**如果求组合数就是外层for循环遍历物品内层for遍历背包** **如果求组合数就是外层for循环遍历物品内层for遍历背包**
**如果求排列数就是外层for遍历背包内层for循环遍历物品** **如果求排列数就是外层for遍历背包内层for循环遍历物品**
我在这里做一个一个总结 我在这里做一个总结
求组合数[动态规划518.零钱兑换II](https://programmercarl.com/0518.零钱兑换II.html) 求组合数[动态规划518.零钱兑换II](https://programmercarl.com/0518.零钱兑换II.html)
求排列数[动态规划377. 组合总和 Ⅳ](https://programmercarl.com/0377.组合总和.html)[动态规划70. 爬楼梯进阶版(完全背包)](https://programmercarl.com/0070.爬楼梯完全背包版本.html) 求排列数[动态规划377. 组合总和 Ⅳ](https://programmercarl.com/0377.组合总和.html)[动态规划70. 爬楼梯进阶版(完全背包)](https://programmercarl.com/0070.爬楼梯完全背包版本.html)
@ -170,7 +170,7 @@ dp[0]表示如果字符串为空的话,说明出现在字典里。
"apple" + "apple" + "pen" 或者 "pen" + "apple" + "apple" 是不可以的那么我们就是强调物品之间顺序 "apple" + "apple" + "pen" 或者 "pen" + "apple" + "apple" 是不可以的那么我们就是强调物品之间顺序
所以说本题一定是 先遍历 背包遍历物品 所以说本题一定是 先遍历 背包遍历物品
5. 举例推导dp[i] 5. 举例推导dp[i]
@ -209,7 +209,7 @@ public:
关于遍历顺序再给大家讲一下为什么 先遍历物品再遍历背包不行 关于遍历顺序再给大家讲一下为什么 先遍历物品再遍历背包不行
这里可以给出先遍历物品遍历背包的代码 这里可以给出先遍历物品遍历背包的代码
```CPP ```CPP
class Solution { class Solution {
@ -241,7 +241,7 @@ public:
最后dp[s.size()] = 0 dp[13] = 0 ,而不是1,因为先用 "apple" 去遍历的时候,dp[8]并没有被赋值为1 (还没用"pen"),所以 dp[13]也不能变成1 最后dp[s.size()] = 0 dp[13] = 0 ,而不是1,因为先用 "apple" 去遍历的时候,dp[8]并没有被赋值为1 (还没用"pen"),所以 dp[13]也不能变成1
除非是先用 "apple" 遍历一遍, "pen" 遍历,此时 dp[8]已经是1,最后再用 "apple" 去遍历,dp[13]才能是1 除非是先用 "apple" 遍历一遍, "pen" 遍历,此时 dp[8]已经是1,最后再用 "apple" 去遍历,dp[13]才能是1
如果大家对这里不理解,建议可以把我上面给的代码,拿去力扣上跑一跑,把dp数组打印出来,对着递推公式一步一步去看,思路就清晰了。 如果大家对这里不理解,建议可以把我上面给的代码,拿去力扣上跑一跑,把dp数组打印出来,对着递推公式一步一步去看,思路就清晰了。
@ -352,16 +352,16 @@ class Solution:
Go Go
```Go ```Go
func wordBreak(s string,wordDict []string) bool { func wordBreak(s string,wordDict []string) bool {
wordDictSet:=make(map[string]bool) wordDictSet := make(map[string]bool)
for _,w:=range wordDict{ for _, w := range wordDict {
wordDictSet[w]=true wordDictSet[w] = true
} }
dp:=make([]bool,len(s)+1) dp := make([]bool, len(s)+1)
dp[0]=true dp[0] = true
for i:=1;i<=len(s);i++{ for i := 1; i <= len(s); i++ {
for j:=0;j<i;j++{ for j := 0; j < i; j++ {
if dp[j]&& wordDictSet[s[j:i]]{ if dp[j] && wordDictSet[s[j:i]] {
dp[i]=true dp[i] = true
break break
} }
} }

View File

@ -150,7 +150,7 @@ void removeExtraSpaces(string& s) {//去除所有空格并在相邻单词之间
代码如下: 代码如下:
```CPP ```CPP
// 反转字符串s中左闭闭的区间[start, end] // 反转字符串s中左闭闭的区间[start, end]
void reverse(string& s, int start, int end) { void reverse(string& s, int start, int end) {
for (int i = start, j = end; i < j; i++, j--) { for (int i = start, j = end; i < j; i++, j--) {
swap(s[i], s[j]); swap(s[i], s[j]);
@ -163,7 +163,7 @@ void reverse(string& s, int start, int end) {
```CPP ```CPP
class Solution { class Solution {
public: public:
void reverse(string& s, int start, int end){ //翻转,区间写法:左闭闭 [] void reverse(string& s, int start, int end){ //翻转,区间写法:左闭闭 []
for (int i = start, j = end; i < j; i++, j--) { for (int i = start, j = end; i < j; i++, j--) {
swap(s[i], s[j]); swap(s[i], s[j]);
} }

View File

@ -323,40 +323,6 @@ func max(a, b int) int {
} }
``` ```
```go
func maxProfit(k int, prices []int) int {
if len(prices)==0{
return 0
}
dp:=make([][]int,len(prices))
for i:=0;i<len(prices);i++{
dp[i]=make([]int,2*k+1)
}
for i:=1;i<len(dp[0]);i++{
if i%2!=0{
dp[0][i]=-prices[0]
}
}
for i:=1;i<len(prices);i++{
dp[i][0]=dp[i-1][0]
for j:=1;j<len(dp[0]);j++{
if j%2!=0{
dp[i][j]=max(dp[i-1][j],dp[i-1][j-1]-prices[i])
}else {
dp[i][j]=max(dp[i-1][j],dp[i-1][j-1]+prices[i])
}
}
}
return dp[len(prices)-1][2*k]
}
func max(a,b int)int{
if a>b{
return a
}
return b
}
```
Javascript: Javascript:
```javascript ```javascript

View File

@ -52,7 +52,7 @@
如果偷第i房间那么dp[i] = dp[i - 2] + nums[i] 第i-1房一定是不考虑的找出 下标i-2包括i-2以内的房屋最多可以偷窃的金额为dp[i-2] 加上第i房间偷到的钱。 如果偷第i房间那么dp[i] = dp[i - 2] + nums[i] 第i-1房一定是不考虑的找出 下标i-2包括i-2以内的房屋最多可以偷窃的金额为dp[i-2] 加上第i房间偷到的钱。
如果不偷第i房间那么dp[i] = dp[i - 1]即考虑i-1房**注意这里是考虑并不是一定要偷i-1房这是很多同学容易混淆的点** 如果不偷第i房间那么dp[i] = dp[i - 1],即考 虑i-1房**注意这里是考虑并不是一定要偷i-1房这是很多同学容易混淆的点**
然后dp[i]取最大值即dp[i] = max(dp[i - 2] + nums[i], dp[i - 1]); 然后dp[i]取最大值即dp[i] = max(dp[i - 2] + nums[i], dp[i - 1]);
@ -154,29 +154,20 @@ class Solution:
Go Go
```Go ```Go
func rob(nums []int) int { func rob(nums []int) int {
if len(nums)<1{ n := len(nums)
return 0 dp := make([]int, n+1) // dp[i]表示偷到第i家能够偷得的最大金额
} dp[1] = nums[0]
if len(nums)==1{ for i := 2; i <= n; i++ {
return nums[0] dp[i] = max(dp[i-1], dp[i-2] + nums[i-1])
} }
if len(nums)==2{ return dp[n]
return max(nums[0],nums[1])
}
dp :=make([]int,len(nums))
dp[0]=nums[0]
dp[1]=max(nums[0],nums[1])
for i:=2;i<len(nums);i++{
dp[i]=max(dp[i-2]+nums[i],dp[i-1])
}
return dp[len(dp)-1]
} }
func max(a, b int) int { func max(a, b int) int {
if a>b{ if a > b {
return a return a
} }
return b return b
} }
``` ```

View File

@ -131,7 +131,7 @@ class Solution:
val2=self.roblist(nums[:-1])#不偷最后一间房 val2=self.roblist(nums[:-1])#不偷最后一间房
return max(val1,val2) return max(val1,val2)
def robRange(self,nums): def roblist(self,nums):
l=len(nums) l=len(nums)
dp=[0]*l dp=[0]*l
dp[0]=nums[0] dp[0]=nums[0]
@ -143,6 +143,44 @@ class Solution:
return dp[-1] return dp[-1]
``` ```
Go
```go
// 打家劫舍Ⅱ 动态规划
// 时间复杂度O(n) 空间复杂度O(n)
func rob(nums []int) int {
if len(nums) == 1 {
return nums[0]
}
if len(nums) == 2 {
return max(nums[0], nums[1])
}
result1 := robRange(nums, 0)
result2 := robRange(nums, 1)
return max(result1, result2)
}
// 偷盗指定的范围
func robRange(nums []int, start int) int {
dp := make([]int, len(nums))
dp[1] = nums[start]
for i := 2; i < len(nums); i++ {
dp[i] = max(dp[i - 2] + nums[i - 1 + start], dp[i - 1])
}
return dp[len(nums) - 1]
}
func max(a, b int) int {
if a > b {
return a
}
return b
}
```
javascipt: javascipt:
```javascript ```javascript
var rob = function(nums) { var rob = function(nums) {
@ -187,44 +225,6 @@ function robRange(nums: number[], start: number, end: number): number {
} }
``` ```
Go
```go
// 打家劫舍Ⅱ 动态规划
// 时间复杂度O(n) 空间复杂度O(n)
func rob(nums []int) int {
if len(nums) == 1 {
return nums[0]
}
if len(nums) == 2 {
return max(nums[0], nums[1])
}
result1 := robRange(nums, 0)
result2 := robRange(nums, 1)
return max(result1, result2)
}
// 偷盗指定的范围
func robRange(nums []int, start int) int {
dp := make([]int, len(nums))
dp[1] = nums[start]
for i := 2; i < len(nums); i++ {
dp[i] = max(dp[i - 2] + nums[i - 1 + start], dp[i - 1])
}
return dp[len(nums) - 1]
}
func max(a, b int) int {
if a > b {
return a
}
return b
}
```
<p align="center"> <p align="center">

View File

@ -479,28 +479,36 @@ function combinationSum3(k: number, n: number): number[][] {
```Rust ```Rust
impl Solution { impl Solution {
fn backtracking(result: &mut Vec<Vec<i32>>, path:&mut Vec<i32>, targetSum:i32, k: i32, mut sum: i32, startIndex: i32) { pub fn combination_sum3(k: i32, n: i32) -> Vec<Vec<i32>> {
let mut result = vec![];
let mut path = vec![];
Self::backtrace(&mut result, &mut path, n, k, 0, 1);
result
}
pub fn backtrace(
result: &mut Vec<Vec<i32>>,
path: &mut Vec<i32>,
target_sum: i32,
k: i32,
sum: i32,
start_index: i32,
) {
if sum > target_sum {
return;
}
let len = path.len() as i32; let len = path.len() as i32;
if len == k { if len == k {
if sum == targetSum { if sum == target_sum {
result.push(path.to_vec()); result.push(path.to_vec());
} }
return; return;
} }
for i in startIndex..=9 { for i in start_index..=9 - (k - len) + 1 {
sum += i;
path.push(i); path.push(i);
Self::backtracking(result, path, targetSum, k, sum, i+1); Self::backtrace(result, path, target_sum, k, sum + i, i + 1);
sum -= i;
path.pop(); path.pop();
} }
} }
pub fn combination_sum3(k: i32, n: i32) -> Vec<Vec<i32>> {
let mut result: Vec<Vec<i32>> = Vec::new();
let mut path: Vec<i32> = Vec::new();
Self::backtracking(&mut result, &mut path, n, k, 0, 1);
result
}
} }
``` ```

View File

@ -322,6 +322,18 @@ class Solution:
return root return root
``` ```
递归法:后序遍历:
```python
class Solution:
def invertTree(self, root: TreeNode) -> TreeNode:
if root is None:
return None
self.invertTree(root.left)
self.invertTree(root.right)
root.left, root.right = root.right, root.left
return root
```
迭代法:深度优先遍历(前序遍历): 迭代法:深度优先遍历(前序遍历):
```python ```python
class Solution: class Solution:
@ -359,7 +371,22 @@ class Solution:
queue.append(node.right) queue.append(node.right)
return root 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)
return root
```
### Go ### Go
递归版本的前序遍历 递归版本的前序遍历

View File

@ -429,6 +429,67 @@ object Solution {
} }
``` ```
## rust
递归:
```rust
impl Solution {
pub fn lowest_common_ancestor(
root: Option<Rc<RefCell<TreeNode>>>,
p: Option<Rc<RefCell<TreeNode>>>,
q: Option<Rc<RefCell<TreeNode>>>,
) -> Option<Rc<RefCell<TreeNode>>> {
let q_val = q.as_ref().unwrap().borrow().val;
let p_val = p.as_ref().unwrap().borrow().val;
let root_val = root.as_ref().unwrap().borrow().val;
if root_val > q_val && root_val > p_val {
return Self::lowest_common_ancestor(
root.as_ref().unwrap().borrow().left.clone(),
p,
q,
);
};
if root_val < q_val && root_val < p_val {
return Self::lowest_common_ancestor(
root.as_ref().unwrap().borrow().right.clone(),
p,
q,
);
}
root
}
}
```
迭代:
```rust
impl Solution {
pub fn lowest_common_ancestor(
mut root: Option<Rc<RefCell<TreeNode>>>,
p: Option<Rc<RefCell<TreeNode>>>,
q: Option<Rc<RefCell<TreeNode>>>,
) -> Option<Rc<RefCell<TreeNode>>> {
let p_val = p.unwrap().borrow().val;
let q_val = q.unwrap().borrow().val;
while let Some(node) = root.clone() {
let root_val = node.borrow().val;
if root_val > q_val && root_val > p_val {
root = node.borrow().left.clone();
} else if root_val < q_val && root_val < p_val {
root = node.borrow().right.clone();
} else {
return root;
}
}
None
}
}
```
<p align="center"> <p align="center">
<a href="https://programmercarl.com/other/kstar.html" target="_blank"> <a href="https://programmercarl.com/other/kstar.html" target="_blank">

View File

@ -74,7 +74,7 @@ public:
大家此时应该陷入深思..... 大家此时应该陷入深思.....
**其实队列没有必要维护窗口里的所有元素,只需要维护有可能成为窗口里最大值的元素就可以了,同时保证队里的元素数值是由大到小的。** **其实队列没有必要维护窗口里的所有元素,只需要维护有可能成为窗口里最大值的元素就可以了,同时保证队里的元素数值是由大到小的。**
那么这个维护元素单调递减的队列就叫做**单调队列即单调递减或单调递增的队列。C++中没有直接支持单调队列,需要我们自己来实现一个单调队列** 那么这个维护元素单调递减的队列就叫做**单调队列即单调递减或单调递增的队列。C++中没有直接支持单调队列,需要我们自己来实现一个单调队列**

View File

@ -36,9 +36,9 @@
首先通过本题大家要明确什么是子序列,“子序列是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序”。 首先通过本题大家要明确什么是子序列,“子序列是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序”。
本题也是代码随想录中子序列问题的第一题,如果没接触过这种题目的话,本题还是很难的,甚至想暴力去搜索也不知道怎么搜。 本题也是代码随想录中子序列问题的第一题,如果没接触过这种题目的话,本题还是很难的,甚至想暴力去搜索也不知道怎么搜。
子序列问题是动态规划解决的经典问题当前下标i的递增子序列长度其实和i之前的下表j的子序列长度有关系又是什么样的关系呢。 子序列问题是动态规划解决的经典问题当前下标i的递增子序列长度其实和i之前的下表j的子序列长度有关系那又是什么样的关系呢。
接下来,我们依然用动规五部曲来分析详细一波: 接下来,我们依然用动规五部曲来详细分析一波:
1. dp[i]的定义 1. dp[i]的定义
@ -46,7 +46,7 @@
**dp[i]表示i之前包括i的以nums[i]结尾的最长递增子序列的长度** **dp[i]表示i之前包括i的以nums[i]结尾的最长递增子序列的长度**
为什么一定表示 “以nums[i]结尾的最长递增子序” ,因为我们在 做 递增比较的时候,如果比较 nums[j] 和 nums[i] 的大小那么两个递增子序列一定分别以nums[j]为结尾 和 nums[i]为结尾, 要不然这个比较就没有意义了,不是尾部元素的比较那么 如算递增呢。 为什么一定表示 “以nums[i]结尾的最长递增子序” ,因为我们在 做 递增比较的时候,如果比较 nums[j] 和 nums[i] 的大小那么两个递增子序列一定分别以nums[j]为结尾 和 nums[i]为结尾, 要不然这个比较就没有意义了,不是尾部元素的比较那么 如算递增呢。
2. 状态转移方程 2. 状态转移方程
@ -155,31 +155,6 @@ class Solution:
``` ```
Go Go
```go
func lengthOfLIS(nums []int ) int {
dp := []int{}
for _, num := range nums {
if len(dp) ==0 || dp[len(dp) - 1] < num {
dp = append(dp, num)
} else {
l, r := 0, len(dp) - 1
pos := r
for l <= r {
mid := (l + r) >> 1
if dp[mid] >= num {
pos = mid;
r = mid - 1
} else {
l = mid + 1
}
}
dp[pos] = num
}//二分查找
}
return len(dp)
}
```
```go ```go
// 动态规划求解 // 动态规划求解
func lengthOfLIS(nums []int) int { func lengthOfLIS(nums []int) int {
@ -212,21 +187,29 @@ func max(x, y int) int {
return y return y
} }
``` ```
贪心+二分 优化
Rust: ```go
```rust func lengthOfLIS(nums []int ) int {
pub fn length_of_lis(nums: Vec<i32>) -> i32 { dp := []int{}
let mut dp = vec![1; nums.len() + 1]; for _, num := range nums {
let mut result = 1; if len(dp) == 0 || dp[len(dp) - 1] < num {
for i in 1..nums.len() { dp = append(dp, num)
for j in 0..i { } else {
if nums[j] < nums[i] { l, r := 0, len(dp) - 1
dp[i] = dp[i].max(dp[j] + 1); pos := r
} for l <= r {
result = result.max(dp[i]); mid := (l + r) >> 1
} if dp[mid] >= num {
} pos = mid;
result r = mid - 1
} else {
l = mid + 1
}
}
dp[pos] = num
}//二分查找
}
return len(dp)
} }
``` ```
@ -270,6 +253,22 @@ function lengthOfLIS(nums: number[]): number {
}; };
``` ```
Rust:
```rust
pub fn length_of_lis(nums: Vec<i32>) -> i32 {
let mut dp = vec![1; nums.len() + 1];
let mut result = 1;
for i in 1..nums.len() {
for j in 0..i {
if nums[j] < nums[i] {
dp[i] = dp[i].max(dp[j] + 1);
}
result = result.max(dp[i]);
}
}
result
}
```

View File

@ -129,7 +129,7 @@ if (cur == NULL) return vector<int>{0, 0};
3. 确定遍历顺序 3. 确定遍历顺序
首先明确的是使用后序遍历。 因为通过递归函数的返回值来做下一步计算。 首先明确的是使用后序遍历。 因为通过递归函数的返回值来做下一步计算。
通过递归左节点,得到左节点偷与不偷的金钱。 通过递归左节点,得到左节点偷与不偷的金钱。
@ -147,7 +147,7 @@ vector<int> right = robTree(cur->right); // 右
4. 确定单层递归的逻辑 4. 确定单层递归的逻辑
如果是偷当前节点那么左右孩子就不能偷val1 = cur->val + left[0] + right[0]; **如果对下标含义不理解就回顾一下dp数组的含义** 如果是偷当前节点那么左右孩子就不能偷val1 = cur->val + left[0] + right[0]; **如果对下标含义不理解就回顾一下dp数组的含义**
如果不偷当前节点那么左右孩子就可以偷至于到底偷不偷一定是选一个最大的所以val2 = max(left[0], left[1]) + max(right[0], right[1]); 如果不偷当前节点那么左右孩子就可以偷至于到底偷不偷一定是选一个最大的所以val2 = max(left[0], left[1]) + max(right[0], right[1]);
@ -483,37 +483,6 @@ function robNode(node: TreeNode | null): MaxValueArr {
} }
``` ```
### Go
```go
// 打家劫舍Ⅲ 动态规划
// 时间复杂度O(n) 空间复杂度O(logn)
func rob(root *TreeNode) int {
dp := traversal(root)
return max(dp[0], dp[1])
}
func traversal(cur *TreeNode) []int {
if cur == nil {
return []int{0, 0}
}
dpL := traversal(cur.Left)
dpR := traversal(cur.Right)
val1 := cur.Val + dpL[0] + dpR[0] // 偷盗当前节点
val2 := max(dpL[0], dpL[1]) + max(dpR[0], dpR[1]) // 不偷盗当前节点
return []int{val2, val1}
}
func max(a, b int) int {
if a > b {
return a
}
return b
}
```
<p align="center"> <p align="center">

View File

@ -9,7 +9,7 @@
# 491.递增子序列 # 491.递增子序列
[力扣题目链接](https://leetcode.cn/problems/increasing-subsequences/) [力扣题目链接](https://leetcode.cn/problems/non-decreasing-subsequences/)
给定一个整型数组, 你的任务是找到所有该数组的递增子序列递增子序列的长度至少是2。 给定一个整型数组, 你的任务是找到所有该数组的递增子序列递增子序列的长度至少是2。
@ -399,7 +399,7 @@ var findSubsequences = function(nums) {
``` ```
## TypeScript ### TypeScript
```typescript ```typescript
function findSubsequences(nums: number[]): number[][] { function findSubsequences(nums: number[]): number[][] {
@ -545,7 +545,7 @@ int** findSubsequences(int* nums, int numsSize, int* returnSize, int** returnCol
} }
``` ```
## Swift ### Swift
```swift ```swift
func findSubsequences(_ nums: [Int]) -> [[Int]] { func findSubsequences(_ nums: [Int]) -> [[Int]] {
@ -576,7 +576,7 @@ func findSubsequences(_ nums: [Int]) -> [[Int]] {
``` ```
## Scala ### Scala
```scala ```scala
object Solution { object Solution {
@ -614,3 +614,4 @@ object Solution {
<a href="https://programmercarl.com/other/kstar.html" target="_blank"> <a href="https://programmercarl.com/other/kstar.html" target="_blank">
<img src="../pics/网站星球宣传海报.jpg" width="1000"/> <img src="../pics/网站星球宣传海报.jpg" width="1000"/>
</a> </a>

View File

@ -375,6 +375,55 @@ object Solution {
} }
``` ```
## rust
递归:
```rust
impl Solution {
pub fn convert_bst(root: Option<Rc<RefCell<TreeNode>>>) -> Option<Rc<RefCell<TreeNode>>> {
let mut pre = 0;
Self::traversal(&root, &mut pre);
root
}
pub fn traversal(cur: &Option<Rc<RefCell<TreeNode>>>, pre: &mut i32) {
if cur.is_none() {
return;
}
let mut node = cur.as_ref().unwrap().borrow_mut();
Self::traversal(&node.right, pre);
*pre += node.val;
node.val = *pre;
Self::traversal(&node.left, pre);
}
}
```
迭代:
```rust
impl Solution {
pub fn convert_bst(root: Option<Rc<RefCell<TreeNode>>>) -> Option<Rc<RefCell<TreeNode>>> {
let mut cur = root.clone();
let mut stack = vec![];
let mut pre = 0;
while !stack.is_empty() || cur.is_some() {
while let Some(node) = cur {
cur = node.borrow().right.clone();
stack.push(node);
}
if let Some(node) = stack.pop() {
pre += node.borrow().val;
node.borrow_mut().val = pre;
cur = node.borrow().left.clone();
}
}
root
}
}
```
<p align="center"> <p align="center">
<a href="https://programmercarl.com/other/kstar.html" target="_blank"> <a href="https://programmercarl.com/other/kstar.html" target="_blank">

View File

@ -473,6 +473,33 @@ object Solution {
} }
``` ```
## rust
// 递归
```rust
impl Solution {
pub fn trim_bst(
root: Option<Rc<RefCell<TreeNode>>>,
low: i32,
high: i32,
) -> Option<Rc<RefCell<TreeNode>>> {
root.as_ref()?;
let mut node = root.as_ref().unwrap().borrow_mut();
if node.val < low {
return Self::trim_bst(node.right.clone(), low, high);
}
if node.val > high {
return Self::trim_bst(node.left.clone(), low, high);
}
node.left = Self::trim_bst(node.left.clone(), low, high);
node.right = Self::trim_bst(node.right.clone(), low, high);
drop(node);
root
}
}
```
<p align="center"> <p align="center">
<a href="https://programmercarl.com/other/kstar.html" target="_blank"> <a href="https://programmercarl.com/other/kstar.html" target="_blank">

View File

@ -645,6 +645,66 @@ object Solution {
} }
``` ```
### rust
迭代:
```rust
impl Solution {
pub fn insert_into_bst(
root: Option<Rc<RefCell<TreeNode>>>,
val: i32,
) -> Option<Rc<RefCell<TreeNode>>> {
if root.is_none() {
return Some(Rc::new(RefCell::new(TreeNode::new(val))));
}
let mut cur = root.clone();
let mut pre = None;
while let Some(node) = cur.clone() {
pre = cur;
if node.borrow().val > val {
cur = node.borrow().left.clone();
} else {
cur = node.borrow().right.clone();
};
}
let r = Some(Rc::new(RefCell::new(TreeNode::new(val))));
let mut p = pre.as_ref().unwrap().borrow_mut();
if val < p.val {
p.left = r;
} else {
p.right = r;
}
root
}
}
```
递归:
```rust
impl Solution {
pub fn insert_into_bst(
root: Option<Rc<RefCell<TreeNode>>>,
val: i32,
) -> Option<Rc<RefCell<TreeNode>>> {
if let Some(node) = &root {
if node.borrow().val > val {
let left = Self::insert_into_bst(node.borrow_mut().left.take(), val);
node.borrow_mut().left = left;
} else {
let right = Self::insert_into_bst(node.borrow_mut().right.take(), val);
node.borrow_mut().right = right;
}
root
} else {
Some(Rc::new(RefCell::new(TreeNode::new(val))))
}
}
}
```
<p align="center"> <p align="center">
<a href="https://programmercarl.com/other/kstar.html" target="_blank"> <a href="https://programmercarl.com/other/kstar.html" target="_blank">

View File

@ -157,7 +157,7 @@ private:
## 其他语言版本 ## 其他语言版本
C: C:
```C ```C
typedef struct { typedef struct MyLinkedList {
int val; int val;
struct MyLinkedList* next; struct MyLinkedList* next;
}MyLinkedList; }MyLinkedList;
@ -233,7 +233,7 @@ void myLinkedListDeleteAtIndex(MyLinkedList* obj, int index) {
MyLinkedList *tmp = obj->next; MyLinkedList *tmp = obj->next;
if (tmp != NULL){ if (tmp != NULL){
obj->next = tmp->next; obj->next = tmp->next;
free(tmp) free(tmp);
} }
return; return;
} }

View File

@ -28,7 +28,7 @@
注意题目中说的子数组其实就是连续子序列 注意题目中说的子数组其实就是连续子序列
要求两个数组中最长重复子数组如果是暴力的解法 需要先两层for循环确定两个数组起始位置然后来一个循环可以是for或者while来从两个起始位置开始比较取得重复子数组的长度 要求两个数组中最长重复子数组如果是暴力的解法 只需要先两层for循环确定两个数组起始位置然后来一个循环可以是for或者while来从两个起始位置开始比较取得重复子数组的长度
本题其实是动规解决的经典题目我们只要想到 用二维数组可以记录两个字符串的所有比较情况这样就比较好推 递推公式了 本题其实是动规解决的经典题目我们只要想到 用二维数组可以记录两个字符串的所有比较情况这样就比较好推 递推公式了
动规五部曲分析如下 动规五部曲分析如下
@ -163,7 +163,7 @@ public:
当然可以就是实现起来麻烦一些 当然可以就是实现起来麻烦一些
如果定义 dp[i][j] 以下标i为结尾的A和以下标j 为结尾的B那么 第一行和第一列毕竟要行初始化如果nums1[i] nums2[0] 相同的话对应的 dp[i][0]就要初始为1 因为此时最长重复子数组为1 nums2[j] nums1[0]相同的话同理 如果定义 dp[i][j] 以下标i为结尾的A和以下标j 为结尾的B那么 第一行和第一列毕竟要行初始化如果nums1[i] nums2[0] 相同的话对应的 dp[i][0]就要初始为1 因为此时最长重复子数组为1 nums2[j] nums1[0]相同的话同理
所以代码如下 所以代码如下
@ -298,6 +298,29 @@ func findLength(A []int, B []int) int {
} }
return res return res
} }
// 滚动数组
func findLength(nums1 []int, nums2 []int) int {
n, m, res := len(nums1), len(nums2), 0
dp := make([]int, m+1)
for i := 1; i <= n; i++ {
for j := m; j >= 1; j-- {
if nums1[i-1] == nums2[j-1] {
dp[j] = dp[j-1] + 1
} else {
dp[j] = 0 // 注意这里不相等要赋值为0供下一层使用
}
res = max(res, dp[j])
}
}
return res
}
func max(a, b int) int {
if a > b {
return a
}
return b
}
``` ```
JavaScript JavaScript

View File

@ -577,6 +577,91 @@ object Solution {
result result
} }
} }
```
### Rust
```Rust
/// 版本一
impl Solution {
pub fn min_camera_cover(root: Option<Rc<RefCell<TreeNode>>>) -> i32 {
let mut res = 0;
if Self::traversal(&root, &mut res) == 0 {
res += 1;
}
res
}
pub fn traversal(cur: &Option<Rc<RefCell<TreeNode>>>, ans: &mut i32) -> i32 {
// 0 未覆盖 1 节点已设置摄像头 2 节点已覆盖
if let Some(node) = cur {
let node = node.borrow();
let left = Self::traversal(&node.left, ans);
let right = Self::traversal(&node.right, ans);
// 左右节点都被覆盖
if left == 2 && right == 2 {
return 0; // 无覆盖
}
// left == 0 right == 0 左右无覆盖
// left == 0 right == 1 左节点无覆盖 右节点有摄像头
// left == 1 right == 0 左节点有摄像头 左节点无覆盖
// left == 0 right == 2 左节点无覆盖 右节点有覆盖
// left == 2 right == 0 左节点有覆盖 右节点无覆盖
if left == 0 || right == 0 {
*ans += 1;
return 1;
}
// left == 1 right == 1 左节点有摄像头 右节点有摄像头
// left == 1 right == 2 左节点有摄像头 右节点覆盖
// left == 2 right == 1 左节点覆盖 右节点有摄像头
if left == 1 || right == 1 {
return 2; // 已覆盖
}
} else {
return 2;
}
-1
}
}
/// 版本二
enum NodeState {
NoCover = 0,
Camera = 1,
Covered = 2,
}
impl Solution {
pub fn min_camera_cover(root: Option<Rc<RefCell<TreeNode>>>) -> i32 {
let mut res = 0;
let state = Self::traversal(&root, &mut res);
match state {
NodeState::NoCover => res + 1,
_ => res,
}
}
pub fn traversal(cur: &Option<Rc<RefCell<TreeNode>>>, ans: &mut i32) -> NodeState {
if let Some(node) = cur {
let node = node.borrow();
let left_state = Self::traversal(&node.left, ans);
let right_state = Self::traversal(&node.right, ans);
match (left_state, right_state) {
(NodeState::NoCover, _) | (_, NodeState::NoCover) => {
*ans += 1;
NodeState::Camera
}
(NodeState::Camera, _) | (_, NodeState::Camera) => NodeState::Covered,
(_, _) => NodeState::NoCover,
}
} else {
NodeState::Covered
}
}
}
``` ```
<p align="center"> <p align="center">

View File

@ -49,7 +49,7 @@ dp[i][j]:长度为[0, i - 1]的字符串text1与长度为[0, j - 1]的字符
有同学会问:为什么要定义长度为[0, i - 1]的字符串text1定义为长度为[0, i]的字符串text1不香么 有同学会问:为什么要定义长度为[0, i - 1]的字符串text1定义为长度为[0, i]的字符串text1不香么
这样定义是为了后面代码实现方便,如果非要定义为长度为[0, i]的字符串text1也可以我在 [动态规划718. 最长重复子数组](https://programmercarl.com/0718.最长重复子数组.html) 中的「拓展」里 详细讲解了区别所在其实就是简化了dp数组第一行和第一列的初始化逻辑。 这样定义是为了后面代码实现方便,如果非要定义为长度为[0, i]的字符串text1也可以我在 [动态规划718. 最长重复子数组](https://programmercarl.com/0718.最长重复子数组.html) 中的「拓展」里 详细讲解了区别所在其实就是简化了dp数组第一行和第一列的初始化逻辑。
2. 确定递推公式 2. 确定递推公式
@ -240,27 +240,6 @@ func max(a,b int)int {
``` ```
Rust:
```rust
pub fn longest_common_subsequence(text1: String, text2: String) -> i32 {
let (n, m) = (text1.len(), text2.len());
let (s1, s2) = (text1.as_bytes(), text2.as_bytes());
let mut dp = vec![0; m + 1];
let mut last = vec![0; m + 1];
for i in 1..=n {
dp.swap_with_slice(&mut last);
for j in 1..=m {
dp[j] = if s1[i - 1] == s2[j - 1] {
last[j - 1] + 1
} else {
last[j].max(dp[j - 1])
};
}
}
dp[m]
}
```
Javascript Javascript
```javascript ```javascript
const longestCommonSubsequence = (text1, text2) => { const longestCommonSubsequence = (text1, text2) => {
@ -304,6 +283,26 @@ function longestCommonSubsequence(text1: string, text2: string): number {
}; };
``` ```
Rust:
```rust
pub fn longest_common_subsequence(text1: String, text2: String) -> i32 {
let (n, m) = (text1.len(), text2.len());
let (s1, s2) = (text1.as_bytes(), text2.as_bytes());
let mut dp = vec![0; m + 1];
let mut last = vec![0; m + 1];
for i in 1..=n {
dp.swap_with_slice(&mut last);
for j in 1..=m {
dp[j] = if s1[i - 1] == s2[j - 1] {
last[j - 1] + 1
} else {
last[j].max(dp[j - 1])
};
}
}
dp[m]
}
```