mirror of
https://github.com/youngyangyang04/leetcode-master.git
synced 2025-07-07 15:45:40 +08:00
Merge branch 'master' of github.com:youngyangyang04/leetcode-master
This commit is contained in:
@ -455,42 +455,31 @@ function letterCombinations(digits: string): string[] {
|
||||
## Rust
|
||||
|
||||
```Rust
|
||||
const map: [&str; 10] = [
|
||||
"", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz",
|
||||
];
|
||||
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();
|
||||
if len == index {
|
||||
result.push(s.to_string());
|
||||
return;
|
||||
}
|
||||
// 在保证不会越界的情况下使用unwrap()将Some()中的值提取出来
|
||||
let digit= digits.chars().nth(index).unwrap().to_digit(10).unwrap() as usize;
|
||||
let letters = map[digit];
|
||||
for i in letters.chars() {
|
||||
let digit = (digits.as_bytes()[index] - b'0') as usize;
|
||||
for i in map[digit].chars() {
|
||||
s.push(i);
|
||||
Self::backtracking(result, s, &map, &digits, index+1);
|
||||
Self::back_trace(result, s, digits, index + 1);
|
||||
s.pop();
|
||||
}
|
||||
}
|
||||
pub fn letter_combinations(digits: String) -> Vec<String> {
|
||||
if digits.len() == 0 {
|
||||
if digits.is_empty() {
|
||||
return vec![];
|
||||
}
|
||||
const MAP: [&str; 10] = [
|
||||
"",
|
||||
"",
|
||||
"abc",
|
||||
"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
|
||||
let mut res = vec![];
|
||||
let mut s = String::new();
|
||||
Self::back_trace(&mut res, &mut s, &digits, 0);
|
||||
res
|
||||
}
|
||||
}
|
||||
```
|
||||
|
@ -154,24 +154,25 @@ class Solution {
|
||||
```
|
||||
|
||||
```java
|
||||
// 虚拟头结点
|
||||
class Solution {
|
||||
public ListNode swapPairs(ListNode head) {
|
||||
|
||||
ListNode dummyNode = new ListNode(0);
|
||||
dummyNode.next = head;
|
||||
ListNode prev = dummyNode;
|
||||
|
||||
while (prev.next != null && prev.next.next != null) {
|
||||
ListNode temp = head.next.next; // 缓存 next
|
||||
prev.next = head.next; // 将 prev 的 next 改为 head 的 next
|
||||
head.next.next = head; // 将 head.next(prev.next) 的next,指向 head
|
||||
head.next = temp; // 将head 的 next 接上缓存的temp
|
||||
prev = head; // 步进1位
|
||||
head = head.next; // 步进1位
|
||||
ListNode dumyhead = new ListNode(-1); // 设置一个虚拟头结点
|
||||
dumyhead.next = head; // 将虚拟头结点指向head,这样方面后面做删除操作
|
||||
ListNode cur = dumyhead;
|
||||
ListNode temp; // 临时节点,保存两个节点后面的节点
|
||||
ListNode firstnode; // 临时节点,保存两个节点之中的第一个节点
|
||||
ListNode secondnode; // 临时节点,保存两个节点之中的第二个节点
|
||||
while (cur.next != null && cur.next.next != null) {
|
||||
temp = cur.next.next.next;
|
||||
firstnode = cur.next;
|
||||
secondnode = cur.next.next;
|
||||
cur.next = secondnode; // 步骤一
|
||||
secondnode.next = firstnode; // 步骤二
|
||||
firstnode.next = temp; // 步骤三
|
||||
cur = firstnode; // cur移动,准备下一轮交换
|
||||
}
|
||||
return dumyhead.next;
|
||||
}
|
||||
return dummyNode.next;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -267,22 +267,22 @@ Rust:
|
||||
|
||||
```Rust
|
||||
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;
|
||||
if len == k{
|
||||
result.push(path.to_vec());
|
||||
return;
|
||||
}
|
||||
// 此处剪枝
|
||||
for i in startIndex..= n - (k - len) + 1 {
|
||||
for i in start_index..= n - (k - len) + 1 {
|
||||
path.push(i);
|
||||
Self::backtracking(result, path, n, k, i+1);
|
||||
path.pop();
|
||||
}
|
||||
}
|
||||
pub fn combine(n: i32, k: i32) -> Vec<Vec<i32>> {
|
||||
let mut result: Vec<Vec<i32>> = Vec::new();
|
||||
let mut path: Vec<i32> = Vec::new();
|
||||
let mut result = vec![];
|
||||
let mut path = vec![];
|
||||
Self::backtracking(&mut result, &mut path, n, k, 1);
|
||||
result
|
||||
}
|
||||
|
@ -392,7 +392,26 @@ class Solution:
|
||||
return is_left_valid and is_right_valid
|
||||
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
|
||||
迭代-中序遍历
|
||||
class Solution:
|
||||
|
@ -271,6 +271,36 @@ func levelOrder(root *TreeNode) [][]int {
|
||||
|
||||
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代码:
|
||||
@ -1072,7 +1102,6 @@ public class N0637 {
|
||||
|
||||
que.offerLast(root);
|
||||
while (!que.isEmpty()) {
|
||||
TreeNode peek = que.peekFirst();
|
||||
|
||||
int levelSize = que.size();
|
||||
double levelSum = 0.0;
|
||||
@ -1346,6 +1375,22 @@ class Solution:
|
||||
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
|
||||
@ -2955,7 +3000,7 @@ impl Solution {
|
||||
* 107.二叉树的层次遍历II
|
||||
* 199.二叉树的右视图
|
||||
* 637.二叉树的层平均值
|
||||
* 429.N叉树的前序遍历
|
||||
* 429.N叉树的层序遍历
|
||||
* 515.在每个树行中找最大值
|
||||
* 116.填充每个节点的下一个右侧节点指针
|
||||
* 117.填充每个节点的下一个右侧节点指针II
|
||||
@ -2970,3 +3015,4 @@ impl Solution {
|
||||
<a href="https://programmercarl.com/other/kstar.html" target="_blank">
|
||||
<img src="../pics/网站星球宣传海报.jpg" width="1000"/>
|
||||
</a>
|
||||
|
||||
|
@ -402,10 +402,10 @@ class solution:
|
||||
def getdepth(self, node):
|
||||
if not node:
|
||||
return 0
|
||||
leftdepth = self.getdepth(node.left) #左
|
||||
rightdepth = self.getdepth(node.right) #右
|
||||
depth = 1 + max(leftdepth, rightdepth) #中
|
||||
return depth
|
||||
leftheight = self.getdepth(node.left) #左
|
||||
rightheight = self.getdepth(node.right) #右
|
||||
height = 1 + max(leftheight, rightheight) #中
|
||||
return height
|
||||
```
|
||||
|
||||
递归法:精简代码
|
||||
|
@ -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">
|
||||
<a href="https://programmercarl.com/other/kstar.html" target="_blank">
|
||||
|
@ -89,7 +89,7 @@ dp[i][1] 表示第i天不持有股票所得最多现金
|
||||
|
||||
**注意这里说的是“持有”,“持有”不代表就是当天“买入”!也有可能是昨天就买入了,今天保持持有的状态**
|
||||
|
||||
很多同学把“持有”和“买入”没分区分清楚。
|
||||
很多同学把“持有”和“买入”没区分清楚。
|
||||
|
||||
在下面递推公式分析中,我会进一步讲解。
|
||||
|
||||
@ -103,11 +103,11 @@ dp[i][1] 表示第i天不持有股票所得最多现金
|
||||
|
||||
如果第i天不持有股票即dp[i][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]);
|
||||
|
||||
这样递归公式我们就分析完了
|
||||
这样递推公式我们就分析完了
|
||||
|
||||
3. dp数组如何初始化
|
||||
|
||||
@ -121,7 +121,7 @@ dp[0][1]表示第0天不持有股票,不持有股票那么现金就是0,所
|
||||
|
||||
4. 确定遍历顺序
|
||||
|
||||
从递推公式可以看出dp[i]都是有dp[i - 1]推导出来的,那么一定是从前向后遍历。
|
||||
从递推公式可以看出dp[i]都是由dp[i - 1]推导出来的,那么一定是从前向后遍历。
|
||||
|
||||
5. 举例推导dp数组
|
||||
|
||||
@ -326,53 +326,40 @@ Go:
|
||||
> 贪心法:
|
||||
```Go
|
||||
func maxProfit(prices []int) int {
|
||||
low := math.MaxInt32
|
||||
rlt := 0
|
||||
for i := range prices{
|
||||
low = min(low, prices[i])
|
||||
rlt = max(rlt, prices[i]-low)
|
||||
min := prices[0]
|
||||
res := 0
|
||||
for i := 1; i < len(prices); i++ {
|
||||
if prices[i] - min > res {
|
||||
res = prices[i]-min
|
||||
}
|
||||
if min > prices[i] {
|
||||
min = prices[i]
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
return res
|
||||
}
|
||||
```
|
||||
|
||||
> 动态规划:版本一
|
||||
```Go
|
||||
func maxProfit(prices []int) int {
|
||||
length:=len(prices)
|
||||
if length==0{return 0}
|
||||
dp:=make([][]int,length)
|
||||
for i:=0;i<length;i++{
|
||||
dp[i]=make([]int,2)
|
||||
length := len(prices)
|
||||
if length == 0{return 0}
|
||||
dp := make([][]int,length)
|
||||
for i := 0; i < length; i++ {
|
||||
dp[i] = make([]int, 2)
|
||||
}
|
||||
|
||||
dp[0][0]=-prices[0]
|
||||
dp[0][1]=0
|
||||
for i:=1;i<length;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[0][0] = -prices[0]
|
||||
dp[0][1] = 0
|
||||
for i := 1; i < length; 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])
|
||||
}
|
||||
return dp[length-1][1]
|
||||
}
|
||||
|
||||
func max(a,b int)int {
|
||||
if a>b{
|
||||
func max(a, b int) int {
|
||||
if a > b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
@ -385,7 +372,7 @@ func maxProfit(prices []int) int {
|
||||
dp := [2][2]int{}
|
||||
dp[0][0] = -prices[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][1] = max(dp[(i-1)%2][1], dp[(i-1)%2][0]+prices[i])
|
||||
}
|
||||
|
@ -39,7 +39,7 @@
|
||||
本题我们在讲解贪心专题的时候就已经讲解过了[贪心算法:买卖股票的最佳时机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)一样一样的**。
|
||||
|
||||
@ -63,9 +63,9 @@
|
||||
|
||||
那么第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天卖出股票,所得现金就是按照今天股票佳价格卖出后所得现金即:prices[i] + dp[i - 1][0]
|
||||
* 第i天卖出股票,所得现金就是按照今天股票价格卖出后所得现金即:prices[i] + dp[i - 1][0]
|
||||
|
||||
**注意这里和[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]。
|
||||
|
||||
想到到这一点,对这两道题理解的比较深刻了。
|
||||
想到到这一点,对这两道题理解的就比较深刻了。
|
||||
|
||||
这里我依然给出滚动数组的版本,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
|
||||
// 方法一:动态规划(dp 数组)
|
||||
|
@ -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-1天 就买入了,那么 dp[i][1] 延续买入股票的这个状态。
|
||||
例如 dp[i][1] ,并不是说 第i天一定买入股票,有可能 第 i-1天 就买入了,那么 dp[i][1] 延续买入股票的这个状态。
|
||||
|
||||
2. 确定递推公式
|
||||
|
||||
@ -102,7 +102,7 @@ dp[i][4] = max(dp[i - 1][4], dp[i - 1][3] + prices[i]);
|
||||
|
||||
第0天第二次买入操作,初始值应该是多少呢?应该不少同学疑惑,第一次还没买入呢,怎么初始化第二次买入呢?
|
||||
|
||||
第二次买入依赖于第一次卖出的状态,其实相当于第0天第一次买入了,第一次卖出了,然后在买入一次(第二次买入),那么现在手头上没有现金,只要买入,现金就做相应的减少。
|
||||
第二次买入依赖于第一次卖出的状态,其实相当于第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]取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
|
||||
func maxProfit(prices []int) int {
|
||||
dp:=make([][]int,len(prices))
|
||||
for i:=0;i<len(prices);i++{
|
||||
dp[i]=make([]int,5)
|
||||
dp := make([][]int, len(prices))
|
||||
for i := 0; i < len(prices); i++ {
|
||||
dp[i] = make([]int, 5)
|
||||
}
|
||||
dp[0][0]=0
|
||||
dp[0][1]=-prices[0]
|
||||
dp[0][2]=0
|
||||
dp[0][3]=-prices[0]
|
||||
dp[0][4]=0
|
||||
for i:=1;i<len(prices);i++{
|
||||
dp[i][0]=dp[i-1][0]
|
||||
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])
|
||||
dp[i][4]=max(dp[i-1][4],dp[i-1][3]+prices[i])
|
||||
dp[0][0] = 0
|
||||
dp[0][1] = -prices[0]
|
||||
dp[0][2] = 0
|
||||
dp[0][3] = -prices[0]
|
||||
dp[0][4] = 0
|
||||
for i := 1; i < len(prices); i++ {
|
||||
dp[i][0] = dp[i-1][0]
|
||||
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])
|
||||
dp[i][4] = max(dp[i-1][4], dp[i-1][3] + prices[i])
|
||||
}
|
||||
return dp[len(prices)-1][4]
|
||||
}
|
||||
func max(a,b int)int{
|
||||
if a>b{
|
||||
func max(a, b int) int {
|
||||
if a > b {
|
||||
return a
|
||||
}
|
||||
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
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -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">
|
||||
<a href="https://programmercarl.com/other/kstar.html" target="_blank">
|
||||
|
@ -490,15 +490,15 @@ var partition = function(s) {
|
||||
const res = [], path = [], len = s.length;
|
||||
backtracking(0);
|
||||
return res;
|
||||
function backtracking(i) {
|
||||
if(i >= len) {
|
||||
function backtracking(startIndex) {
|
||||
if(startIndex >= len) {
|
||||
res.push(Array.from(path));
|
||||
return;
|
||||
}
|
||||
for(let j = i; j < len; j++) {
|
||||
if(!isPalindrome(s, i, j)) continue;
|
||||
path.push(s.slice(i, j + 1));
|
||||
backtracking(j + 1);
|
||||
for(let i = startIndex; i < len; i++) {
|
||||
if(!isPalindrome(s, startIndex, i)) continue;
|
||||
path.push(s.slice(startIndex, i + 1));
|
||||
backtracking(i + 1);
|
||||
path.pop();
|
||||
}
|
||||
}
|
||||
|
@ -138,7 +138,7 @@ public:
|
||||
|
||||
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]有没有意义呢?
|
||||
|
||||
@ -152,13 +152,13 @@ dp[0]表示如果字符串为空的话,说明出现在字典里。
|
||||
|
||||
题目中说是拆分为一个或多个在字典中出现的单词,所以这是完全背包。
|
||||
|
||||
还要讨论两层for循环的前后循序。
|
||||
还要讨论两层for循环的前后顺序。
|
||||
|
||||
**如果求组合数就是外层for循环遍历物品,内层for遍历背包**。
|
||||
|
||||
**如果求排列数就是外层for遍历背包,内层for循环遍历物品**。
|
||||
|
||||
我在这里做一个一个总结:
|
||||
我在这里做一个总结:
|
||||
|
||||
求组合数:[动态规划:518.零钱兑换II](https://programmercarl.com/0518.零钱兑换II.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" 是不可以的,那么我们就是强调物品之间顺序。
|
||||
|
||||
所以说,本题一定是 先遍历 背包,在遍历物品。
|
||||
所以说,本题一定是 先遍历 背包,再遍历物品。
|
||||
|
||||
5. 举例推导dp[i]
|
||||
|
||||
@ -209,7 +209,7 @@ public:
|
||||
|
||||
关于遍历顺序,再给大家讲一下为什么 先遍历物品再遍历背包不行。
|
||||
|
||||
这里可以给出先遍历物品在遍历背包的代码:
|
||||
这里可以给出先遍历物品再遍历背包的代码:
|
||||
|
||||
```CPP
|
||||
class Solution {
|
||||
@ -241,7 +241,7 @@ public:
|
||||
|
||||
最后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数组打印出来,对着递推公式一步一步去看,思路就清晰了。
|
||||
|
||||
@ -352,16 +352,16 @@ class Solution:
|
||||
Go:
|
||||
```Go
|
||||
func wordBreak(s string,wordDict []string) bool {
|
||||
wordDictSet:=make(map[string]bool)
|
||||
for _,w:=range wordDict{
|
||||
wordDictSet[w]=true
|
||||
wordDictSet := make(map[string]bool)
|
||||
for _, w := range wordDict {
|
||||
wordDictSet[w] = true
|
||||
}
|
||||
dp:=make([]bool,len(s)+1)
|
||||
dp[0]=true
|
||||
for i:=1;i<=len(s);i++{
|
||||
for j:=0;j<i;j++{
|
||||
if dp[j]&& wordDictSet[s[j:i]]{
|
||||
dp[i]=true
|
||||
dp := make([]bool, len(s)+1)
|
||||
dp[0] = true
|
||||
for i := 1; i <= len(s); i++ {
|
||||
for j := 0; j < i; j++ {
|
||||
if dp[j] && wordDictSet[s[j:i]] {
|
||||
dp[i] = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
@ -150,7 +150,7 @@ void removeExtraSpaces(string& s) {//去除所有空格并在相邻单词之间
|
||||
代码如下:
|
||||
|
||||
```CPP
|
||||
// 反转字符串s中左闭又闭的区间[start, end]
|
||||
// 反转字符串s中左闭右闭的区间[start, end]
|
||||
void reverse(string& s, int start, int end) {
|
||||
for (int i = start, j = end; i < j; i++, j--) {
|
||||
swap(s[i], s[j]);
|
||||
@ -163,7 +163,7 @@ void reverse(string& s, int start, int end) {
|
||||
```CPP
|
||||
class Solution {
|
||||
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--) {
|
||||
swap(s[i], s[j]);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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 - 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]);
|
||||
|
||||
@ -154,29 +154,20 @@ class Solution:
|
||||
Go:
|
||||
```Go
|
||||
func rob(nums []int) int {
|
||||
if len(nums)<1{
|
||||
return 0
|
||||
}
|
||||
if len(nums)==1{
|
||||
return nums[0]
|
||||
}
|
||||
if len(nums)==2{
|
||||
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]
|
||||
n := len(nums)
|
||||
dp := make([]int, n+1) // dp[i]表示偷到第i家能够偷得的最大金额
|
||||
dp[1] = nums[0]
|
||||
for i := 2; i <= n; i++ {
|
||||
dp[i] = max(dp[i-1], dp[i-2] + nums[i-1])
|
||||
}
|
||||
return dp[n]
|
||||
}
|
||||
|
||||
func max(a, b int) int {
|
||||
if a>b{
|
||||
return a
|
||||
}
|
||||
return b
|
||||
if a > b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -131,7 +131,7 @@ class Solution:
|
||||
val2=self.roblist(nums[:-1])#不偷最后一间房
|
||||
return max(val1,val2)
|
||||
|
||||
def robRange(self,nums):
|
||||
def roblist(self,nums):
|
||||
l=len(nums)
|
||||
dp=[0]*l
|
||||
dp[0]=nums[0]
|
||||
@ -143,6 +143,44 @@ class Solution:
|
||||
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:
|
||||
```javascript
|
||||
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">
|
||||
|
@ -479,28 +479,36 @@ function combinationSum3(k: number, n: number): number[][] {
|
||||
|
||||
```Rust
|
||||
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;
|
||||
if len == k {
|
||||
if sum == targetSum {
|
||||
if sum == target_sum {
|
||||
result.push(path.to_vec());
|
||||
}
|
||||
return;
|
||||
}
|
||||
for i in startIndex..=9 {
|
||||
sum += i;
|
||||
for i in start_index..=9 - (k - len) + 1 {
|
||||
path.push(i);
|
||||
Self::backtracking(result, path, targetSum, k, sum, i+1);
|
||||
sum -= i;
|
||||
Self::backtrace(result, path, target_sum, k, sum + i, i + 1);
|
||||
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
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -322,6 +322,18 @@ class Solution:
|
||||
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
|
||||
class Solution:
|
||||
@ -359,7 +371,22 @@ class Solution:
|
||||
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)
|
||||
return root
|
||||
```
|
||||
### Go
|
||||
|
||||
递归版本的前序遍历
|
||||
|
@ -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">
|
||||
<a href="https://programmercarl.com/other/kstar.html" target="_blank">
|
||||
|
@ -74,7 +74,7 @@ public:
|
||||
|
||||
大家此时应该陷入深思.....
|
||||
|
||||
**其实队列没有必要维护窗口里的所有元素,只需要维护有可能成为窗口里最大值的元素就可以了,同时保证队里的元素数值是由大到小的。**
|
||||
**其实队列没有必要维护窗口里的所有元素,只需要维护有可能成为窗口里最大值的元素就可以了,同时保证队列里的元素数值是由大到小的。**
|
||||
|
||||
那么这个维护元素单调递减的队列就叫做**单调队列,即单调递减或单调递增的队列。C++中没有直接支持单调队列,需要我们自己来实现一个单调队列**
|
||||
|
||||
|
@ -36,9 +36,9 @@
|
||||
首先通过本题大家要明确什么是子序列,“子序列是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序”。
|
||||
|
||||
本题也是代码随想录中子序列问题的第一题,如果没接触过这种题目的话,本题还是很难的,甚至想暴力去搜索也不知道怎么搜。
|
||||
子序列问题是动态规划解决的经典问题,当前下标i的递增子序列长度,其实和i之前的下表j的子序列长度有关系,那那又是什么样的关系呢。
|
||||
子序列问题是动态规划解决的经典问题,当前下标i的递增子序列长度,其实和i之前的下表j的子序列长度有关系,那又是什么样的关系呢。
|
||||
|
||||
接下来,我们依然用动规五部曲来分析详细一波:
|
||||
接下来,我们依然用动规五部曲来详细分析一波:
|
||||
|
||||
1. dp[i]的定义
|
||||
|
||||
@ -46,7 +46,7 @@
|
||||
|
||||
**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. 状态转移方程
|
||||
@ -155,31 +155,6 @@ class Solution:
|
||||
```
|
||||
|
||||
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
|
||||
// 动态规划求解
|
||||
func lengthOfLIS(nums []int) int {
|
||||
@ -212,21 +187,29 @@ func max(x, y int) int {
|
||||
return y
|
||||
}
|
||||
```
|
||||
|
||||
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
|
||||
贪心+二分 优化
|
||||
```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)
|
||||
}
|
||||
```
|
||||
|
||||
@ -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
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
@ -129,7 +129,7 @@ if (cur == NULL) return vector<int>{0, 0};
|
||||
|
||||
3. 确定遍历顺序
|
||||
|
||||
首先明确的是使用后序遍历。 因为通过递归函数的返回值来做下一步计算。
|
||||
首先明确的是使用后序遍历。 因为要通过递归函数的返回值来做下一步计算。
|
||||
|
||||
通过递归左节点,得到左节点偷与不偷的金钱。
|
||||
|
||||
@ -147,7 +147,7 @@ vector<int> right = robTree(cur->right); // 右
|
||||
|
||||
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]);
|
||||
|
||||
@ -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">
|
||||
|
@ -9,7 +9,7 @@
|
||||
|
||||
# 491.递增子序列
|
||||
|
||||
[力扣题目链接](https://leetcode.cn/problems/increasing-subsequences/)
|
||||
[力扣题目链接](https://leetcode.cn/problems/non-decreasing-subsequences/)
|
||||
|
||||
给定一个整型数组, 你的任务是找到所有该数组的递增子序列,递增子序列的长度至少是2。
|
||||
|
||||
@ -399,7 +399,7 @@ var findSubsequences = function(nums) {
|
||||
|
||||
```
|
||||
|
||||
## TypeScript
|
||||
### TypeScript
|
||||
|
||||
```typescript
|
||||
function findSubsequences(nums: number[]): number[][] {
|
||||
@ -545,7 +545,7 @@ int** findSubsequences(int* nums, int numsSize, int* returnSize, int** returnCol
|
||||
}
|
||||
```
|
||||
|
||||
## Swift
|
||||
### Swift
|
||||
|
||||
```swift
|
||||
func findSubsequences(_ nums: [Int]) -> [[Int]] {
|
||||
@ -576,7 +576,7 @@ func findSubsequences(_ nums: [Int]) -> [[Int]] {
|
||||
```
|
||||
|
||||
|
||||
## Scala
|
||||
### Scala
|
||||
|
||||
```scala
|
||||
object Solution {
|
||||
@ -614,3 +614,4 @@ object Solution {
|
||||
<a href="https://programmercarl.com/other/kstar.html" target="_blank">
|
||||
<img src="../pics/网站星球宣传海报.jpg" width="1000"/>
|
||||
</a>
|
||||
|
||||
|
@ -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">
|
||||
<a href="https://programmercarl.com/other/kstar.html" target="_blank">
|
||||
|
@ -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">
|
||||
<a href="https://programmercarl.com/other/kstar.html" target="_blank">
|
||||
|
@ -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">
|
||||
<a href="https://programmercarl.com/other/kstar.html" target="_blank">
|
||||
|
@ -157,7 +157,7 @@ private:
|
||||
## 其他语言版本
|
||||
C:
|
||||
```C
|
||||
typedef struct {
|
||||
typedef struct MyLinkedList {
|
||||
int val;
|
||||
struct MyLinkedList* next;
|
||||
}MyLinkedList;
|
||||
@ -233,7 +233,7 @@ void myLinkedListDeleteAtIndex(MyLinkedList* obj, int index) {
|
||||
MyLinkedList *tmp = obj->next;
|
||||
if (tmp != NULL){
|
||||
obj->next = tmp->next;
|
||||
free(tmp)
|
||||
free(tmp);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
// 滚动数组
|
||||
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:
|
||||
|
@ -577,6 +577,91 @@ object Solution {
|
||||
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">
|
||||
|
@ -49,7 +49,7 @@ dp[i][j]:长度为[0, i - 1]的字符串text1与长度为[0, j - 1]的字符
|
||||
|
||||
有同学会问:为什么要定义长度为[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. 确定递推公式
|
||||
|
||||
@ -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
|
||||
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]
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
Reference in New Issue
Block a user