This commit is contained in:
programmercarl
2023-04-21 22:50:16 +08:00
80 changed files with 2049 additions and 502 deletions

View File

@ -66,7 +66,7 @@
**这里每一篇题解,都是精品,值得仔细琢磨** **这里每一篇题解,都是精品,值得仔细琢磨**
我在题目讲解中统一使用C++但你会发现下面几乎每篇题解都配有其他语言版本Java、Python、Go、JavaScript等等正是这些[热心小伙们](https://github.com/youngyangyang04/leetcode-master/graphs/contributors)贡献的代码,当然我也会严格把控代码质量。 我在题目讲解中统一使用C++但你会发现下面几乎每篇题解都配有其他语言版本Java、Python、Go、JavaScript等等正是这些[热心小伙们](https://github.com/youngyangyang04/leetcode-master/graphs/contributors)贡献的代码,当然我也会严格把控代码质量。
**所以也欢迎大家参与进来,完善题解的各个语言版本,拥抱开源,让更多小伙伴们受益** **所以也欢迎大家参与进来,完善题解的各个语言版本,拥抱开源,让更多小伙伴们受益**

View File

@ -109,6 +109,9 @@ public:
}; };
``` ```
* 时间复杂度: O(n)
* 空间复杂度: O(n)
## 总结 ## 总结
本题其实有四个重点: 本题其实有四个重点:
@ -213,6 +216,26 @@ impl Solution {
} }
} }
``` ```
Rust
```
use std::collections::HashMap;
impl Solution {
pub fn two_sum(nums: Vec<i32>, target: i32) -> Vec<i32> {
let mut hm: HashMap<i32, i32> = HashMap::new();
for i in 0..nums.len() {
let j = target - nums[i];
if hm.contains_key(&j) {
return vec![*hm.get(&j).unwrap(), i as i32]
} else {
hm.insert(nums[i], i as i32);
}
}
vec![-1, -1]
}
}
```
Javascript Javascript

View File

@ -83,6 +83,10 @@ public:
}; };
``` ```
* 时间复杂度: O(n^2)
* 空间复杂度: O(n),额外的 set 开销
## 双指针 ## 双指针
**其实这道题目使用哈希法并不十分合适**因为在去重的操作中有很多细节需要注意在面试中很难直接写出没有bug的代码。 **其实这道题目使用哈希法并不十分合适**因为在去重的操作中有很多细节需要注意在面试中很难直接写出没有bug的代码。
@ -158,6 +162,10 @@ public:
}; };
``` ```
* 时间复杂度: O(n^2)
* 空间复杂度: O(1)
## 去重逻辑的思考 ## 去重逻辑的思考
### a的去重 ### a的去重

View File

@ -121,6 +121,10 @@ public:
``` ```
* 时间复杂度: O(n^3)
* 空间复杂度: O(1)
## 补充 ## 补充
二级剪枝的部分: 二级剪枝的部分:

View File

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

View File

@ -135,6 +135,9 @@ public:
}; };
``` ```
* 时间复杂度: O(n)
* 空间复杂度: O(n)
技巧性的东西没有固定的学习方法,还是要多看多练,自己灵活运用了。 技巧性的东西没有固定的学习方法,还是要多看多练,自己灵活运用了。

View File

@ -444,6 +444,8 @@ public:
}; };
``` ```
* 时间复杂度: O(n + m)
* 空间复杂度: O(m), 只需要保存字符串needle的前缀表
# 前缀表不减一C++实现 # 前缀表不减一C++实现
@ -540,6 +542,9 @@ public:
} }
}; };
``` ```
* 时间复杂度: O(n + m)
* 空间复杂度: O(m)
# 总结 # 总结

View File

@ -4,7 +4,6 @@
</a> </a>
<p align="center"><strong><a href="https://mp.weixin.qq.com/s/tqCxrMEU-ajQumL1i8im9A">参与本项目</a>,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!</strong></p> <p align="center"><strong><a href="https://mp.weixin.qq.com/s/tqCxrMEU-ajQumL1i8im9A">参与本项目</a>,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!</strong></p>
> 相对于[贪心算法:跳跃游戏](https://mp.weixin.qq.com/s/606_N9j8ACKCODoCbV1lSA)难了不少,做好心里准备! > 相对于[贪心算法:跳跃游戏](https://mp.weixin.qq.com/s/606_N9j8ACKCODoCbV1lSA)难了不少,做好心里准备!
# 45.跳跃游戏 II # 45.跳跃游戏 II
@ -18,13 +17,17 @@
你的目标是使用最少的跳跃次数到达数组的最后一个位置。 你的目标是使用最少的跳跃次数到达数组的最后一个位置。
示例: 示例:
* 输入: [2,3,1,1,4]
*: 2 -: [2,3,1,1,4]
* 解释: 跳到最后一个位置的最小跳跃数是 2。从下标为 0 跳到下标为 1 的位置 1 然后跳 3 步到达数组的最后一个位置。 - 输出: 2
- 解释: 跳到最后一个位置的最小跳跃数是 2。从下标为 0 跳到下标为 1 的位置,跳  1  步,然后跳  3  步到达数组的最后一个位置。
说明: 说明:
假设你总是可以到达数组的最后一个位置。 假设你总是可以到达数组的最后一个位置。
# 视频讲解
**《代码随想录》算法视频公开课:[贪心算法,最少跳几步还得看覆盖范围 | LeetCode 45.跳跃游戏 II](https://www.bilibili.com/video/BV1Y24y1r7XZ),相信结合视频在看本篇题解,更有助于大家对本题的理解**。
## 思路 ## 思路
@ -46,7 +49,6 @@
如图: 如图:
![45.跳跃游戏II](https://code-thinking-1253855093.file.myqcloud.com/pics/20201201232309103.png) ![45.跳跃游戏II](https://code-thinking-1253855093.file.myqcloud.com/pics/20201201232309103.png)
**图中覆盖范围的意义在于,只要红色的区域,最多两步一定可以到!(不用管具体怎么跳,反正一定可以跳到)** **图中覆盖范围的意义在于,只要红色的区域,最多两步一定可以到!(不用管具体怎么跳,反正一定可以跳到)**
@ -57,8 +59,8 @@
这里还是有个特殊情况需要考虑,当移动下标达到了当前覆盖的最远距离下标时 这里还是有个特殊情况需要考虑,当移动下标达到了当前覆盖的最远距离下标时
* 如果当前覆盖最远距离下标不是是集合终点,步数就加一,还需要继续走。 - 如果当前覆盖最远距离下标不是是集合终点,步数就加一,还需要继续走。
* 如果当前覆盖最远距离下标就是是集合终点,步数不用加一,因为不能再往后走了。 - 如果当前覆盖最远距离下标就是是集合终点,步数不用加一,因为不能再往后走了。
C++代码如下:(详细注释) C++代码如下:(详细注释)
@ -74,11 +76,9 @@ public:
for (int i = 0; i < nums.size(); i++) { for (int i = 0; i < nums.size(); i++) {
nextDistance = max(nums[i] + i, nextDistance); // 更新下一步覆盖最远距离下标 nextDistance = max(nums[i] + i, nextDistance); // 更新下一步覆盖最远距离下标
if (i == curDistance) { // 遇到当前覆盖最远距离下标 if (i == curDistance) { // 遇到当前覆盖最远距离下标
if (curDistance < nums.size() - 1) { // 如果当前覆盖最远距离下标不是终点
ans++; // 需要走下一步 ans++; // 需要走下一步
curDistance = nextDistance; // 更新当前覆盖最远距离下标(相当于加油了) curDistance = nextDistance; // 更新当前覆盖最远距离下标(相当于加油了)
if (nextDistance >= nums.size() - 1) break; // 下一步的覆盖范围已经可以达到终点,结束循环 if (nextDistance >= nums.size() - 1) break; // 当前覆盖最远距到达集合终点不用做ans++操作了,直接结束
} else break; // 当前覆盖最远距到达集合终点不用做ans++操作了,直接结束
} }
} }
return ans; return ans;
@ -86,6 +86,10 @@ public:
}; };
``` ```
* 时间复杂度: O(n)
* 空间复杂度: O(1)
## 方法二 ## 方法二
依然是贪心,思路和方法一差不多,代码可以简洁一些。 依然是贪心,思路和方法一差不多,代码可以简洁一些。
@ -96,10 +100,10 @@ public:
因为当移动下标指向 nums.size - 2 时: 因为当移动下标指向 nums.size - 2 时:
* 如果移动下标等于当前覆盖最大距离下标, 需要再走一步即ans++),因为最后一步一定是可以到的终点。(题目假设总是可以到达数组的最后一个位置),如图: - 如果移动下标等于当前覆盖最大距离下标, 需要再走一步(即 ans++),因为最后一步一定是可以到的终点。(题目假设总是可以到达数组的最后一个位置),如图:
![45.跳跃游戏II2](https://code-thinking-1253855093.file.myqcloud.com/pics/20201201232445286.png) ![45.跳跃游戏II2](https://code-thinking-1253855093.file.myqcloud.com/pics/20201201232445286.png)
* 如果移动下标不等于当前覆盖最大距离下标,说明当前覆盖最远距离就可以直接达到终点了,不需要再走一步。如图: - 如果移动下标不等于当前覆盖最大距离下标,说明当前覆盖最远距离就可以直接达到终点了,不需要再走一步。如图:
![45.跳跃游戏II1](https://code-thinking-1253855093.file.myqcloud.com/pics/20201201232338693.png) ![45.跳跃游戏II1](https://code-thinking-1253855093.file.myqcloud.com/pics/20201201232338693.png)
@ -125,6 +129,11 @@ public:
}; };
``` ```
* 时间复杂度: O(n)
* 空间复杂度: O(1)
可以看出版本二的代码相对于版本一简化了不少! 可以看出版本二的代码相对于版本一简化了不少!
**其精髓在于控制移动下标 i 只移动到 nums.size() - 2 的位置**,所以移动下标只要遇到当前覆盖最远距离的下标,直接步数加一,不用考虑别的了。 **其精髓在于控制移动下标 i 只移动到 nums.size() - 2 的位置**,所以移动下标只要遇到当前覆盖最远距离的下标,直接步数加一,不用考虑别的了。
@ -137,11 +146,10 @@ public:
理解本题的关键在于:**以最小的步数增加最大的覆盖范围,直到覆盖范围覆盖了终点**,这个范围内最小步数一定可以跳到,不用管具体是怎么跳的,不纠结于一步究竟跳一个单位还是两个单位。 理解本题的关键在于:**以最小的步数增加最大的覆盖范围,直到覆盖范围覆盖了终点**,这个范围内最小步数一定可以跳到,不用管具体是怎么跳的,不纠结于一步究竟跳一个单位还是两个单位。
## 其他语言版本 ## 其他语言版本
### Java ### Java
```Java ```Java
// 版本一 // 版本一
class Solution { class Solution {
@ -230,6 +238,22 @@ class Solution:
step += 1 step += 1
return step 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 ```python
# 动态规划做法 # 动态规划做法
class Solution: class Solution:
@ -244,7 +268,6 @@ class Solution:
``` ```
### Go ### Go
```go ```go
@ -345,7 +368,7 @@ function jump(nums: number[]): number {
curIndex++; curIndex++;
} }
return stepNum; return stepNum;
}; }
``` ```
### Scala ### Scala
@ -379,23 +402,25 @@ object Solution {
```Rust ```Rust
//版本一 //版本一
impl Solution { impl Solution {
fn max(a: i32, b:i32) -> i32 {
if a > b { a } else { b }
}
pub fn jump(nums: Vec<i32>) -> i32 { pub fn jump(nums: Vec<i32>) -> i32 {
if nums.len() == 0 { return 0; } if nums.len() == 1 {
let mut cur_distance: i32 = 0; return 0;
let mut ans: i32 = 0; }
let mut next_distance: i32 = 0; let mut cur_distance = 0;
for i in 0..nums.len() { let mut ans = 0;
next_distance = Self::max(nums[i] + i as i32, next_distance); let mut next_distance = 0;
if i as i32 == cur_distance { for (i, &n) in nums.iter().enumerate().take(nums.len() - 1) {
if cur_distance != (nums.len() - 1) as i32 { next_distance = (n as usize + i).max(next_distance);
if i == cur_distance {
if cur_distance < nums.len() - 1 {
ans += 1; ans += 1;
cur_distance = next_distance; cur_distance = next_distance;
if next_distance == (nums.len() - 1) as i32 { break; } if next_distance >= nums.len() - 1 {
break;
};
} else {
break;
} }
else { break; }
} }
} }
ans ans
@ -406,16 +431,16 @@ impl Solution {
```Rust ```Rust
//版本二 //版本二
impl Solution { impl Solution {
fn max(a: i32, b:i32) -> i32 {
if a > b { a } else { b }
}
pub fn jump(nums: Vec<i32>) -> i32 { pub fn jump(nums: Vec<i32>) -> i32 {
let mut cur_distance: i32 = 0; if nums.len() == 1 {
let mut ans: i32 = 0; return 0;
let mut next_distance: i32 = 0; }
for i in 0..nums.len() - 1 { let mut cur_distance = 0;
next_distance = Self::max(nums[i] + i as i32, next_distance); let mut ans = 0;
if i as i32 == cur_distance { let mut next_distance = 0;
for (i, &n) in nums.iter().enumerate().take(nums.len() - 1) {
next_distance = (n as usize + i).max(next_distance);
if i == cur_distance {
cur_distance = next_distance; cur_distance = next_distance;
ans += 1; ans += 1;
} }
@ -425,7 +450,6 @@ impl Solution {
} }
``` ```
<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">
<img src="../pics/网站星球宣传海报.jpg" width="1000"/> <img src="../pics/网站星球宣传海报.jpg" width="1000"/>

View File

@ -4,7 +4,6 @@
</a> </a>
<p align="center"><strong><a href="https://mp.weixin.qq.com/s/tqCxrMEU-ajQumL1i8im9A">参与本项目</a>,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!</strong></p> <p align="center"><strong><a href="https://mp.weixin.qq.com/s/tqCxrMEU-ajQumL1i8im9A">参与本项目</a>,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!</strong></p>
# 53. 最大子序和 # 53. 最大子序和
[力扣题目链接](https://leetcode.cn/problems/maximum-subarray/) [力扣题目链接](https://leetcode.cn/problems/maximum-subarray/)
@ -12,17 +11,19 @@
给定一个整数数组 nums 找到一个具有最大和的连续子数组子数组最少包含一个元素返回其最大和。 给定一个整数数组 nums 找到一个具有最大和的连续子数组子数组最少包含一个元素返回其最大和。
示例: 示例:
* 输入: [-2,1,-3,4,-1,2,1,-5,4]
* 输出: 6
* 解释: 连续子数组 [4,-1,2,1] 的和最大 6。
- 输入: [-2,1,-3,4,-1,2,1,-5,4]
- 输出: 6
- 解释:  连续子数组  [4,-1,2,1] 的和最大,为  6。
# 视频讲解
**《代码随想录》算法视频公开课:[贪心算法的巧妙需要慢慢体会LeetCode53. 最大子序和](https://www.bilibili.com/video/BV1aY4y1Z7ya),相信结合视频在看本篇题解,更有助于大家对本题的理解**。
## 暴力解法 ## 暴力解法
暴力解法的思路,第一层 for 就是设置起始位置,第二层 for 循环遍历数组寻找最大值 暴力解法的思路,第一层 for 就是设置起始位置,第二层 for 循环遍历数组寻找最大值
* 时间复杂度O(n^2)
* 空间复杂度O(1)
```CPP ```CPP
class Solution { class Solution {
@ -41,6 +42,9 @@ public:
} }
}; };
``` ```
* 时间复杂度O(n^2)
* 空间复杂度O(1)
以上暴力的解法 C++勉强可以过,其他语言就不确定了。 以上暴力的解法 C++勉强可以过,其他语言就不确定了。
@ -56,12 +60,10 @@ public:
**局部最优的情况下,并记录最大的“连续和”,可以推出全局最优** **局部最优的情况下,并记录最大的“连续和”,可以推出全局最优**
从代码角度上来讲:遍历 nums从头开始用 count 累积,如果 count 一旦加上 nums[i]变为负数,那么就应该从 nums[i+1]开始从 0 累积 count 了,因为已经变为负数的 count只会拖累总和。 从代码角度上来讲:遍历 nums从头开始用 count 累积,如果 count 一旦加上 nums[i]变为负数,那么就应该从 nums[i+1]开始从 0 累积 count 了,因为已经变为负数的 count只会拖累总和。
**这相当于是暴力解法中的不断调整最大子序和区间的起始位置** **这相当于是暴力解法中的不断调整最大子序和区间的起始位置**
**那有同学问了,区间终止位置不用调整么? 如何才能得到最大“连续和”呢?** **那有同学问了,区间终止位置不用调整么? 如何才能得到最大“连续和”呢?**
区间的终止位置,其实就是如果 count 取到最大值了,及时记录下来了。例如如下代码: 区间的终止位置,其实就是如果 count 取到最大值了,及时记录下来了。例如如下代码:
@ -97,20 +99,17 @@ public:
} }
}; };
``` ```
- 时间复杂度O(n)
* 间复杂度O(n) - 间复杂度O(1)
* 空间复杂度O(1)
当然题目没有说如果数组为空,应该返回什么,所以数组为空的话返回啥都可以了。 当然题目没有说如果数组为空,应该返回什么,所以数组为空的话返回啥都可以了。
## 常见误区 ## 常见误区
误区一: 误区一:
不少同学认为 如果输入用例都是-1或者 都是负数,这个贪心算法跑出来的结果是 0 这是**又一次证明脑洞模拟不靠谱的经典案例**,建议大家把代码运行一下试一试,就知道了,也会理解 为什么 result 要初始化为最小负数了。 不少同学认为 如果输入用例都是-1或者 都是负数,这个贪心算法跑出来的结果是 0 这是**又一次证明脑洞模拟不靠谱的经典案例**,建议大家把代码运行一下试一试,就知道了,也会理解 为什么 result 要初始化为最小负数了。
误区二: 误区二:
大家在使用贪心算法求解本题,经常陷入的误区,就是分不清,是遇到 负数就选择起始位置,还是连续和为负选择起始位置。 大家在使用贪心算法求解本题,经常陷入的误区,就是分不清,是遇到 负数就选择起始位置,还是连续和为负选择起始位置。
@ -123,8 +122,6 @@ public:
其实并不会,因为还有一个变量 result 一直在更新 最大的连续和只要有更大的连续和出现result 就更新了,那么 result 已经把 4 更新了,后面 连续和变成 3也不会对最后结果有影响。 其实并不会,因为还有一个变量 result 一直在更新 最大的连续和只要有更大的连续和出现result 就更新了,那么 result 已经把 4 更新了,后面 连续和变成 3也不会对最后结果有影响。
## 动态规划 ## 动态规划
当然本题还可以用动态规划来做,当前[「代码随想录」](https://img-blog.csdnimg.cn/20201124161234338.png)主要讲解贪心系列,后续到动态规划系列的时候会详细讲解本题的 dp 方法。 当然本题还可以用动态规划来做,当前[「代码随想录」](https://img-blog.csdnimg.cn/20201124161234338.png)主要讲解贪心系列,后续到动态规划系列的时候会详细讲解本题的 dp 方法。
@ -148,8 +145,8 @@ public:
}; };
``` ```
* 时间复杂度O(n) - 时间复杂度O(n)
* 空间复杂度O(n) - 空间复杂度O(n)
## 总结 ## 总结
@ -159,8 +156,8 @@ public:
## 其他语言版本 ## 其他语言版本
### Java ### Java
```java ```java
class Solution { class Solution {
public int maxSubArray(int[] nums) { public int maxSubArray(int[] nums) {
@ -201,6 +198,7 @@ class Solution {
``` ```
### Python ### Python
```python ```python
class Solution: class Solution:
def maxSubArray(self, nums: List[int]) -> int: def maxSubArray(self, nums: List[int]) -> int:
@ -233,6 +231,7 @@ func maxSubArray(nums []int) int {
``` ```
### Rust ### Rust
```rust ```rust
pub fn max_sub_array(nums: Vec<i32>) -> i32 { pub fn max_sub_array(nums: Vec<i32>) -> i32 {
let mut max_sum = i32::MIN; let mut max_sum = i32::MIN;
@ -247,6 +246,7 @@ pub fn max_sub_array(nums: Vec<i32>) -> i32 {
``` ```
### Javascript: ### Javascript:
```Javascript ```Javascript
var maxSubArray = function(nums) { var maxSubArray = function(nums) {
let result = -Infinity let result = -Infinity
@ -264,9 +264,10 @@ var maxSubArray = function(nums) {
}; };
``` ```
### C: ### C:
贪心: 贪心:
```c ```c
int maxSubArray(int* nums, int numsSize){ int maxSubArray(int* nums, int numsSize){
int maxVal = INT_MIN; int maxVal = INT_MIN;
@ -286,6 +287,7 @@ int maxSubArray(int* nums, int numsSize){
``` ```
动态规划: 动态规划:
```c ```c
/** /**
* 解题思路:动态规划: * 解题思路:动态规划:
@ -332,7 +334,7 @@ function maxSubArray(nums: number[]): number {
if (curSum < 0) curSum = 0; if (curSum < 0) curSum = 0;
} }
return resMax; return resMax;
}; }
``` ```
**动态规划** **动态规划**
@ -350,7 +352,7 @@ function maxSubArray(nums: number[]): number {
resMax = Math.max(resMax, dp[i]); resMax = Math.max(resMax, dp[i]);
} }
return resMax; return resMax;
}; }
``` ```
### Scala ### Scala

View File

@ -4,7 +4,6 @@
</a> </a>
<p align="center"><strong><a href="https://mp.weixin.qq.com/s/tqCxrMEU-ajQumL1i8im9A">参与本项目</a>,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!</strong></p> <p align="center"><strong><a href="https://mp.weixin.qq.com/s/tqCxrMEU-ajQumL1i8im9A">参与本项目</a>,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!</strong></p>
# 55. 跳跃游戏 # 55. 跳跃游戏
[力扣题目链接](https://leetcode.cn/problems/jump-game/) [力扣题目链接](https://leetcode.cn/problems/jump-game/)
@ -16,15 +15,20 @@
判断你是否能够到达最后一个位置。 判断你是否能够到达最后一个位置。
示例  1: 示例  1:
* 输入: [2,3,1,1,4]
*: true -: [2,3,1,1,4]
* 解释: 我们可以先跳 1 步,从位置 0 到达 位置 1, 然后再从位置 1 跳 3 步到达最后一个位置。 - 输出: true
- 解释: 我们可以先跳 1 步,从位置 0 到达 位置 1, 然后再从位置 1 跳 3 步到达最后一个位置。
示例  2: 示例  2:
* 输入: [3,2,1,0,4]
* 输出: false
* 解释: 无论怎样,你总会到达索引为 3 的位置。但该位置的最大跳跃长度是 0 所以你永远不可能到达最后一个位置。
- 输入: [3,2,1,0,4]
- 输出: false
- 解释: 无论怎样,你总会到达索引为 3 的位置。但该位置的最大跳跃长度是 0 所以你永远不可能到达最后一个位置。
# 视频讲解
**《代码随想录》算法视频公开课:[贪心算法,怎么跳跃不重要,关键在覆盖范围 | LeetCode55.跳跃游戏](https://www.bilibili.com/video/BV1VG4y1X7kB),相信结合视频在看本篇题解,更有助于大家对本题的理解**。
## 思路 ## 思路
@ -71,6 +75,11 @@ public:
} }
}; };
``` ```
* 时间复杂度: O(n)
* 空间复杂度: O(1)
## 总结 ## 总结
这道题目关键点在于:不用拘泥于每次究竟跳几步,而是看覆盖范围,覆盖范围内一定是可以跳过来的,不用管是怎么跳的。 这道题目关键点在于:不用拘泥于每次究竟跳几步,而是看覆盖范围,覆盖范围内一定是可以跳过来的,不用管是怎么跳的。
@ -83,8 +92,8 @@ public:
## 其他语言版本 ## 其他语言版本
### Java ### Java
```Java ```Java
class Solution { class Solution {
public boolean canJump(int[] nums) { public boolean canJump(int[] nums) {
@ -106,6 +115,7 @@ class Solution {
``` ```
### Python ### Python
```python ```python
class Solution: class Solution:
def canJump(self, nums: List[int]) -> bool: def canJump(self, nums: List[int]) -> bool:
@ -156,8 +166,6 @@ func max(a, b int ) int {
} }
``` ```
### Javascript ### Javascript
```Javascript ```Javascript
@ -178,16 +186,16 @@ var canJump = function(nums) {
```Rust ```Rust
impl Solution { impl Solution {
fn max(a: usize, b: usize) -> usize {
if a > b { a } else { b }
}
pub fn can_jump(nums: Vec<i32>) -> bool { pub fn can_jump(nums: Vec<i32>) -> bool {
let mut cover = 0; if nums.len() == 1 {
if (nums.len() == 1) { return true; } return true;
let mut i = 0; }
let (mut i, mut cover) = (0, 0);
while i <= cover { while i <= cover {
cover = Self::max(i + nums[i] as usize, cover); cover = (i + nums[i] as usize).max(cover);
if cover >= nums.len() - 1 { return true; } if cover >= nums.len() - 1 {
return true;
}
i += 1; i += 1;
} }
false false
@ -196,6 +204,7 @@ impl Solution {
``` ```
### C ### C
```c ```c
#define max(a, b) (((a) > (b)) ? (a) : (b)) #define max(a, b) (((a) > (b)) ? (a) : (b))
@ -217,7 +226,6 @@ bool canJump(int* nums, int numsSize){
} }
``` ```
### TypeScript ### TypeScript
```typescript ```typescript
@ -230,10 +238,11 @@ function canJump(nums: number[]): boolean {
cur++; cur++;
} }
return false; return false;
}; }
``` ```
### Scala ### Scala
```scala ```scala
object Solution { object Solution {
def canJump(nums: Array[Int]): Boolean = { def canJump(nums: Array[Int]): Boolean = {
@ -250,7 +259,6 @@ object Solution {
} }
``` ```
<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">
<img src="../pics/网站星球宣传海报.jpg" width="1000"/> <img src="../pics/网站星球宣传海报.jpg" width="1000"/>

View File

@ -22,6 +22,9 @@
* 解释: 区间 [1,4] 和 [4,5] 可被视为重叠区间。 * 解释: 区间 [1,4] 和 [4,5] 可被视为重叠区间。
* 注意输入类型已于2019年4月15日更改。 请重置默认代码定义以获取新方法签名。 * 注意输入类型已于2019年4月15日更改。 请重置默认代码定义以获取新方法签名。
# 视频讲解
**《代码随想录》算法视频公开课:[贪心算法合并区间有细节LeetCode56.合并区间](https://www.bilibili.com/video/BV1wx4y157nD),相信结合视频在看本篇题解,更有助于大家对本题的理解**。
## 思路 ## 思路
@ -70,6 +73,10 @@ public:
}; };
``` ```
* 时间复杂度: O(nlogn)
* 空间复杂度: O(logn),排序需要的空间开销
## 其他语言版本 ## 其他语言版本
@ -173,6 +180,34 @@ func max(a, b int) int {
return b return b
} }
``` ```
```go
// 版本2
func merge(intervals [][]int) [][]int {
if len(intervals) == 1 {
return intervals
}
sort.Slice(intervals, func(i, j int) bool {
return intervals[i][0] < intervals[j][0]
})
res := make([][]int, 0)
res = append(res, intervals[0])
for i := 1; i < len(intervals); i++ {
if intervals[i][0] <= res[len(res)-1][1]{
res[len(res)-1][1] = max56(res[len(res)-1][1],intervals[i][1])
} else {
res = append(res, intervals[i])
}
}
return res
}
func max56(a, b int) int {
if a > b {
return a
}
return b
}
```
### Javascript ### Javascript
```javascript ```javascript
@ -276,24 +311,22 @@ object Solution {
```Rust ```Rust
impl Solution { impl Solution {
fn max(a: i32, b: i32) -> i32 { pub fn merge(mut intervals: Vec<Vec<i32>>) -> Vec<Vec<i32>> {
if a > b { a } else { b } let mut res = vec![];
if intervals.is_empty() {
return res;
} }
intervals.sort_by_key(|a| a[0]);
pub fn merge(intervals: Vec<Vec<i32>>) -> Vec<Vec<i32>> { res.push(intervals[0].clone());
let mut intervals = intervals; for interval in intervals.into_iter().skip(1) {
let mut result = Vec::new(); let res_last_ele = res.last_mut().unwrap();
if intervals.len() == 0 { return result; } if res_last_ele[1] >= interval[0] {
intervals.sort_by(|a, b| a[0].cmp(&b[0])); res_last_ele[1] = interval[1].max(res_last_ele[1]);
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]);
} else { } else {
result.push(intervals[i].clone()); res.push(interval);
} }
} }
result res
} }
} }
``` ```

View File

@ -72,7 +72,7 @@ dp[i] 爬到第i层楼梯有dp[i]种方法
3. dp数组如何初始化 3. dp数组如何初始化
回顾一下dp[i]的定义爬到第i层楼梯有dp[i]方法。 回顾一下dp[i]的定义爬到第i层楼梯有dp[i]方法。
那么i为0dp[i]应该是多少呢,这个可以有很多解释,但基本都是直接奔着答案去解释的。 那么i为0dp[i]应该是多少呢,这个可以有很多解释,但基本都是直接奔着答案去解释的。

View File

@ -101,6 +101,11 @@ public:
}; };
``` ```
* 时间复杂度: O(nm)
* 空间复杂度: O(n)
代码中m表示最多可以爬m个台阶代码中把m改成2就是本题70.爬楼梯可以AC的代码了。 代码中m表示最多可以爬m个台阶代码中把m改成2就是本题70.爬楼梯可以AC的代码了。
## 总结 ## 总结
@ -128,12 +133,12 @@ Java
class Solution { class Solution {
public int climbStairs(int n) { public int climbStairs(int n) {
int[] dp = new int[n + 1]; int[] dp = new int[n + 1];
int[] weight = {1,2}; int m = 2;
dp[0] = 1; dp[0] = 1;
for (int i = 0; i <= n; i++) { for (int i = 1; i <= n; i++) { // 遍历背包
for (int j = 0; j < weight.length; j++) { for (int j = 1; j <= m; j++) { //遍历物品
if (i >= weight[j]) dp[i] += dp[i - weight[j]]; if (i >= j) dp[i] += dp[i - j];
} }
} }
@ -227,3 +232,4 @@ function climbStairs(n: number): number {
<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

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

View File

@ -50,7 +50,7 @@
代码如下: 代码如下:
```CPP ```CPP
int getdepth(treenode* node) int getdepth(TreeNode* node)
``` ```
2. 确定终止条件如果为空节点的话就返回0表示高度为0。 2. 确定终止条件如果为空节点的话就返回0表示高度为0。
@ -76,14 +76,14 @@ return depth;
```CPP ```CPP
class solution { class solution {
public: public:
int getdepth(treenode* node) { int getdepth(TreeNode* node) {
if (node == NULL) return 0; if (node == NULL) return 0;
int leftdepth = getdepth(node->left); // 左 int leftdepth = getdepth(node->left); // 左
int rightdepth = getdepth(node->right); // 右 int rightdepth = getdepth(node->right); // 右
int depth = 1 + max(leftdepth, rightdepth); // 中 int depth = 1 + max(leftdepth, rightdepth); // 中
return depth; return depth;
} }
int maxdepth(treenode* root) { int maxDepth(TreeNode* root) {
return getdepth(root); return getdepth(root);
} }
}; };
@ -93,9 +93,9 @@ public:
```CPP ```CPP
class solution { class solution {
public: public:
int maxdepth(treenode* root) { int maxDepth(TreeNode* root) {
if (root == null) return 0; 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 { class solution {
public: public:
int result; int result;
void getdepth(treenode* node, int depth) { void getdepth(TreeNode* node, int depth) {
result = depth > result ? depth : result; // 中 result = depth > result ? depth : result; // 中
if (node->left == NULL && node->right == NULL) return ; if (node->left == NULL && node->right == NULL) return ;
@ -127,7 +127,7 @@ public:
} }
return ; return ;
} }
int maxdepth(treenode* root) { int maxDepth(TreeNode* root) {
result = 0; result = 0;
if (root == NULL) return result; if (root == NULL) return result;
getdepth(root, 1); getdepth(root, 1);
@ -144,7 +144,7 @@ public:
class solution { class solution {
public: public:
int result; int result;
void getdepth(treenode* node, int depth) { void getdepth(TreeNode* node, int depth) {
result = depth > result ? depth : result; // 中 result = depth > result ? depth : result; // 中
if (node->left == NULL && node->right == NULL) return ; if (node->left == NULL && node->right == NULL) return ;
if (node->left) { // 左 if (node->left) { // 左
@ -155,7 +155,7 @@ public:
} }
return ; return ;
} }
int maxdepth(treenode* root) { int maxDepth(TreeNode* root) {
result = 0; result = 0;
if (root == 0) return result; if (root == 0) return result;
getdepth(root, 1); getdepth(root, 1);
@ -182,16 +182,16 @@ c++代码如下:
```CPP ```CPP
class solution { class solution {
public: public:
int maxdepth(treenode* root) { int maxDepth(TreeNode* root) {
if (root == NULL) return 0; if (root == NULL) return 0;
int depth = 0; int depth = 0;
queue<treenode*> que; queue<TreeNode*> que;
que.push(root); que.push(root);
while(!que.empty()) { while(!que.empty()) {
int size = que.size(); int size = que.size();
depth++; // 记录深度 depth++; // 记录深度
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {
treenode* node = que.front(); TreeNode* node = que.front();
que.pop(); que.pop();
if (node->left) que.push(node->left); if (node->left) que.push(node->left);
if (node->right) que.push(node->right); if (node->right) que.push(node->right);
@ -230,11 +230,11 @@ c++代码:
```CPP ```CPP
class solution { class solution {
public: public:
int maxdepth(node* root) { int maxDepth(Node* root) {
if (root == 0) return 0; if (root == 0) return 0;
int depth = 0; int depth = 0;
for (int i = 0; i < root->children.size(); i++) { 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; return depth + 1;
} }
@ -247,15 +247,15 @@ public:
```CPP ```CPP
class solution { class solution {
public: public:
int maxdepth(node* root) { int maxDepth(Node* root) {
queue<node*> que; queue<Node*> que;
if (root != NULL) que.push(root); if (root != NULL) que.push(root);
int depth = 0; int depth = 0;
while (!que.empty()) { while (!que.empty()) {
int size = que.size(); int size = que.size();
depth++; // 记录深度 depth++; // 记录深度
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {
node* node = que.front(); Node* node = que.front();
que.pop(); que.pop();
for (int j = 0; j < node->children.size(); j++) { for (int j = 0; j < node->children.size(); j++) {
if (node->children[j]) que.push(node->children[j]); if (node->children[j]) que.push(node->children[j]);

View File

@ -250,7 +250,7 @@ private:
vector<vector<int>> result; vector<vector<int>> result;
vector<int> path; vector<int> path;
// 递归函数不需要返回值,因为我们要遍历整个树 // 递归函数不需要返回值,因为我们要遍历整个树
void traversal(treenode* cur, int count) { void traversal(TreeNode* cur, int count) {
if (!cur->left && !cur->right && count == 0) { // 遇到了叶子节点且找到了和为sum的路径 if (!cur->left && !cur->right && count == 0) { // 遇到了叶子节点且找到了和为sum的路径
result.push_back(path); result.push_back(path);
return; return;
@ -276,10 +276,10 @@ private:
} }
public: public:
vector<vector<int>> pathsum(treenode* root, int sum) { vector<vector<int>> pathSum(TreeNode* root, int sum) {
result.clear(); result.clear();
path.clear(); path.clear();
if (root == null) return result; if (root == NULL) return result;
path.push_back(root->val); // 把根节点放进路径 path.push_back(root->val); // 把根节点放进路径
traversal(root, sum - root->val); traversal(root, sum - root->val);
return result; return result;
@ -475,6 +475,12 @@ class solution:
return false # 别忘记处理空treenode return false # 别忘记处理空treenode
else: else:
return isornot(root, targetsum - root.val) return isornot(root, targetsum - root.val)
class Solution: # 简洁版
def hasPathSum(self, root: Optional[TreeNode], targetSum: int) -> bool:
if not root: return False
if root.left==root.right==None and root.val == targetSum: return True
return self.hasPathSum(root.left,targetSum-root.val) or self.hasPathSum(root.right,targetSum-root.val)
``` ```
**迭代 - 层序遍历** **迭代 - 层序遍历**
@ -560,6 +566,26 @@ class Solution:
return result return result
``` ```
**迭代法,前序遍历**
```python
class Solution:
def pathSum(self, root: Optional[TreeNode], targetSum: int) -> List[List[int]]:
if not root: return []
stack, path_stack,result = [[root,root.val]],[[root.val]],[]
while stack:
cur,cursum = stack.pop()
path = path_stack.pop()
if cur.left==cur.right==None:
if cursum==targetSum: result.append(path)
if cur.right:
stack.append([cur.right,cursum+cur.right.val])
path_stack.append(path+[cur.right.val])
if cur.left:
stack.append([cur.left,cursum+cur.left.val])
path_stack.append(path+[cur.left.val])
return result
```
## go ## go
### 112. 路径总和 ### 112. 路径总和

View File

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

View File

@ -4,7 +4,6 @@
</a> </a>
<p align="center"><strong><a href="https://mp.weixin.qq.com/s/tqCxrMEU-ajQumL1i8im9A">参与本项目</a>,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!</strong></p> <p align="center"><strong><a href="https://mp.weixin.qq.com/s/tqCxrMEU-ajQumL1i8im9A">参与本项目</a>,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!</strong></p>
# 122.买卖股票的最佳时机 II # 122.买卖股票的最佳时机 II
[力扣题目链接](https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-ii/) [力扣题目链接](https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-ii/)
@ -15,32 +14,39 @@
注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。 注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
示例 1: 示例 1:
* 输入: [7,1,5,3,6,4]
*: 7 -: [7,1,5,3,6,4]
* 解释: 在第 2 天(股票价格 = 1的时候买入在第 3 天(股票价格 = 5的时候卖出, 这笔交易所能获得利润 = 5-1 = 4。随后在第 4 天(股票价格 = 3的时候买入在第 5 天(股票价格 = 6的时候卖出, 这笔交易所能获得利润 = 6-3 = 3 。 - 输出: 7
- 解释: 在第 2 天(股票价格 = 1的时候买入在第 3 天(股票价格 = 5的时候卖出, 这笔交易所能获得利润 = 5-1 = 4。随后在第 4 天(股票价格 = 3的时候买入在第 5 天(股票价格 = 6的时候卖出, 这笔交易所能获得利润 = 6-3 = 3 。
示例 2: 示例 2:
* 输入: [1,2,3,4,5]
*: 4 -: [1,2,3,4,5]
* 解释: 在第 1 天(股票价格 = 1的时候买入在第 5 天 (股票价格 = 5的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。注意你不能在第 1 天和第 2 天接连购买股票,之后再将它们卖出。因为这样属于同时参与了多笔交易,你必须在再次购买前出售掉之前的股票。 - 输出: 4
- 解释: 在第 1 天(股票价格 = 1的时候买入在第 5 天 (股票价格 = 5的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。注意你不能在第 1 天和第 2 天接连购买股票,之后再将它们卖出。因为这样属于同时参与了多笔交易,你必须在再次购买前出售掉之前的股票。
示例  3: 示例  3:
* 输入: [7,6,4,3,1]
*: 0 -: [7,6,4,3,1]
* 解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。 - 输出: 0
- 解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。
提示: 提示:
* 1 <= prices.length <= 3 * 10 ^ 4
* 0 <= prices[i] <= 10 ^ 4 - 1 <= prices.length <= 3 \* 10 ^ 4
- 0 <= prices[i] <= 10 ^ 4
# 视频讲解
**《代码随想录》算法视频公开课:[贪心算法也能解决股票问题LeetCode122.买卖股票最佳时机 II](https://www.bilibili.com/video/BV1ev4y1C7na),相信结合视频在看本篇题解,更有助于大家对本题的理解**。
## 思路 ## 思路
本题首先要清楚两点: 本题首先要清楚两点:
* 只有一只股票! - 只有一只股票!
* 当前只有买股票或者卖股票的操作 - 当前只有买股票或者卖股票的操作
想获得利润至少要两天为一个交易单元。 想获得利润至少要两天为一个交易单元。
@ -62,7 +68,6 @@
如图: 如图:
![122.买卖股票的最佳时机II](https://code-thinking-1253855093.file.myqcloud.com/pics/2020112917480858-20230310134659477.png) ![122.买卖股票的最佳时机II](https://code-thinking-1253855093.file.myqcloud.com/pics/2020112917480858-20230310134659477.png)
一些同学陷入:第一天怎么就没有利润呢,第一天到底算不算的困惑中。 一些同学陷入:第一天怎么就没有利润呢,第一天到底算不算的困惑中。
@ -92,8 +97,8 @@ public:
}; };
``` ```
* 时间复杂度O(n) - 时间复杂度O(n)
* 空间复杂度O(1) - 空间复杂度O(1)
### 动态规划 ### 动态规划
@ -119,8 +124,8 @@ public:
}; };
``` ```
* 时间复杂度:$O(n)$ - 时间复杂度:$O(n)$
* 空间复杂度:$O(n)$ - 空间复杂度:$O(n)$
## 总结 ## 总结
@ -137,6 +142,7 @@ public:
### Java: ### Java:
贪心: 贪心:
```java ```java
// 贪心思路 // 贪心思路
class Solution { class Solution {
@ -151,6 +157,7 @@ class Solution {
``` ```
动态规划: 动态规划:
```java ```java
class Solution { // 动态规划 class Solution { // 动态规划
public int maxProfit(int[] prices) { public int maxProfit(int[] prices) {
@ -173,7 +180,9 @@ class Solution { // 动态规划
``` ```
### Python: ### Python:
贪心: 贪心:
```python ```python
class Solution: class Solution:
def maxProfit(self, prices: List[int]) -> int: def maxProfit(self, prices: List[int]) -> int:
@ -184,6 +193,7 @@ class Solution:
``` ```
动态规划: 动态规划:
```python ```python
class Solution: class Solution:
def maxProfit(self, prices: List[int]) -> int: def maxProfit(self, prices: List[int]) -> int:
@ -200,6 +210,7 @@ class Solution:
### Go: ### Go:
贪心算法 贪心算法
```go ```go
func maxProfit(prices []int) int { func maxProfit(prices []int) int {
var sum int var sum int
@ -212,7 +223,9 @@ func maxProfit(prices []int) int {
return sum return sum
} }
``` ```
动态规划 动态规划
```go ```go
func maxProfit(prices []int) int { func maxProfit(prices []int) int {
dp := make([][]int, len(prices)) dp := make([][]int, len(prices))
@ -239,6 +252,7 @@ func max(a, b int) int {
### Javascript: ### Javascript:
贪心 贪心
```Javascript ```Javascript
var maxProfit = function(prices) { var maxProfit = function(prices) {
let result = 0 let result = 0
@ -250,6 +264,7 @@ var maxProfit = function(prices) {
``` ```
动态规划 动态规划
```javascript ```javascript
const maxProfit = (prices) => { const maxProfit = (prices) => {
let dp = Array.from(Array(prices.length), () => Array(2).fill(0)); let dp = Array.from(Array(prices.length), () => Array(2).fill(0));
@ -275,6 +290,7 @@ const maxProfit = (prices) => {
### TypeScript ### TypeScript
贪心
```typescript ```typescript
function maxProfit(prices: number[]): number { function maxProfit(prices: number[]): number {
let resProfit: number = 0; let resProfit: number = 0;
@ -282,12 +298,28 @@ function maxProfit(prices: number[]): number {
resProfit += Math.max(prices[i] - prices[i - 1], 0); resProfit += Math.max(prices[i] - prices[i - 1], 0);
} }
return resProfit; return resProfit;
}; }
```
动态规划
```typescript
function maxProfit(prices: number[]): number {
const dp = Array(prices.length)
.fill(0)
.map(() => Array(2).fill(0))
dp[0][0] = -prices[0]
for (let i = 1; i < prices.length; i++) {
dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][1] - prices[i])
dp[i][1] = Math.max(dp[i - 1][1], dp[i - 1][0] + prices[i])
}
return dp[prices.length - 1][1]
}
``` ```
### Rust ### Rust
贪心: 贪心:
```Rust ```Rust
impl Solution { impl Solution {
fn max(a: i32, b: i32) -> i32 { fn max(a: i32, b: i32) -> i32 {
@ -304,6 +336,7 @@ impl Solution {
``` ```
动态规划: 动态规划:
```Rust ```Rust
impl Solution { impl Solution {
fn max(a: i32, b: i32) -> i32 { fn max(a: i32, b: i32) -> i32 {
@ -323,7 +356,9 @@ impl Solution {
``` ```
### C: ### C:
贪心: 贪心:
```c ```c
int maxProfit(int* prices, int pricesSize){ int maxProfit(int* prices, int pricesSize){
int result = 0; int result = 0;
@ -339,6 +374,7 @@ int maxProfit(int* prices, int pricesSize){
``` ```
动态规划: 动态规划:
```c ```c
#define max(a, b) (((a) > (b)) ? (a) : (b)) #define max(a, b) (((a) > (b)) ? (a) : (b))
@ -363,6 +399,7 @@ int maxProfit(int* prices, int pricesSize){
### Scala ### Scala
贪心: 贪心:
```scala ```scala
object Solution { object Solution {
def maxProfit(prices: Array[Int]): Int = { def maxProfit(prices: Array[Int]): Int = {

View File

@ -84,7 +84,221 @@ public:
## 其他语言版本 ## 其他语言版本
### Java
```Java
// 广度优先遍历
// 使用 visited 数组进行标记
class Solution {
private static final int[][] position = {{-1, 0}, {0, 1}, {1, 0}, {0, -1}}; // 四个方向
public void solve(char[][] board) {
// rowSize行的长度colSize列的长度
int rowSize = board.length, colSize = board[0].length;
boolean[][] visited = new boolean[rowSize][colSize];
Queue<int[]> queue = new ArrayDeque<>();
// 从左侧边,和右侧边遍历
for (int row = 0; row < rowSize; row++) {
if (board[row][0] == 'O') {
visited[row][0] = true;
queue.add(new int[]{row, 0});
}
if (board[row][colSize - 1] == 'O') {
visited[row][colSize - 1] = true;
queue.add(new int[]{row, colSize - 1});
}
}
// 从上边和下边遍历,在对左侧边和右侧边遍历时我们已经遍历了矩阵的四个角
// 所以在遍历上边和下边时可以不用遍历四个角
for (int col = 1; col < colSize - 1; col++) {
if (board[0][col] == 'O') {
visited[0][col] = true;
queue.add(new int[]{0, col});
}
if (board[rowSize - 1][col] == 'O') {
visited[rowSize - 1][col] = true;
queue.add(new int[]{rowSize - 1, col});
}
}
// 广度优先遍历,把没有被 'X' 包围的 'O' 进行标记
while (!queue.isEmpty()) {
int[] current = queue.poll();
for (int[] pos: position) {
int row = current[0] + pos[0], col = current[1] + pos[1];
// 如果范围越界、位置已被访问过、该位置的值不是 'O',就直接跳过
if (row < 0 || row >= rowSize || col < 0 || col >= colSize) continue;
if (visited[row][col] || board[row][col] != 'O') continue;
visited[row][col] = true;
queue.add(new int[]{row, col});
}
}
// 遍历数组,把没有被标记的 'O' 修改成 'X'
for (int row = 0; row < rowSize; row++) {
for (int col = 0; col < colSize; col++) {
if (board[row][col] == 'O' && !visited[row][col]) board[row][col] = 'X';
}
}
}
}
```
```Java
// 广度优先遍历
// 直接修改 board 的值为其他特殊值
class Solution {
private static final int[][] position = {{-1, 0}, {0, 1}, {1, 0}, {0, -1}}; // 四个方向
public void solve(char[][] board) {
// rowSize行的长度colSize列的长度
int rowSize = board.length, colSize = board[0].length;
Queue<int[]> queue = new ArrayDeque<>();
// 从左侧边,和右侧边遍历
for (int row = 0; row < rowSize; row++) {
if (board[row][0] == 'O')
queue.add(new int[]{row, 0});
if (board[row][colSize - 1] == 'O')
queue.add(new int[]{row, colSize - 1});
}
// 从上边和下边遍历,在对左侧边和右侧边遍历时我们已经遍历了矩阵的四个角
// 所以在遍历上边和下边时可以不用遍历四个角
for (int col = 1; col < colSize - 1; col++) {
if (board[0][col] == 'O')
queue.add(new int[]{0, col});
if (board[rowSize - 1][col] == 'O')
queue.add(new int[]{rowSize - 1, col});
}
// 广度优先遍历,把没有被 'X' 包围的 'O' 修改成特殊值
while (!queue.isEmpty()) {
int[] current = queue.poll();
board[current[0]][current[1]] = 'A';
for (int[] pos: position) {
int row = current[0] + pos[0], col = current[1] + pos[1];
// 如果范围越界、该位置的值不是 'O',就直接跳过
if (row < 0 || row >= rowSize || col < 0 || col >= colSize) continue;
if (board[row][col] != 'O') continue;
queue.add(new int[]{row, col});
}
}
// 遍历数组,把 'O' 修改成 'X',特殊值修改成 'O'
for (int row = 0; row < rowSize; row++) {
for (int col = 0; col < colSize; col++) {
if (board[row][col] == 'A') board[row][col] = 'O';
else if (board[row][col] == 'O') board[row][col] = 'X';
}
}
}
}
```
```Java
// 深度优先遍历
// 使用 visited 数组进行标记
class Solution {
private static final int[][] position = {{-1, 0}, {0, 1}, {1, 0}, {0, -1}}; // 四个方向
public void dfs(char[][] board, int row, int col, boolean[][] visited) {
for (int[] pos: position) {
int nextRow = row + pos[0], nextCol = col + pos[1];
// 位置越界
if (nextRow < 0 || nextRow >= board.length || nextCol < 0 || nextCol >= board[0].length)
continue;
// 位置已被访问过、新位置值不是 'O'
if (visited[nextRow][nextCol] || board[nextRow][nextCol] != 'O') continue;
visited[nextRow][nextCol] = true;
dfs(board, nextRow, nextCol, visited);
}
}
public void solve(char[][] board) {
int rowSize = board.length, colSize = board[0].length;
boolean[][] visited = new boolean[rowSize][colSize];
// 从左侧遍、右侧遍遍历
for (int row = 0; row < rowSize; row++) {
if (board[row][0] == 'O' && !visited[row][0]) {
visited[row][0] = true;
dfs(board, row, 0, visited);
}
if (board[row][colSize - 1] == 'O' && !visited[row][colSize - 1]) {
visited[row][colSize - 1] = true;
dfs(board, row, colSize - 1, visited);
}
}
// 从上边和下边遍历,在对左侧边和右侧边遍历时我们已经遍历了矩阵的四个角
// 所以在遍历上边和下边时可以不用遍历四个角
for (int col = 1; col < colSize - 1; col++) {
if (board[0][col] == 'O' && !visited[0][col]) {
visited[0][col] = true;
dfs(board, 0, col, visited);
}
if (board[rowSize - 1][col] == 'O' && !visited[rowSize - 1][col]) {
visited[rowSize - 1][col] = true;
dfs(board, rowSize - 1, col, visited);
}
}
// 遍历数组,把没有被标记的 'O' 修改成 'X'
for (int row = 0; row < rowSize; row++) {
for (int col = 0; col < colSize; col++) {
if (board[row][col] == 'O' && !visited[row][col]) board[row][col] = 'X';
}
}
}
}
```
```Java
// 深度优先遍历
// // 直接修改 board 的值为其他特殊值
class Solution {
private static final int[][] position = {{-1, 0}, {0, 1}, {1, 0}, {0, -1}}; // 四个方向
public void dfs(char[][] board, int row, int col) {
for (int[] pos: position) {
int nextRow = row + pos[0], nextCol = col + pos[1];
// 位置越界
if (nextRow < 0 || nextRow >= board.length || nextCol < 0 || nextCol >= board[0].length)
continue;
// 新位置值不是 'O'
if (board[nextRow][nextCol] != 'O') continue;
board[nextRow][nextCol] = 'A'; // 修改为特殊值
dfs(board, nextRow, nextCol);
}
}
public void solve(char[][] board) {
int rowSize = board.length, colSize = board[0].length;
// 从左侧遍、右侧遍遍历
for (int row = 0; row < rowSize; row++) {
if (board[row][0] == 'O') {
board[row][0] = 'A';
dfs(board, row, 0);
}
if (board[row][colSize - 1] == 'O') {
board[row][colSize - 1] = 'A';
dfs(board, row, colSize - 1);
}
}
// 从上边和下边遍历,在对左侧边和右侧边遍历时我们已经遍历了矩阵的四个角
// 所以在遍历上边和下边时可以不用遍历四个角
for (int col = 1; col < colSize - 1; col++) {
if (board[0][col] == 'O') {
board[0][col] = 'A';
dfs(board, 0, col);
}
if (board[rowSize - 1][col] == 'O') {
board[rowSize - 1][col] = 'A';
dfs(board, rowSize - 1, col);
}
}
// 遍历数组,把 'O' 修改成 'X',特殊值修改成 'O'
for (int row = 0; row < rowSize; row++) {
for (int col = 0; col < colSize; col++) {
if (board[row][col] == 'O') board[row][col] = 'X';
else if (board[row][col] == 'A') board[row][col] = 'O';
}
}
}
}
```
<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">
<img src="../pics/网站星球宣传海报.jpg" width="1000"/> <img src="../pics/网站星球宣传海报.jpg" width="1000"/>
</a> </a>

View File

@ -289,7 +289,45 @@ class Solution:
## Go ## Go
```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 ## JavaScript

View File

@ -45,6 +45,10 @@
* 解释: * 解释:
你不能从 0 号或 1 号加油站出发,因为没有足够的汽油可以让你行驶到下一个加油站。我们从 2 号加油站出发,可以获得 4 升汽油。 此时油箱有 = 0 + 4 = 4 升汽油。开往 0 号加油站,此时油箱有 4 - 3 + 2 = 3 升汽油。开往 1 号加油站,此时油箱有 3 - 3 + 3 = 3 升汽油。你无法返回 2 号加油站,因为返程需要消耗 4 升汽油,但是你的油箱只有 3 升汽油。因此,无论怎样,你都不可能绕环路行驶一周。 你不能从 0 号或 1 号加油站出发,因为没有足够的汽油可以让你行驶到下一个加油站。我们从 2 号加油站出发,可以获得 4 升汽油。 此时油箱有 = 0 + 4 = 4 升汽油。开往 0 号加油站,此时油箱有 4 - 3 + 2 = 3 升汽油。开往 1 号加油站,此时油箱有 3 - 3 + 3 = 3 升汽油。你无法返回 2 号加油站,因为返程需要消耗 4 升汽油,但是你的油箱只有 3 升汽油。因此,无论怎样,你都不可能绕环路行驶一周。
# 视频讲解
**《代码随想录》算法视频公开课:[贪心算法得这么加油才能跑完全程LeetCode 134.加油站](https://www.bilibili.com/video/BV1jA411r7WX),相信结合视频在看本篇题解,更有助于大家对本题的理解**。
## 暴力方法 ## 暴力方法

View File

@ -28,6 +28,10 @@
* 输出: 4 * 输出: 4
* 解释: 你可以分别给这三个孩子分发 1、2、1 颗糖果。第三个孩子只得到 1 颗糖果,这已满足上述两个条件。 * 解释: 你可以分别给这三个孩子分发 1、2、1 颗糖果。第三个孩子只得到 1 颗糖果,这已满足上述两个条件。
# 视频讲解
**《代码随想录》算法视频公开课:[贪心算法两者兼顾很容易顾此失彼LeetCode135.分发糖果](https://www.bilibili.com/video/BV1ev4y1r7wN),相信结合视频在看本篇题解,更有助于大家对本题的理解**。
## 思路 ## 思路
@ -117,6 +121,11 @@ public:
}; };
``` ```
* 时间复杂度: O(n)
* 空间复杂度: O(n)
## 总结 ## 总结
这在leetcode上是一道困难的题目其难点就在于贪心的策略如果在考虑局部的时候想两边兼顾就会顾此失彼。 这在leetcode上是一道困难的题目其难点就在于贪心的策略如果在考虑局部的时候想两边兼顾就会顾此失彼。

View File

@ -145,6 +145,9 @@ public:
}; };
``` ```
* 时间复杂度: O(n)快慢指针相遇前指针走的次数小于链表长度快慢指针相遇后两个index指针走的次数也小于链表长度总体为走的次数小于 2n
* 空间复杂度: O(1)
## 补充 ## 补充
在推理过程中,大家可能有一个疑问就是:**为什么第一次在环中相遇slow的 步数 是 x+y 而不是 x + 若干环的长度 + y 呢?** 在推理过程中,大家可能有一个疑问就是:**为什么第一次在环中相遇slow的 步数 是 x+y 而不是 x + 若干环的长度 + y 呢?**

View File

@ -113,6 +113,9 @@ public:
}; };
``` ```
* 时间复杂度: O(n)
* 空间复杂度: O(n)
## 题外话 ## 题外话

View File

@ -114,6 +114,7 @@ void removeExtraSpaces(string& s) {
} }
``` ```
有的同学可能发现用erase来移除空格在leetcode上性能也还行。主要是以下几点 有的同学可能发现用erase来移除空格在leetcode上性能也还行。主要是以下几点
1. leetcode上的测试集里字符串的长度不够长如果足够长性能差距会非常明显。 1. leetcode上的测试集里字符串的长度不够长如果足够长性能差距会非常明显。
@ -197,6 +198,9 @@ public:
}; };
``` ```
* 时间复杂度: O(n)
* 空间复杂度: O(1) 或 O(n),取决于语言中字符串是否可变
## 其他语言版本 ## 其他语言版本
@ -516,6 +520,48 @@ class Solution:
return s[:ps] + s[ps:][::-1] # Must do the last step, because the last word is omit though the pointers are on the correct positions, return s[:ps] + s[ps:][::-1] # Must do the last step, because the last word is omit though the pointers are on the correct positions,
``` ```
```python
class Solution: # 使用双指针法移除空格
def reverseWords(self, s: str) -> str:
def removeextraspace(s):
start = 0; end = len(s)-1
while s[start]==' ':
start+=1
while s[end]==' ':
end-=1
news = list(s[start:end+1])
slow = fast = 0
while fast<len(news):
while fast>0 and news[fast]==news[fast-1]==' ':
fast+=1
news[slow]=news[fast]
slow+=1; fast+=1
#return "".join(news[:slow])
return news[:slow]
def reversestr(s):
left,right = 0,len(s)-1
news = list(s)
while left<right:
news[left],news[right] = news[right],news[left]
left+=1; right-=1
#return "".join(news)
return news
news = removeextraspace(s)
news.append(' ')
fast=slow=0
#print(news)
while fast<len(news):
while news[fast]!=' ':
fast+=1
news[slow:fast] = reversestr(news[slow:fast])
# print(news[slow:fast])
fast=slow=fast+1
news2 = reversestr(news[:-1])
return ''.join(news2)
```
Go Go
```go ```go
@ -924,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"> <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

@ -156,6 +156,11 @@ public:
}; };
``` ```
* 时间复杂度: O(n * k),其中 n 为 prices 的长度
* 空间复杂度: O(n * k)
当然有的解法是定义一个三维数组dp[i][j][k]第i天第j次买卖k表示买还是卖的状态从定义上来讲是比较直观。 当然有的解法是定义一个三维数组dp[i][j][k]第i天第j次买卖k表示买还是卖的状态从定义上来讲是比较直观。
但感觉三维数组操作起来有些麻烦,我是直接用二维数组来模拟三维数组的情况,代码看起来也清爽一些。 但感觉三维数组操作起来有些麻烦,我是直接用二维数组来模拟三维数组的情况,代码看起来也清爽一些。

View File

@ -108,6 +108,9 @@ public:
}; };
``` ```
* 时间复杂度: O(n)
* 空间复杂度: O(n)
## 总结 ## 总结
打家劫舍是DP解决的经典题目这道题也是打家劫舍入门级题目后面我们还会变种方式来打劫的。 打家劫舍是DP解决的经典题目这道题也是打家劫舍入门级题目后面我们还会变种方式来打劫的。

View File

@ -75,6 +75,8 @@ public:
}; };
``` ```
* 时间复杂度: O(logn)
* 空间复杂度: O(logn)
@ -132,6 +134,19 @@ class Solution:
else: else:
record.add(n) record.add(n)
# python的另一种写法 - 通过字符串来计算各位平方和
class Solution:
def isHappy(self, n: int) -> bool:
record = []
while n not in record:
record.append(n)
newn = 0
nn = str(n)
for i in nn:
newn+=int(i)**2
if newn==1: return True
n = newn
return False
``` ```
Go Go

View File

@ -118,6 +118,9 @@ public:
}; };
``` ```
* 时间复杂度: O(n)
* 空间复杂度: O(1)
**设置一个虚拟头结点在进行移除节点操作:** **设置一个虚拟头结点在进行移除节点操作:**
```CPP ```CPP
@ -144,6 +147,9 @@ public:
``` ```
* 时间复杂度: O(n)
* 空间复杂度: O(1)
@ -310,8 +316,8 @@ class Solution:
def removeElements(self, head: ListNode, val: int) -> ListNode: def removeElements(self, head: ListNode, val: int) -> ListNode:
dummy_head = ListNode(next=head) #添加一个虚拟节点 dummy_head = ListNode(next=head) #添加一个虚拟节点
cur = dummy_head cur = dummy_head
while(cur.next!=None): while cur.next:
if(cur.next.val == val): if cur.next.val == val:
cur.next = cur.next.next #删除cur.next节点 cur.next = cur.next.next #删除cur.next节点
else: else:
cur = cur.next cur = cur.next

View File

@ -68,6 +68,9 @@ public:
}; };
``` ```
* 时间复杂度: O(n)
* 空间复杂度: O(1)
## 递归法 ## 递归法
递归法相对抽象一些但是其实和双指针法是一样的逻辑同样是当cur为空的时候循环结束不断将cur指向pre的过程。 递归法相对抽象一些但是其实和双指针法是一样的逻辑同样是当cur为空的时候循环结束不断将cur指向pre的过程。
@ -97,6 +100,9 @@ public:
}; };
``` ```
* 时间复杂度: O(n), 要递归处理链表的每个节点
* 空间复杂度: O(n), 递归调用了 n 层栈空间
我们可以发现,上面的递归写法和双指针法实质上都是从前往后翻转指针指向,其实还有另外一种与双指针法不同思路的递归写法:从后往前翻转指针指向。 我们可以发现,上面的递归写法和双指针法实质上都是从前往后翻转指针指向,其实还有另外一种与双指针法不同思路的递归写法:从后往前翻转指针指向。
具体代码如下(带详细注释): 具体代码如下(带详细注释):
@ -120,6 +126,9 @@ public:
}; };
``` ```
* 时间复杂度: O(n)
* 空间复杂度: O(n)
## 其他语言版本 ## 其他语言版本

View File

@ -82,6 +82,11 @@ public:
}; };
``` ```
* 时间复杂度: O(n)
* 空间复杂度: O(n)
## 总结 ## 总结
成环之后还是难了一些的, 不少题解没有把“考虑房间”和“偷房间”说清楚。 成环之后还是难了一些的, 不少题解没有把“考虑房间”和“偷房间”说清楚。

View File

@ -379,6 +379,20 @@ class Solution:
return (2 << leftDepth) - 1 #注意(2<<1) 相当于2^2所以leftDepth初始为0 return (2 << leftDepth) - 1 #注意(2<<1) 相当于2^2所以leftDepth初始为0
return self.countNodes(root.left) + self.countNodes(root.right) + 1 return self.countNodes(root.left) + self.countNodes(root.right) + 1
``` ```
完全二叉树写法2
```python
class Solution: # 利用完全二叉树特性
def countNodes(self, root: TreeNode) -> int:
if not root: return 0
count = 1
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 ## Go

View File

@ -111,6 +111,8 @@ public:
} }
}; };
``` ```
* 时间复杂度: push为O(n)其他为O(1)
* 空间复杂度: O(n)
# 优化 # 优化
@ -156,6 +158,9 @@ public:
} }
}; };
``` ```
* 时间复杂度: push为O(n)其他为O(1)
* 空间复杂度: O(n)
# 其他语言版本 # 其他语言版本

View File

@ -112,6 +112,10 @@ public:
``` ```
* 时间复杂度: push和empty为O(1), pop和peek为O(n)
* 空间复杂度: O(n)
## 拓展 ## 拓展
可以看出peek()的实现直接复用了pop() 要不然对stOut判空的逻辑又要重写一遍。 可以看出peek()的实现直接复用了pop() 要不然对stOut判空的逻辑又要重写一遍。

View File

@ -184,6 +184,9 @@ public:
} }
}; };
``` ```
* 时间复杂度: O(n)
* 空间复杂度: O(k)
再来看一下时间复杂度,使用单调队列的时间复杂度是 O(n)。 再来看一下时间复杂度,使用单调队列的时间复杂度是 O(n)。

View File

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

View File

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

View File

@ -381,6 +381,32 @@ class Solution:
return path 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
```go ```go
type pair struct { type pair struct {

View File

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

View File

@ -79,8 +79,6 @@
```CPP ```CPP
// 时间复杂度O(nlogk)
// 空间复杂度O(n)
class Solution { class Solution {
public: public:
// 小顶堆 // 小顶堆
@ -120,6 +118,10 @@ public:
} }
}; };
``` ```
* 时间复杂度: O(nlogk)
* 空间复杂度: O(n)
# 拓展 # 拓展
大家对这个比较运算在建堆时是如何应用的,为什么左大于右就会建立小顶堆,反而建立大顶堆比较困惑。 大家对这个比较运算在建堆时是如何应用的,为什么左大于右就会建立小顶堆,反而建立大顶堆比较困惑。

View File

@ -72,6 +72,9 @@ public:
}; };
``` ```
* 时间复杂度: O(mn)
* 空间复杂度: O(n)
## 拓展 ## 拓展
那有同学可能问了遇到哈希问题我直接都用set不就得了用什么数组啊。 那有同学可能问了遇到哈希问题我直接都用set不就得了用什么数组啊。
@ -110,6 +113,8 @@ public:
}; };
``` ```
* 时间复杂度: O(m + n)
* 空间复杂度: O(n)
## 其他语言版本 ## 其他语言版本
@ -169,6 +174,21 @@ class Solution:
val_dict[num] = 0 val_dict[num] = 0
return ans return ans
class Solution: # 使用数组方法
def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]:
count1 = [0]*1001
count2 = [0]*1001
result = []
for i in range(len(nums1)):
count1[nums1[i]]+=1
for j in range(len(nums2)):
count2[nums2[j]]+=1
for k in range(1001):
if count1[k]*count2[k]>0:
result.append(k)
return result
``` ```

View File

@ -4,7 +4,6 @@
</a> </a>
<p align="center"><strong><a href="https://mp.weixin.qq.com/s/tqCxrMEU-ajQumL1i8im9A">参与本项目</a>,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!</strong></p> <p align="center"><strong><a href="https://mp.weixin.qq.com/s/tqCxrMEU-ajQumL1i8im9A">参与本项目</a>,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!</strong></p>
> 本周讲解了[贪心理论基础](https://programmercarl.com/贪心算法理论基础.html),以及第一道贪心的题目:[贪心算法:分发饼干](https://programmercarl.com/0455.分发饼干.html),可能会给大家一种贪心算法比较简单的错觉,好了,接下来几天的题目难度要上来了,哈哈。 > 本周讲解了[贪心理论基础](https://programmercarl.com/贪心算法理论基础.html),以及第一道贪心的题目:[贪心算法:分发饼干](https://programmercarl.com/0455.分发饼干.html),可能会给大家一种贪心算法比较简单的错觉,好了,接下来几天的题目难度要上来了,哈哈。
# 376. 摆动序列 # 376. 摆动序列
@ -18,18 +17,25 @@
给定一个整数序列,返回作为摆动序列的最长子序列的长度。 通过从原始序列中删除一些(也可以不删除)元素来获得子序列,剩下的元素保持其原始顺序。 给定一个整数序列,返回作为摆动序列的最长子序列的长度。 通过从原始序列中删除一些(也可以不删除)元素来获得子序列,剩下的元素保持其原始顺序。
示例 1: 示例 1:
* 输入: [1,7,4,9,2,5]
*: 6 -: [1,7,4,9,2,5]
* 解释: 整个序列均为摆动序列。 - 输出: 6
- 解释: 整个序列均为摆动序列。
示例 2: 示例 2:
* 输入: [1,17,5,10,13,15,10,5,16,8]
*: 7 -: [1,17,5,10,13,15,10,5,16,8]
* 解释: 这个序列包含几个长度为 7 摆动序列,其中一个可为[1,17,10,13,10,16,8]。 - 输出: 7
- 解释: 这个序列包含几个长度为 7 摆动序列,其中一个可为[1,17,10,13,10,16,8]。
示例 3: 示例 3:
* 输入: [1,2,3,4,5,6,7,8,9]
*: 2 -: [1,2,3,4,5,6,7,8,9]
- 输出: 2
# 视频讲解
**《代码随想录》算法视频公开课:[贪心算法,寻找摆动有细节!| LeetCode376.摆动序列](https://www.bilibili.com/video/BV17M411b7NS),相信结合视频在看本篇题解,更有助于大家对本题的理解**。
## 思路 1贪心解法 ## 思路 1贪心解法
@ -81,11 +87,9 @@
所以我们记录峰值的条件应该是: `(preDiff <= 0 && curDiff > 0) || (preDiff >= 0 && curDiff < 0)`,为什么这里允许 prediff == 0 ,就是为了 上面我说的这种情况。 所以我们记录峰值的条件应该是: `(preDiff <= 0 && curDiff > 0) || (preDiff >= 0 && curDiff < 0)`,为什么这里允许 prediff == 0 ,就是为了 上面我说的这种情况。
### 情况二:数组首尾两端 ### 情况二:数组首尾两端
所以本题统计峰值的时候,数组最左面和最右面如何统计呢?
所以本题统计峰值的时候,数组最左面和最右面如果统计呢?
题目中说了,如果只有两个不同的元素,那摆动序列也是 2。 题目中说了,如果只有两个不同的元素,那摆动序列也是 2。
@ -130,8 +134,9 @@ public:
} }
}; };
``` ```
* 时间复杂度O(n)
* 空间复杂度O(1) -间复杂度O(n)
- 空间复杂度O(1)
此时大家是不是发现 以上代码提交也不能通过本题? 此时大家是不是发现 以上代码提交也不能通过本题?
@ -188,13 +193,13 @@ public:
很容易可以发现,对于我们当前考虑的这个数,要么是作为山峰(即 nums[i] > nums[i-1]),要么是作为山谷(即 nums[i] < nums[i - 1])。 很容易可以发现,对于我们当前考虑的这个数,要么是作为山峰(即 nums[i] > nums[i-1]),要么是作为山谷(即 nums[i] < nums[i - 1])。
* 设dp状态`dp[i][0]`表示考虑前i个数第i个数作为山峰的摆动子序列的最长长度 - dp 状态`dp[i][0]`表示考虑前 i 个数 i 个数作为山峰的摆动子序列的最长长度
* 设dp状态`dp[i][1]`表示考虑前i个数第i个数作为山谷的摆动子序列的最长长度 - dp 状态`dp[i][1]`表示考虑前 i 个数 i 个数作为山谷的摆动子序列的最长长度
则转移方程为 则转移方程为
* `dp[i][0] = max(dp[i][0], dp[j][1] + 1)`,其中`0 < j < i`且`nums[j] < nums[i]`表示将nums[i]接到前面某个山谷后面,作为山峰。 - `dp[i][0] = max(dp[i][0], dp[j][1] + 1)`其中`0 < j < i``nums[j] < nums[i]`表示将 nums[i]接到前面某个山谷后面作为山峰
* `dp[i][1] = max(dp[i][1], dp[j][0] + 1)`,其中`0 < j < i`且`nums[j] > nums[i]`表示将nums[i]接到前面某个山峰后面,作为山谷。 - `dp[i][1] = max(dp[i][1], dp[j][0] + 1)`其中`0 < j < i``nums[j] > nums[i]`表示将 nums[i]接到前面某个山峰后面作为山谷
初始状态 初始状态
@ -223,28 +228,25 @@ public:
}; };
``` ```
* 时间复杂度O(n^2) - 时间复杂度O(n^2)
* 空间复杂度O(n) - 空间复杂度O(n)
**进阶** **进阶**
可以用两棵线段树来维护区间的最大值 可以用两棵线段树来维护区间的最大值
* 每次更新`dp[i][0]`,则在`tree1`的`nums[i]`位置值更新为`dp[i][0]` - 每次更新`dp[i][0]`则在`tree1``nums[i]`位置值更新为`dp[i][0]`
* 每次更新`dp[i][1]`,则在`tree2`的`nums[i]`位置值更新为`dp[i][1]` - 每次更新`dp[i][1]`则在`tree2``nums[i]`位置值更新为`dp[i][1]`
* 则dp转移方程中就没有必要j从0遍历到i-1可以直接在线段树中查询指定区间的值即可 - dp 转移方程中就没有必要 j 0 遍历到 i-1可以直接在线段树中查询指定区间的值即可
时间复杂度O(nlog n) 时间复杂度O(nlog n)
空间复杂度O(n) 空间复杂度O(n)
## 其他语言版本 ## 其他语言版本
### Java ### Java
```Java ```Java
class Solution { class Solution {
public int wiggleMaxLength(int[] nums) { public int wiggleMaxLength(int[] nums) {
@ -270,6 +272,7 @@ class Solution {
} }
} }
``` ```
```java ```java
// DP // DP
class Solution { class Solution {
@ -360,6 +363,7 @@ class Solution:
### Go ### Go
**贪心** **贪心**
```go ```go
func wiggleMaxLength(nums []int) int { func wiggleMaxLength(nums []int) int {
n := len(nums) n := len(nums)
@ -383,6 +387,7 @@ func wiggleMaxLength(nums []int) int {
``` ```
**动态规划** **动态规划**
```go ```go
func wiggleMaxLength(nums []int) int { func wiggleMaxLength(nums []int) int {
n := len(nums) n := len(nums)
@ -420,7 +425,9 @@ func max(a, b int) int {
``` ```
### Javascript ### Javascript
**贪心** **贪心**
```Javascript ```Javascript
var wiggleMaxLength = function(nums) { var wiggleMaxLength = function(nums) {
if(nums.length <= 1) return nums.length if(nums.length <= 1) return nums.length
@ -437,7 +444,9 @@ var wiggleMaxLength = function(nums) {
return result return result
}; };
``` ```
**动态规划** **动态规划**
```Javascript ```Javascript
var wiggleMaxLength = function(nums) { var wiggleMaxLength = function(nums) {
if (nums.length === 1) return 1; if (nums.length === 1) return 1;
@ -458,7 +467,9 @@ var wiggleMaxLength = function(nums) {
``` ```
### Rust ### Rust
**贪心** **贪心**
```Rust ```Rust
impl Solution { impl Solution {
pub fn wiggle_max_length(nums: Vec<i32>) -> i32 { pub fn wiggle_max_length(nums: Vec<i32>) -> i32 {
@ -504,6 +515,7 @@ impl Solution {
``` ```
### C ### C
**贪心** **贪心**
```c ```c
@ -568,8 +580,6 @@ int wiggleMaxLength(int* nums, int numsSize){
} }
``` ```
### TypeScript ### TypeScript
**贪心** **贪心**
@ -583,16 +593,13 @@ function wiggleMaxLength(nums: number[]): number {
let count: number = 1; let count: number = 1;
for (let i = 1; i < length; i++) { for (let i = 1; i < length; i++) {
curDiff = nums[i] - nums[i - 1]; curDiff = nums[i] - nums[i - 1];
if ( if ((preDiff <= 0 && curDiff > 0) || (preDiff >= 0 && curDiff < 0)) {
(preDiff <= 0 && curDiff > 0) ||
(preDiff >= 0 && curDiff < 0)
) {
preDiff = curDiff; preDiff = curDiff;
count++; count++;
} }
} }
return count; return count;
}; }
``` ```
**动态规划** **动态规划**
@ -601,7 +608,7 @@ function wiggleMaxLength(nums: number[]): number {
function wiggleMaxLength(nums: number[]): number { function wiggleMaxLength(nums: number[]): number {
const length: number = nums.length; const length: number = nums.length;
if (length <= 1) return length; if (length <= 1) return length;
const dp: number[][] = new Array(length).fill(0).map(_ => []); const dp: number[][] = new Array(length).fill(0).map((_) => []);
dp[0][0] = 1; // 第一个数作为波峰 dp[0][0] = 1; // 第一个数作为波峰
dp[0][1] = 1; // 第一个数作为波谷 dp[0][1] = 1; // 第一个数作为波谷
for (let i = 1; i < length; i++) { for (let i = 1; i < length; i++) {
@ -615,7 +622,7 @@ function wiggleMaxLength(nums: number[]): number {
} }
} }
return Math.max(dp[length - 1][0], dp[length - 1][1]); return Math.max(dp[length - 1][0], dp[length - 1][1]);
}; }
``` ```
### Scala ### Scala
@ -648,3 +655,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

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

View File

@ -39,8 +39,6 @@ canConstruct("aa", "aab") -> true
那么第一个思路其实就是暴力枚举了两层for循环不断去寻找代码如下 那么第一个思路其实就是暴力枚举了两层for循环不断去寻找代码如下
```CPP ```CPP
// 时间复杂度: O(n^2)
// 空间复杂度O(1)
class Solution { class Solution {
public: public:
bool canConstruct(string ransomNote, string magazine) { bool canConstruct(string ransomNote, string magazine) {
@ -62,6 +60,9 @@ public:
}; };
``` ```
* 时间复杂度: O(n^2)
* 空间复杂度: O(1)
这里时间复杂度是比较高的而且里面还有一个字符串删除也就是erase的操作也是费时的当然这段代码也可以过这道题。 这里时间复杂度是比较高的而且里面还有一个字符串删除也就是erase的操作也是费时的当然这段代码也可以过这道题。
@ -78,8 +79,6 @@ public:
代码如下: 代码如下:
```CPP ```CPP
// 时间复杂度: O(n)
// 空间复杂度O(1)
class Solution { class Solution {
public: public:
bool canConstruct(string ransomNote, string magazine) { bool canConstruct(string ransomNote, string magazine) {
@ -105,6 +104,10 @@ public:
}; };
``` ```
* 时间复杂度: O(n)
* 空间复杂度: O(1)
## 其他语言版本 ## 其他语言版本

View File

@ -37,6 +37,10 @@
题目数据确保队列可以被重建 题目数据确保队列可以被重建
# 视频讲解
**代码随想录算法视频公开课[贪心算法,不要两边一起贪,会顾此失彼 | LeetCode406.根据身高重建队列](https://www.bilibili.com/video/BV1EA411675Y)相信结合视频在看本篇题解更有助于大家对本题的理解**。
## 思路 ## 思路
本题有两个维度h和k看到这种题目一定要想如何确定一个维度然后再按照另一个维度重新排列 本题有两个维度h和k看到这种题目一定要想如何确定一个维度然后再按照另一个维度重新排列

View File

@ -236,9 +236,222 @@ for (int j = 0; j < m; j++) {
空间复杂度为O(n * m) 这个就不难理解了。开了几个 n * m 的数组。 空间复杂度为O(n * m) 这个就不难理解了。开了几个 n * m 的数组。
## 其他语言版本 ## 其他语言版本
### Java
深度优先遍历:
```Java
class Solution {
// 四个位置
private static final int[][] position = {{-1, 0}, {0, 1}, {1, 0}, {0, -1}};
/**
* @param heights 题目给定的二维数组
* @param row 当前位置的行号
* @param col 当前位置的列号
* @param sign 记录是哪一条河,两条河中可以一个为 0一个为 1
* @param visited 记录这个位置可以到哪条河
*/
public void dfs(int[][] heights, int row, int col, int sign, boolean[][][] visited) {
for (int[] current: position) {
int curRow = row + current[0], curCol = col + current[1];
// 越界
if (curRow < 0 || curRow >= heights.length || curCol < 0 || curCol >= heights[0].length)
continue;
// 高度不合适或者已经被访问过了
if (heights[curRow][curCol] < heights[row][col] || visited[curRow][curCol][sign]) continue;
visited[curRow][curCol][sign] = true;
dfs(heights, curRow, curCol, sign, visited);
}
}
public List<List<Integer>> pacificAtlantic(int[][] heights) {
int rowSize = heights.length, colSize = heights[0].length;
List<List<Integer>> ans = new ArrayList<>();
// 记录 [row, col] 位置是否可以到某条河,可以为 true反之为 false
// 假设太平洋的标记为 1大西洋为 0
boolean[][][] visited = new boolean[rowSize][colSize][2];
for (int row = 0; row < rowSize; row++) {
visited[row][colSize - 1][0] = true;
visited[row][0][1] = true;
dfs(heights, row, colSize - 1, 0, visited);
dfs(heights, row, 0, 1, visited);
}
for (int col = 0; col < colSize; col++) {
visited[rowSize - 1][col][0] = true;
visited[0][col][1] = true;
dfs(heights, rowSize - 1, col, 0, visited);
dfs(heights, 0, col, 1, visited);
}
for (int row = 0; row < rowSize; row++) {
for (int col = 0; col < colSize; col++) {
// 如果该位置即可以到太平洋又可以到大西洋,就放入答案数组
if (visited[row][col][0] && visited[row][col][1])
ans.add(List.of(row, col));
}
}
return ans;
}
}
```
广度优先遍历:
```Java
class Solution {
// 四个位置
private static final int[][] position = {{-1, 0}, {0, 1}, {1, 0}, {0, -1}};
/**
* @param heights 题目给定的二维数组
* @param queue 记录可以到达边界的节点
* @param visited 记录这个位置可以到哪条河
*/
public void bfs(int[][] heights, Queue<int[]> queue, boolean[][][] visited) {
while (!queue.isEmpty()) {
int[] curPos = queue.poll();
for (int[] current: position) {
int row = curPos[0] + current[0], col = curPos[1] + current[1], sign = curPos[2];
// 越界
if (row < 0 || row >= heights.length || col < 0 || col >= heights[0].length) continue;
// 高度不合适或者已经被访问过了
if (heights[row][col] < heights[curPos[0]][curPos[1]] || visited[row][col][sign]) continue;
visited[row][col][sign] = true;
queue.add(new int[]{row, col, sign});
}
}
}
public List<List<Integer>> pacificAtlantic(int[][] heights) {
int rowSize = heights.length, colSize = heights[0].length;
List<List<Integer>> ans = new ArrayList<>();
boolean[][][] visited = new boolean[rowSize][colSize][2];
// 队列,保存的数据为 [行号, 列号, 标记]
// 假设太平洋的标记为 1大西洋为 0
Queue<int[]> queue = new ArrayDeque<>();
for (int row = 0; row < rowSize; row++) {
visited[row][colSize - 1][0] = true;
visited[row][0][1] = true;
queue.add(new int[]{row, colSize - 1, 0});
queue.add(new int[]{row, 0, 1});
}
for (int col = 0; col < colSize; col++) {
visited[rowSize - 1][col][0] = true;
visited[0][col][1] = true;
queue.add(new int[]{rowSize - 1, col, 0});
queue.add(new int[]{0, col, 1});
}
bfs(heights, queue, visited);
for (int row = 0; row < rowSize; row++) {
for (int col = 0; col < colSize; col++) {
// 如果该位置即可以到太平洋又可以到大西洋,就放入答案数组
if (visited[row][col][0] && visited[row][col][1])
ans.add(List.of(row, col));
}
}
return ans;
}
}
```
### Python
深度优先遍历
```Python3
class Solution:
def __init__(self):
self.position = [[-1, 0], [0, 1], [1, 0], [0, -1]] # 四个方向
# heights题目给定的二维数组 row当前位置的行号 col当前位置的列号
# sign记录是哪一条河两条河中可以一个为 0一个为 1
# visited记录这个位置可以到哪条河
def dfs(self, heights: List[List[int]], row: int, col: int, sign: int, visited: List[List[List[int]]]):
for current in self.position:
curRow, curCol = row + current[0], col + current[1]
# 索引下标越界
if curRow < 0 or curRow >= len(heights) or curCol < 0 or curCol >= len(heights[0]): continue
# 不满足条件或者已经被访问过
if heights[curRow][curCol] < heights[row][col] or visited[curRow][curCol][sign]: continue
visited[curRow][curCol][sign] = True
self.dfs(heights, curRow, curCol, sign, visited)
def pacificAtlantic(self, heights: List[List[int]]) -> List[List[int]]:
rowSize, colSize = len(heights), len(heights[0])
# visited 记录 [row, col] 位置是否可以到某条河,可以为 true反之为 false
# 假设太平洋的标记为 1大西洋为 0
# ans 用来保存满足条件的答案
ans, visited = [], [[[False for _ in range(2)] for _ in range(colSize)] for _ in range(rowSize)]
for row in range(rowSize):
visited[row][0][1] = True
visited[row][colSize - 1][0] = True
self.dfs(heights, row, 0, 1, visited)
self.dfs(heights, row, colSize - 1, 0, visited)
for col in range(0, colSize):
visited[0][col][1] = True
visited[rowSize - 1][col][0] = True
self.dfs(heights, 0, col, 1, visited)
self.dfs(heights, rowSize - 1, col, 0, visited)
for row in range(rowSize):
for col in range(colSize):
# 如果该位置即可以到太平洋又可以到大西洋,就放入答案数组
if visited[row][col][0] and visited[row][col][1]:
ans.append([row, col])
return ans
```
广度优先遍历
```Python3
class Solution:
def __init__(self):
self.position = [[-1, 0], [0, 1], [1, 0], [0, -1]]
# heights题目给定的二维数组visited记录这个位置可以到哪条河
def bfs(self, heights: List[List[int]], queue: deque, visited: List[List[List[int]]]):
while queue:
curPos = queue.popleft()
for current in self.position:
row, col, sign = curPos[0] + current[0], curPos[1] + current[1], curPos[2]
# 越界
if row < 0 or row >= len(heights) or col < 0 or col >= len(heights[0]): continue
# 不满足条件或已经访问过
if heights[row][col] < heights[curPos[0]][curPos[1]] or visited[row][col][sign]: continue
visited[row][col][sign] = True
queue.append([row, col, sign])
def pacificAtlantic(self, heights: List[List[int]]) -> List[List[int]]:
rowSize, colSize = len(heights), len(heights[0])
# visited 记录 [row, col] 位置是否可以到某条河,可以为 true反之为 false
# 假设太平洋的标记为 1大西洋为 0
# ans 用来保存满足条件的答案
ans, visited = [], [[[False for _ in range(2)] for _ in range(colSize)] for _ in range(rowSize)]
# 队列,保存的数据为 [行号, 列号, 标记]
# 假设太平洋的标记为 1大西洋为 0
queue = deque()
for row in range(rowSize):
visited[row][0][1] = True
visited[row][colSize - 1][0] = True
queue.append([row, 0, 1])
queue.append([row, colSize - 1, 0])
for col in range(0, colSize):
visited[0][col][1] = True
visited[rowSize - 1][col][0] = True
queue.append([0, col, 1])
queue.append([rowSize - 1, col, 0])
self.bfs(heights, queue, visited) # 广度优先遍历
for row in range(rowSize):
for col in range(colSize):
# 如果该位置即可以到太平洋又可以到大西洋,就放入答案数组
if visited[row][col][0] and visited[row][col][1]:
ans.append([row, col])
return ans
```
<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

@ -30,6 +30,10 @@
* 输出: 0 * 输出: 0
* 解释: 你不需要移除任何区间,因为它们已经是无重叠的了。 * 解释: 你不需要移除任何区间,因为它们已经是无重叠的了。
# 视频讲解
**《代码随想录》算法视频公开课:[贪心算法,依然是判断重叠区间 | LeetCode435.无重叠区间](https://www.bilibili.com/video/BV1A14y1c7E1),相信结合视频在看本篇题解,更有助于大家对本题的理解**。
## 思路 ## 思路
**相信很多同学看到这道题目都冥冥之中感觉要排序,但是究竟是按照右边界排序,还是按照左边界排序呢?** **相信很多同学看到这道题目都冥冥之中感觉要排序,但是究竟是按照右边界排序,还是按照左边界排序呢?**
@ -395,18 +399,20 @@ object Solution {
```Rust ```Rust
impl Solution { impl Solution {
pub fn erase_overlap_intervals(intervals: Vec<Vec<i32>>) -> i32 { pub fn erase_overlap_intervals(intervals: Vec<Vec<i32>>) -> i32 {
if intervals.len() == 0 { return 0; } if intervals.is_empty() {
let mut intervals = intervals; return 0;
intervals.sort_by(|a, b| a[1].cmp(&b[1])); }
intervals.sort_by_key(|interval| interval[1]);
let mut count = 1; let mut count = 1;
let mut end = intervals[0][1]; let mut end = intervals[0][1];
for i in 1..intervals.len() { for v in intervals.iter().skip(1) {
if end <= intervals[i][0] { if end <= v[0] {
end = intervals[i][1]; end = v[1];
count += 1; count += 1;
} }
} }
intervals.len() as i32 - count
(intervals.len() - count) as i32
} }
} }
``` ```

View File

@ -42,6 +42,10 @@
* points[i].length == 2 * points[i].length == 2
* -2^31 <= xstart < xend <= 2^31 - 1 * -2^31 <= xstart < xend <= 2^31 - 1
# 视频讲解
**代码随想录算法视频公开课[贪心算法,判断重叠区间问题 | LeetCode452.用最少数量的箭引爆气球](https://www.bilibili.com/video/BV1SA41167xe)相信结合视频在看本篇题解更有助于大家对本题的理解**。
## 思路 ## 思路
如何使用最少的弓箭呢 如何使用最少的弓箭呢
@ -173,7 +177,23 @@ class Solution:
points[i][1] = min(points[i - 1][1], points[i][1]) # 更新重叠气球最小右边界 points[i][1] = min(points[i - 1][1], points[i][1]) # 更新重叠气球最小右边界
return result 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
```go ```go
func findMinArrowShots(points [][]int) int { func findMinArrowShots(points [][]int) int {

View File

@ -83,6 +83,9 @@ public:
``` ```
* 时间复杂度: O(n^2)
* 空间复杂度: O(n^2)最坏情况下A和B的值各不相同相加产生的数字个数为 n^2
@ -94,26 +97,25 @@ Java
```Java ```Java
class Solution { class Solution {
public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) { public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
Map<Integer, Integer> map = new HashMap<>();
int temp;
int res = 0; int res = 0;
Map<Integer, Integer> map = new HashMap<Integer, Integer>();
//统计两个数组中的元素之和同时统计出现的次数放入map //统计两个数组中的元素之和同时统计出现的次数放入map
for (int i : nums1) { for (int i : nums1) {
for (int j : nums2) { for (int j : nums2) {
temp = i + j; int tmp = map.getOrDefault(i + j, 0);
if (map.containsKey(temp)) { if (tmp == 0) {
map.put(temp, map.get(temp) + 1); map.put(i + j, 1);
} else { } else {
map.put(temp, 1); map.replace(i + j, tmp + 1);
} }
} }
} }
//统计剩余的两个元素的和在map中找是否存在相加为0的情况同时记录次数 //统计剩余的两个元素的和在map中找是否存在相加为0的情况同时记录次数
for (int i : nums3) { for (int i : nums3) {
for (int j : nums4) { for (int j : nums4) {
temp = i + j; int tmp = map.getOrDefault(0 - i - j, 0);
if (map.containsKey(0 - temp)) { if (tmp != 0) {
res += map.get(0 - temp); res += tmp;
} }
} }
} }

View File

@ -4,7 +4,6 @@
</a> </a>
<p align="center"><strong><a href="https://mp.weixin.qq.com/s/tqCxrMEU-ajQumL1i8im9A">参与本项目</a>,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!</strong></p> <p align="center"><strong><a href="https://mp.weixin.qq.com/s/tqCxrMEU-ajQumL1i8im9A">参与本项目</a>,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!</strong></p>
# 455.分发饼干 # 455.分发饼干
[力扣题目链接](https://leetcode.cn/problems/assign-cookies/) [力扣题目链接](https://leetcode.cn/problems/assign-cookies/)
@ -14,21 +13,26 @@
对每个孩子 i都有一个胃口值  g[i],这是能让孩子们满足胃口的饼干的最小尺寸;并且每块饼干 j都有一个尺寸 s[j] 。如果 s[j] >= g[i],我们可以将这个饼干 j 分配给孩子 i ,这个孩子会得到满足。你的目标是尽可能满足越多数量的孩子,并输出这个最大数值。 对每个孩子 i都有一个胃口值  g[i],这是能让孩子们满足胃口的饼干的最小尺寸;并且每块饼干 j都有一个尺寸 s[j] 。如果 s[j] >= g[i],我们可以将这个饼干 j 分配给孩子 i ,这个孩子会得到满足。你的目标是尽可能满足越多数量的孩子,并输出这个最大数值。
示例  1: 示例  1:
* 输入: g = [1,2,3], s = [1,1]
*: 1 -: g = [1,2,3], s = [1,1]
- 输出: 1
解释:你有三个孩子和两块小饼干3 个孩子的胃口值分别是1,2,3。虽然你有两块小饼干由于他们的尺寸都是 1你只能让胃口值是 1 的孩子满足。所以你应该输出 1。 解释:你有三个孩子和两块小饼干3 个孩子的胃口值分别是1,2,3。虽然你有两块小饼干由于他们的尺寸都是 1你只能让胃口值是 1 的孩子满足。所以你应该输出 1。
示例  2: 示例  2:
* 输入: g = [1,2], s = [1,2,3]
* 输出: 2
* 解释:你有两个孩子和三块小饼干2个孩子的胃口值分别是1,2。你拥有的饼干数量和尺寸都足以让所有孩子满足。所以你应该输出2.
- 输入: g = [1,2], s = [1,2,3]
- 输出: 2
- 解释:你有两个孩子和三块小饼干2 个孩子的胃口值分别是 1,2。你拥有的饼干数量和尺寸都足以让所有孩子满足。所以你应该输出 2.
提示: 提示:
* 1 <= g.length <= 3 * 10^4
* 0 <= s.length <= 3 * 10^4
* 1 <= g[i], s[j] <= 2^31 - 1
- 1 <= g.length <= 3 \* 10^4
- 0 <= s.length <= 3 \* 10^4
- 1 <= g[i], s[j] <= 2^31 - 1
# 视频讲解
**《代码随想录》算法视频公开课:[贪心算法,你想先喂哪个小孩?| LeetCode455.分发饼干](https://www.bilibili.com/video/BV1MM411b7cq),相信结合视频在看本篇题解,更有助于大家对本题的理解**。
## 思路 ## 思路
@ -46,16 +50,12 @@
![](https://code-thinking-1253855093.file.myqcloud.com/pics/20230405225628.png) ![](https://code-thinking-1253855093.file.myqcloud.com/pics/20230405225628.png)
这个例子可以看出饼干 9 只有喂给胃口为 7 的小孩,这样才是整体最优解,并想不出反例,那么就可以撸代码了。 这个例子可以看出饼干 9 只有喂给胃口为 7 的小孩,这样才是整体最优解,并想不出反例,那么就可以撸代码了。
C++代码整体如下: C++代码整体如下:
```CPP ```CPP
// 版本一 // 版本一
// 时间复杂度O(nlogn)
// 空间复杂度O(1)
class Solution { class Solution {
public: public:
int findContentChildren(vector<int>& g, vector<int>& s) { int findContentChildren(vector<int>& g, vector<int>& s) {
@ -73,12 +73,14 @@ public:
} }
}; };
``` ```
* 时间复杂度O(nlogn)
* 空间复杂度O(1)
从代码中可以看出我用了一个 index 来控制饼干数组的遍历,遍历饼干并没有再起一个 for 循环,而是采用自减的方式,这也是常用的技巧。 从代码中可以看出我用了一个 index 来控制饼干数组的遍历,遍历饼干并没有再起一个 for 循环,而是采用自减的方式,这也是常用的技巧。
有的同学看到要遍历两个数组,就想到用两个 for 循环,那样逻辑其实就复杂了。 有的同学看到要遍历两个数组,就想到用两个 for 循环,那样逻辑其实就复杂了。
### 注意事项 ### 注意事项
注意版本一的代码中,可以看出来,是先遍历的胃口,在遍历的饼干,那么可不可以 先遍历 饼干,在遍历胃口呢? 注意版本一的代码中,可以看出来,是先遍历的胃口,在遍历的饼干,那么可不可以 先遍历 饼干,在遍历胃口呢?
@ -95,7 +97,6 @@ if 里的 index 指向 胃口 10 for里的i指向饼干9因为 饼干9 满
所以 一定要 for 控制 胃口,里面的 if 控制饼干。 所以 一定要 for 控制 胃口,里面的 if 控制饼干。
### 其他思路 ### 其他思路
**也可以换一个思路,小饼干先喂饱小胃口** **也可以换一个思路,小饼干先喂饱小胃口**
@ -118,6 +119,9 @@ public:
} }
}; };
``` ```
* 时间复杂度O(nlogn)
* 空间复杂度O(1)
细心的录友可以发现,这种写法,两个循环的顺序改变了,先遍历的饼干,在遍历的胃口,这是因为遍历顺序变了,我们是从小到大遍历。 细心的录友可以发现,这种写法,两个循环的顺序改变了,先遍历的饼干,在遍历的胃口,这是因为遍历顺序变了,我们是从小到大遍历。
@ -131,8 +135,8 @@ public:
## 其他语言版本 ## 其他语言版本
### Java ### Java
```java ```java
class Solution { class Solution {
// 思路1优先考虑饼干小饼干先喂饱小胃口 // 思路1优先考虑饼干小饼干先喂饱小胃口
@ -151,6 +155,7 @@ class Solution {
} }
} }
``` ```
```java ```java
class Solution { class Solution {
// 思路2优先考虑胃口先喂饱大胃口 // 思路2优先考虑胃口先喂饱大胃口
@ -172,6 +177,7 @@ class Solution {
``` ```
### Python ### Python
```python ```python
class Solution: class Solution:
# 思路1优先考虑小胃口 # 思路1优先考虑小胃口
@ -184,6 +190,7 @@ class Solution:
res += 1 res += 1
return res return res
``` ```
```python ```python
class Solution: class Solution:
# 思路2优先考虑大胃口 # 思路2优先考虑大胃口
@ -199,6 +206,7 @@ class Solution:
``` ```
### Go ### Go
```golang ```golang
//排序后,局部最优 //排序后,局部最优
func findContentChildren(g []int, s []int) int { func findContentChildren(g []int, s []int) int {
@ -218,6 +226,7 @@ func findContentChildren(g []int, s []int) int {
``` ```
### Rust ### Rust
```rust ```rust
pub fn find_content_children(mut children: Vec<i32>, mut cookie: Vec<i32>) -> i32 { pub fn find_content_children(mut children: Vec<i32>, mut cookie: Vec<i32>) -> i32 {
children.sort(); children.sort();
@ -236,21 +245,21 @@ pub fn find_content_children(mut children: Vec<i32>, mut cookie: Vec<i32>) -> i3
``` ```
### Javascript ### Javascript
```js ```js
var findContentChildren = function (g, s) { var findContentChildren = function (g, s) {
g = g.sort((a, b) => a - b) g = g.sort((a, b) => a - b);
s = s.sort((a, b) => a - b) s = s.sort((a, b) => a - b);
let result = 0 let result = 0;
let index = s.length - 1 let index = s.length - 1;
for (let i = g.length - 1; i >= 0; i--) { for (let i = g.length - 1; i >= 0; i--) {
if (index >= 0 && s[index] >= g[i]) { if (index >= 0 && s[index] >= g[i]) {
result++ result++;
index-- index--;
} }
} }
return result return result;
}; };
``` ```
### TypeScript ### TypeScript
@ -273,7 +282,7 @@ function findContentChildren(g: number[], s: number[]): number {
curChild--; curChild--;
} }
return resCount; return resCount;
}; }
``` ```
```typescript ```typescript
@ -292,7 +301,7 @@ function findContentChildren(g: number[], s: number[]): number {
curCookie++; curCookie++;
} }
return curChild; return curChild;
}; }
``` ```
### C ### C

View File

@ -73,6 +73,8 @@ public:
} }
}; };
``` ```
* 时间复杂度: O(n)
* 空间复杂度: O(1)
不过这种解法还有一个问题,就是 我们最终还是要判断 一个字符串s + s是否出现过 s 的过程大家可能直接用containsfind 之类的库函数。 却忽略了实现这些函数的时间复杂度暴力解法是m * n一般库函数实现为 O(m + n))。 不过这种解法还有一个问题,就是 我们最终还是要判断 一个字符串s + s是否出现过 s 的过程大家可能直接用containsfind 之类的库函数。 却忽略了实现这些函数的时间复杂度暴力解法是m * n一般库函数实现为 O(m + n))。
@ -185,6 +187,8 @@ public:
} }
}; };
``` ```
* 时间复杂度: O(n)
* 空间复杂度: O(n)
前缀表不减一的C++代码实现: 前缀表不减一的C++代码实现:
@ -219,6 +223,8 @@ public:
} }
}; };
``` ```
* 时间复杂度: O(n)
* 空间复杂度: O(n)
## 其他语言版本 ## 其他语言版本

View File

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

View File

@ -167,7 +167,7 @@ dp[j] 表示填满j包括j这么大容积的包有dp[j]种方法
有哪些来源可以推出dp[j]呢? 有哪些来源可以推出dp[j]呢?
只要搞到nums[i]凑成dp[j]就有dp[j - nums[i]] 种方法。 只要搞到nums[i]凑成dp[j]就有dp[j - nums[i]] 种方法。
例如dp[j]j 为5 例如dp[j]j 为5

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 Python

View File

@ -221,8 +221,27 @@ class Solution:
for i in range(len(res)-1): // 统计有序数组的最小差值 for i in range(len(res)-1): // 统计有序数组的最小差值
r = min(abs(res[i]-res[i+1]),r) r = min(abs(res[i]-res[i+1]),r)
return r return r
class Solution: # 双指针法,不用数组 (同Carl写法) - 更快
def getMinimumDifference(self, root: Optional[TreeNode]) -> int:
global pre,minval
pre = None
minval = 10**5
self.traversal(root)
return minval
def traversal(self,root):
global pre,minval
if not root: return None
self.traversal(root.left)
if pre and root.val-pre.val<minval:
minval = root.val-pre.val
pre = root
self.traversal(root.right)
``` ```
迭代法-中序遍历 迭代法-中序遍历
```python ```python
class Solution: class Solution:

View File

@ -234,6 +234,26 @@ class Solution:
return root 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 ## Go

View File

@ -65,6 +65,9 @@ public:
}; };
``` ```
* 时间复杂度: O(n)
* 空间复杂度: O(1)
@ -96,6 +99,9 @@ public:
}; };
``` ```
* 时间复杂度: O(n)
* 空间复杂度: O(1)或O(n), 取决于使用的语言中字符串是否可以修改.
另一种思路的解法 另一种思路的解法
@ -116,6 +122,9 @@ public:
}; };
``` ```
* 时间复杂度: O(n)
* 空间复杂度: O(1)
## 其他语言版本 ## 其他语言版本

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

@ -133,6 +133,11 @@ public:
LinkedNode* tmp = cur->next; LinkedNode* tmp = cur->next;
cur->next = cur->next->next; cur->next = cur->next->next;
delete tmp; delete tmp;
//delete命令指示释放了tmp指针原本所指的那部分内存
//被delete后的指针tmp的值地址并非就是NULL而是随机值。也就是被delete后
//如果不再加上一句tmp=nullptr,tmp会成为乱指的野指针
//如果之后的程序不小心使用了tmp会指向难以预想的内存空间
tmp=nullptr;
_size--; _size--;
} }
@ -152,6 +157,9 @@ private:
}; };
``` ```
* 时间复杂度: 涉及 `index` 的相关操作为 O(index), 其余为 O(1)
* 空间复杂度: O(n)
## 其他语言版本 ## 其他语言版本
@ -1447,3 +1455,4 @@ impl MyLinkedList {
<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

@ -26,6 +26,10 @@
说明: N 是在 [0, 10^9] 范围内的一个整数。 说明: N 是在 [0, 10^9] 范围内的一个整数。
# 视频讲解
**《代码随想录》算法视频公开课:[贪心算法思路不难想但代码不好写LeetCode:738.单调自增的数字](https://www.bilibili.com/video/BV1Kv4y1x7tP),相信结合视频在看本篇题解,更有助于大家对本题的理解**。
## 暴力解法 ## 暴力解法
@ -276,18 +280,20 @@ object Solution {
```Rust ```Rust
impl Solution { impl Solution {
pub fn monotone_increasing_digits(n: i32) -> i32 { 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 n_bytes = n.to_string().into_bytes();
let mut flag = str_num.len(); let mut flag = n_bytes.len();
for i in (1..str_num.len()).rev() { for i in (1..n_bytes.len()).rev() {
if str_num[i - 1] > str_num[i] { if n_bytes[i - 1] > n_bytes[i] {
flag = i; flag = i;
str_num[i - 1] -= 1; n_bytes[i - 1] -= 1;
} }
} }
for i in flag..str_num.len() { for v in n_bytes.iter_mut().skip(flag) {
str_num[i] = 9; *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
> 未精简版本
```python ```python
class Solution: class Solution:
@ -291,6 +292,21 @@ class Solution:
return answer 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 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; 所以初始化 dp[0] = 0dp[1] = 0;
@ -284,6 +284,33 @@ func min(a, b int) int {
return b return b
} }
``` ```
``` GO
第二种思路: dp[i]表示从i层起跳所需要支付的最小费用
递推公式:
i<n :dp[i] = min(dp[i-1],dp[i-2])+cost[i]
i==n:dp[i] = min(dp[i-1],dp[i-2]) (登顶)
func minCostClimbingStairs(cost []int) int {
n := len(cost)
dp := make([]int, n+1)
dp[0], dp[1] = cost[0], cost[1]
for i := 2; i <= n; i++ {
if i < n {
dp[i] = min(dp[i-1], dp[i-2]) + cost[i]
} else {
dp[i] = min(dp[i-1], dp[i-2])
}
}
return dp[n]
}
func min(a, b int) int {
if a < b {
return a
}
return b
}
```
### Javascript ### Javascript
```Javascript ```Javascript

View File

@ -24,6 +24,10 @@
* S的长度在[1, 500]之间。 * S的长度在[1, 500]之间。
* S只包含小写字母 'a' 到 'z' 。 * S只包含小写字母 'a' 到 'z' 。
# 视频讲解
**《代码随想录》算法视频公开课:[贪心算法,寻找最远的出现位置! LeetCode763.划分字母区间](https://www.bilibili.com/video/BV18G4y1K7d5),相信结合视频在看本篇题解,更有助于大家对本题的理解**。
## 思路 ## 思路
一想到分割字符串就想到了回溯,但本题其实不用回溯去暴力搜索。 一想到分割字符串就想到了回溯,但本题其实不用回溯去暴力搜索。
@ -408,28 +412,22 @@ object Solution {
### Rust ### Rust
```Rust ```Rust
use std::collections::HashMap;
impl Solution { impl Solution {
fn max (a: usize, b: usize) -> usize {
if a > b { a } else { b }
}
pub fn partition_labels(s: String) -> Vec<i32> { pub fn partition_labels(s: String) -> Vec<i32> {
let s = s.chars().collect::<Vec<char>>(); let mut hash = vec![0; 26];
let mut hash: HashMap<char, usize> = HashMap::new(); for (i, &c) in s.as_bytes().iter().enumerate() {
for i in 0..s.len() { hash[(c - b'a') as usize] = i;
hash.insert(s[i], i);
} }
let mut result: Vec<i32> = Vec::new(); let mut res = vec![];
let mut left: usize = 0; let (mut left, mut right) = (0, 0);
let mut right: usize = 0; for (i, &c) in s.as_bytes().iter().enumerate() {
for i in 0..s.len() { right = right.max(hash[(c - b'a') as usize]);
right = Self::max(right, hash[&s[i]]);
if i == right { if i == right {
result.push((right - left + 1) as i32); res.push((right - left + 1) as i32);
left = i + 1; left = i + 1;
} }
} }
result res
} }
} }
``` ```

View File

@ -161,6 +161,35 @@ public:
## Java ## Java
```Java
// 深度优先遍历
class Solution {
List<List<Integer>> ans; // 用来存放满足条件的路径
List<Integer> cnt; // 用来保存 dfs 过程中的节点值
public void dfs(int[][] graph, int node) {
if (node == graph.length - 1) { // 如果当前节点是 n - 1那么就保存这条路径
ans.add(new ArrayList<>(cnt));
return;
}
for (int index = 0; index < graph[node].length; index++) {
int nextNode = graph[node][index];
cnt.add(nextNode);
dfs(graph, nextNode);
cnt.remove(cnt.size() - 1); // 回溯
}
}
public List<List<Integer>> allPathsSourceTarget(int[][] graph) {
ans = new ArrayList<>();
cnt = new ArrayList<>();
cnt.add(0); // 注意0 号节点要加入 cnt 数组中
dfs(graph, 0);
return ans;
}
}
```
## Python ## Python
```python ```python
class Solution: class Solution:
@ -192,3 +221,4 @@ class 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

@ -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"> <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

@ -50,6 +50,10 @@
* 0 <= bills.length <= 10000 * 0 <= bills.length <= 10000
* bills[i] 不是 5 就是 10 或是 20  * bills[i] 不是 5 就是 10 或是 20 
# 视频讲解
**《代码随想录》算法视频公开课:[贪心算法看上去复杂其实逻辑都是固定的LeetCode860.柠檬水找零](https://www.bilibili.com/video/BV12x4y1j7DD),相信结合视频在看本篇题解,更有助于大家对本题的理解**。
## 思路 ## 思路
这是前几天的leetcode每日一题感觉不错给大家讲一下。 这是前几天的leetcode每日一题感觉不错给大家讲一下。
@ -111,6 +115,9 @@ public:
} }
}; };
``` ```
* 时间复杂度: O(n)
* 空间复杂度: O(1)
## 总结 ## 总结

View File

@ -38,6 +38,10 @@
* 给定树的节点数的范围是 [1, 1000]。 * 给定树的节点数的范围是 [1, 1000]。
* 每个节点的值都是 0。 * 每个节点的值都是 0。
# 视频讲解
**《代码随想录》算法视频公开课:[贪心算法,二叉树与贪心的结合,有点难...... LeetCode:968.监督二叉树](https://www.bilibili.com/video/BV1SA411U75i),相信结合视频在看本篇题解,更有助于大家对本题的理解**。
## 思路 ## 思路
@ -299,6 +303,11 @@ public:
``` ```
* 时间复杂度: O(n),需要遍历二叉树上的每个节点
* 空间复杂度: O(n)
大家可能会惊讶,居然可以这么简短,**其实就是在版本一的基础上使用else把一些情况直接覆盖掉了**。 大家可能会惊讶,居然可以这么简短,**其实就是在版本一的基础上使用else把一些情况直接覆盖掉了**。
在网上关于这道题解可以搜到很多这种神级别的代码,但都没讲不清楚,如果直接看代码的话,指定越看越晕,**所以建议大家对着版本一的代码一步一步来,版本二中看不中用!**。 在网上关于这道题解可以搜到很多这种神级别的代码,但都没讲不清楚,如果直接看代码的话,指定越看越晕,**所以建议大家对着版本一的代码一步一步来,版本二中看不中用!**。

View File

@ -34,6 +34,10 @@
* 1 <= K <= 10000 * 1 <= K <= 10000
* -100 <= A[i] <= 100 * -100 <= A[i] <= 100
# 视频讲解
**《代码随想录》算法视频公开课:[贪心算法这不就是常识还能叫贪心LeetCode1005.K次取反后最大化的数组和](https://www.bilibili.com/video/BV138411G7LY),相信结合视频在看本篇题解,更有助于大家对本题的理解**。
## 思路 ## 思路
本题思路其实比较好想了,如何可以让数组和最大呢? 本题思路其实比较好想了,如何可以让数组和最大呢?
@ -81,6 +85,10 @@ public:
}; };
``` ```
* 时间复杂度: O(nlogn)
* 空间复杂度: O(1)
## 总结 ## 总结
贪心的题目如果简单起来,会让人简单到开始怀疑:本来不就应该这么做么?这也算是算法?我认为这不是贪心? 贪心的题目如果简单起来,会让人简单到开始怀疑:本来不就应该这么做么?这也算是算法?我认为这不是贪心?
@ -231,23 +239,18 @@ var largestSumAfterKNegations = function(nums, k) {
```Rust ```Rust
impl Solution { impl Solution {
pub fn largest_sum_after_k_negations(nums: Vec<i32>, k: i32) -> i32 { pub fn largest_sum_after_k_negations(mut nums: Vec<i32>, mut k: i32) -> i32 {
let mut nums = nums; nums.sort_by_key(|b| std::cmp::Reverse(b.abs()));
let mut k = k; for v in nums.iter_mut() {
let len = nums.len(); if *v < 0 && k > 0 {
nums.sort_by(|a, b| b.abs().cmp(&a.abs())); *v *= -1;
for i in 0..len {
if nums[i] < 0 && k > 0 {
nums[i] *= -1;
k -= 1; k -= 1;
} }
} }
if k % 2 == 1 { nums[len - 1] *= -1; } if k % 2 == 1 {
let mut result = 0; *nums.last_mut().unwrap() *= -1;
for num in nums {
result += num;
} }
result nums.iter().sum()
} }
} }
``` ```

View File

@ -144,6 +144,232 @@ public:
} }
}; };
``` ```
## 其他语言版本
### Java
深度优先遍历版本:
```java
class Solution {
// 四个方向
private static final int[][] position = {{-1, 0}, {0, 1}, {1, 0}, {0, -1}};
// 深度优先遍历,把可以通向边缘部分的 1 全部标记成 true
public void dfs(int[][] grid, int row, int col, boolean[][] visited) {
for (int[] current: position) {
int newRow = row + current[0], newCol = col + current[1];
// 下标越界直接跳过
if (newRow < 0 || newRow >= grid.length || newCol < 0 || newCol >= grid[0].length) continue;
// 当前位置不是 1 或者已经被访问了就直接跳过
if (grid[newRow][newCol] != 1 || visited[newRow][newCol]) continue;
visited[newRow][newCol] = true;
dfs(grid, newRow, newCol, visited);
}
}
public int numEnclaves(int[][] grid) {
int rowSize = grid.length, colSize = grid[0].length, ans = 0; // ans 记录答案
// 标记数组记录每个值为 1 的位置是否可以到达边界,可以为 true反之为 false
boolean[][] visited = new boolean[rowSize][colSize];
// 左侧边界和右侧边界查找 1 进行标记并进行深度优先遍历
for (int row = 0; row < rowSize; row++) {
if (grid[row][0] == 1 && !visited[row][0]) {
visited[row][0] = true;
dfs(grid, row, 0, visited);
}
if (grid[row][colSize - 1] == 1 && !visited[row][colSize - 1]) {
visited[row][colSize - 1] = true;
dfs(grid, row, colSize - 1, visited);
}
}
// 上边界和下边界遍历,但是四个角不用遍历,因为上面已经遍历到了
for (int col = 1; col < colSize - 1; col++) {
if (grid[0][col] == 1 && !visited[0][col]) {
visited[0][col] = true;
dfs(grid, 0, col, visited);
}
if (grid[rowSize - 1][col] == 1 && !visited[rowSize - 1][col]) {
visited[rowSize - 1][col] = true;
dfs(grid, rowSize - 1, col, visited);
}
}
// 查找没有标记过的 1记录到 ans 中
for (int row = 0; row < rowSize; row++) {
for (int col = 0; col < colSize; col++) {
if (grid[row][col] == 1 && !visited[row][col]) ++ans;
}
}
return ans;
}
}
```
广度优先遍历版本:
```java
class Solution {
// 四个方向
private static final int[][] position = {{-1, 0}, {0, 1}, {1, 0}, {0, -1}};
// 广度优先遍历,把可以通向边缘部分的 1 全部标记成 true
public void bfs(int[][] grid, Queue<int[]> queue, boolean[][] visited) {
while (!queue.isEmpty()) {
int[] curPos = queue.poll();
for (int[] current: position) {
int row = curPos[0] + current[0], col = curPos[1] + current[1];
// 下标越界直接跳过
if (row < 0 || row >= grid.length || col < 0 || col >= grid[0].length)
continue;
// 当前位置不是 1 或者已经被访问了就直接跳过
if (visited[row][col] || grid[row][col] == 0) continue;
visited[row][col] = true;
queue.add(new int[]{row, col});
}
}
}
public int numEnclaves(int[][] grid) {
int rowSize = grid.length, colSize = grid[0].length, ans = 0; // ans 记录答案
// 标记数组记录每个值为 1 的位置是否可以到达边界,可以为 true反之为 false
boolean[][] visited = new boolean[rowSize][colSize];
Queue<int[]> queue = new ArrayDeque<>();
// 搜索左侧边界和右侧边界查找 1 存入队列
for (int row = 0; row < rowSize; row++) {
if (grid[row][0] == 1) {
visited[row][0] = true;
queue.add(new int[]{row, 0});
}
if (grid[row][colSize - 1] == 1) {
visited[row][colSize - 1] = true;
queue.add(new int[]{row, colSize - 1});
}
}
// 搜索上边界和下边界遍历,但是四个角不用遍历,因为上面已经遍历到了
for (int col = 1; col < colSize - 1; col++) {
if (grid[0][col] == 1) {
visited[0][col] = true;
queue.add(new int[]{0, col});
}
if (grid[rowSize - 1][col] == 1 && !visited[rowSize - 1][col]) {
visited[rowSize - 1][col] = true;
queue.add(new int[]{rowSize - 1, col});
}
}
bfs(grid, queue, visited); // 广度优先遍历
// 查找没有标记过的 1记录到 ans 中
for (int row = 0; row < rowSize; row++) {
for (int col = 0; col < colSize; col++) {
if (grid[row][col] == 1 && !visited[row][col]) ++ans;
}
}
return ans;
}
}
```
### Python
深度优先遍历
```Python3
class Solution:
def __init__(self):
self.position = [[-1, 0], [0, 1], [1, 0], [0, -1]] # 四个方向
# 深度优先遍历,把可以通向边缘部分的 1 全部标记成 true
def dfs(self, grid: List[List[int]], row: int, col: int, visited: List[List[bool]]) -> None:
for current in self.position:
newRow, newCol = row + current[0], col + current[1]
# 索引下标越界
if newRow < 0 or newRow >= len(grid) or newCol < 0 or newCol >= len(grid[0]):
continue
# 当前位置值不是 1 或者已经被访问过了
if grid[newRow][newCol] == 0 or visited[newRow][newCol]: continue
visited[newRow][newCol] = True
self.dfs(grid, newRow, newCol, visited)
def numEnclaves(self, grid: List[List[int]]) -> int:
rowSize, colSize, ans = len(grid), len(grid[0]), 0
# 标记数组记录每个值为 1 的位置是否可以到达边界,可以为 True反之为 False
visited = [[False for _ in range(colSize)] for _ in range(rowSize)]
# 搜索左边界和右边界,对值为 1 的位置进行深度优先遍历
for row in range(rowSize):
if grid[row][0] == 1:
visited[row][0] = True
self.dfs(grid, row, 0, visited)
if grid[row][colSize - 1] == 1:
visited[row][colSize - 1] = True
self.dfs(grid, row, colSize - 1, visited)
# 搜索上边界和下边界,对值为 1 的位置进行深度优先遍历,但是四个角不需要,因为上面遍历过了
for col in range(1, colSize - 1):
if grid[0][col] == 1:
visited[0][col] = True
self.dfs(grid, 0, col, visited)
if grid[rowSize - 1][col] == 1:
visited[rowSize - 1][col] = True
self.dfs(grid, rowSize - 1, col, visited)
# 找出矩阵中值为 1 但是没有被标记过的位置,记录答案
for row in range(rowSize):
for col in range(colSize):
if grid[row][col] == 1 and not visited[row][col]:
ans += 1
return ans
```
广度优先遍历
```Python3
class Solution:
def __init__(self):
self.position = [[-1, 0], [0, 1], [1, 0], [0, -1]] # 四个方向
# 广度优先遍历,把可以通向边缘部分的 1 全部标记成 true
def bfs(self, grid: List[List[int]], queue: deque, visited: List[List[bool]]) -> None:
while queue:
curPos = queue.popleft()
for current in self.position:
row, col = curPos[0] + current[0], curPos[1] + current[1]
# 索引下标越界
if row < 0 or row >= len(grid) or col < 0 or col >= len(grid[0]): continue
# 当前位置值不是 1 或者已经被访问过了
if grid[row][col] == 0 or visited[row][col]: continue
visited[row][col] = True
queue.append([row, col])
def numEnclaves(self, grid: List[List[int]]) -> int:
rowSize, colSize, ans = len(grid), len(grid[0]), 0
# 标记数组记录每个值为 1 的位置是否可以到达边界,可以为 True反之为 False
visited = [[False for _ in range(colSize)] for _ in range(rowSize)]
queue = deque() # 队列
# 搜索左侧边界和右侧边界查找 1 存入队列
for row in range(rowSize):
if grid[row][0] == 1:
visited[row][0] = True
queue.append([row, 0])
if grid[row][colSize - 1] == 1:
visited[row][colSize - 1] = True
queue.append([row, colSize - 1])
# 搜索上边界和下边界查找 1 存入队列,但是四个角不用遍历,因为上面已经遍历到了
for col in range(1, colSize - 1):
if grid[0][col] == 1:
visited[0][col] = True
queue.append([0, col])
if grid[rowSize - 1][col] == 1:
visited[rowSize - 1][col] = True
queue.append([rowSize - 1, col])
self.bfs(grid, queue, visited) # 广度优先遍历
# 找出矩阵中值为 1 但是没有被标记过的位置,记录答案
for row in range(rowSize):
for col in range(colSize):
if grid[row][col] == 1 and not visited[row][col]:
ans += 1
return ans
```
## 类似题目 ## 类似题目
* 1254. 统计封闭岛屿的数目 * 1254. 统计封闭岛屿的数目
@ -153,3 +379,4 @@ public:
<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

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

View File

@ -77,6 +77,9 @@ public:
} }
}; };
``` ```
* 时间复杂度: O(n)
* 空间复杂度: O(n)
当然可以拿字符串直接作为栈,这样省去了栈还要转为字符串的操作。 当然可以拿字符串直接作为栈,这样省去了栈还要转为字符串的操作。
@ -99,6 +102,8 @@ public:
} }
}; };
``` ```
* 时间复杂度: O(n)
* 空间复杂度: O(1),返回值不计空间复杂度
## 题外话 ## 题外话

View File

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

View File

@ -66,6 +66,9 @@ public:
} }
}; };
``` ```
* 时间复杂度: O(n)
* 空间复杂度O(1)
是不是发现这代码也太简单了,哈哈。 是不是发现这代码也太简单了,哈哈。
# 总结 # 总结

View File

@ -286,8 +286,70 @@ class Solution {
} }
``` ```
**90.子集II**
```java
class Solution {
List<List<Integer>> reslut = new ArrayList<>();
LinkedList<Integer> path = new LinkedList<>();
public List<List<Integer>> subsetsWithDup(int[] nums) {
if(nums.length == 0){
reslut.add(path);
return reslut;
}
Arrays.sort(nums);
backtracking(nums,0);
return reslut;
}
public void backtracking(int[] nums,int startIndex){
reslut.add(new ArrayList<>(path));
if(startIndex >= nums.length)return;
HashSet<Integer> hashSet = new HashSet<>();
for(int i = startIndex; i < nums.length; i++){
if(hashSet.contains(nums[i])){
continue;
}
hashSet.add(nums[i]);
path.add(nums[i]);
backtracking(nums,i+1);
path.removeLast();
}
}
}
```
**40.组合总和II**
```java
class Solution {
List<List<Integer>> result = new ArrayList<>();
LinkedList<Integer> path = new LinkedList<>();
public List<List<Integer>> combinationSum2(int[] candidates, int target) {
Arrays.sort( candidates );
if( candidates[0] > target ) return result;
backtracking(candidates,target,0,0);
return result;
}
public void backtracking(int[] candidates,int target,int sum,int startIndex){
if( sum > target )return;
if( sum == target ){
result.add( new ArrayList<>(path) );
}
HashSet<Integer> hashSet = new HashSet<>();
for( int i = startIndex; i < candidates.length; i++){
if( hashSet.contains(candidates[i]) ){
continue;
}
hashSet.add(candidates[i]);
path.add(candidates[i]);
sum += candidates[i];
backtracking(candidates,target,sum,i+1);
path.removeLast();
sum -= candidates[i];
}
}
}
```
Python Python
**90.子集II** **90.子集II**

View File

@ -177,6 +177,35 @@ Java
Python 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 Go

View File

@ -87,7 +87,7 @@ leetcode上没有纯01背包的问题都是01背包应用方面的题目
那么可以有两个方向推出来dp[i][j] 那么可以有两个方向推出来dp[i][j]
* **不放物品i**由dp[i - 1][j]推出即背包容量为j里面不放物品i的最大价值此时dp[i][j]就是dp[i - 1][j]。(其实就是当物品i的重量大于背包j的重量时物品i无法放进背包中所以背包内的价值依然和前面相同。) * **不放物品i**由dp[i - 1][j]推出即背包容量为j里面不放物品i的最大价值此时dp[i][j]就是dp[i - 1][j]。(其实就是当物品i的重量大于背包j的重量时物品i无法放进背包中所以背包内的价值依然和前面相同。)
* **放物品i**由dp[i - 1][j - weight[i]]推出dp[i - 1][j - weight[i]] 为背包容量为j - weight[i]的时候不放物品i的最大价值那么dp[i - 1][j - weight[i]] + value[i] 物品i的价值就是背包放物品i得到的最大价值 * **放物品i**由dp[i - 1][j - weight[i]]推出dp[i - 1][j - weight[i]] 为背包容量为j - weight[i]的时候不放物品i的最大价值那么dp[i - 1][j - weight[i]] + value[i] 物品i的价值就是背包放物品i得到的最大价值
所以递归公式: dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]); 所以递归公式: dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);

View File

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