Skip to content

Commit

Permalink
Merge branch 'youngyangyang04:master' into master
Browse files Browse the repository at this point in the history
  • Loading branch information
fwqaaq authored Apr 29, 2023
2 parents c835ec0 + f979407 commit b124152
Show file tree
Hide file tree
Showing 70 changed files with 1,314 additions and 837 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@
如果你是算法老手,这篇攻略也是复习的最佳资料,如果把每个系列对应的总结篇,快速过一遍,整个算法知识体系以及各种解法就重现脑海了。


目前「代码随想录」刷题攻略更新了:**200多篇文章,精讲了200道经典算法题目,共60w字的详细图解,部分难点题目还搭配了20分钟左右的视频讲解**
目前「代码随想录」刷题攻略更新了:**200多篇文章,精讲了200道经典算法题目,共60w字的详细图解,大部分题目都搭配了20分钟左右的视频讲解**,视频质量很好,口碑很好,大家可以去看看,视频列表:[代码随想录视频讲解](https://www.bilibili.com/video/BV1fA4y1o715)

**这里每一篇题解,都是精品,值得仔细琢磨**

Expand Down
20 changes: 20 additions & 0 deletions problems/0001.两数之和.md
Original file line number Diff line number Diff line change
Expand Up @@ -216,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

Expand Down
3 changes: 3 additions & 0 deletions problems/0020.有效的括号.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,9 @@ public:
};

```
* 时间复杂度: O(n)
* 空间复杂度: O(n)
技巧性的东西没有固定的学习方法,还是要多看多练,自己灵活运用了。
Expand Down
102 changes: 62 additions & 40 deletions problems/0045.跳跃游戏II.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,9 @@
</a>
<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

[力扣题目链接](https://leetcode.cn/problems/jump-game-ii/)

Expand All @@ -18,13 +17,17 @@
你的目标是使用最少的跳跃次数到达数组的最后一个位置。

示例:
* 输入: [2,3,1,1,4]
* 输出: 2
* 解释: 跳到最后一个位置的最小跳跃数是 2。从下标为 0 跳到下标为 1 的位置,跳 1 步,然后跳 3 步到达数组的最后一个位置。

- 输入: [2,3,1,1,4]
- 输出: 2
- 解释: 跳到最后一个位置的最小跳跃数是 2。从下标为 0 跳到下标为 1 的位置,跳  1  步,然后跳  3  步到达数组的最后一个位置。

说明:
假设你总是可以到达数组的最后一个位置。

# 视频讲解

**《代码随想录》算法视频公开课:[贪心算法,最少跳几步还得看覆盖范围 | LeetCode: 45.跳跃游戏 II](https://www.bilibili.com/video/BV1Y24y1r7XZ),相信结合视频在看本篇题解,更有助于大家对本题的理解**

## 思路

Expand All @@ -46,7 +49,6 @@

如图:


![45.跳跃游戏II](https://code-thinking-1253855093.file.myqcloud.com/pics/20201201232309103.png)

**图中覆盖范围的意义在于,只要红色的区域,最多两步一定可以到!(不用管具体怎么跳,反正一定可以跳到)**
Expand All @@ -57,8 +59,8 @@

这里还是有个特殊情况需要考虑,当移动下标达到了当前覆盖的最远距离下标时

* 如果当前覆盖最远距离下标不是是集合终点,步数就加一,还需要继续走。
* 如果当前覆盖最远距离下标就是是集合终点,步数不用加一,因为不能再往后走了。
- 如果当前覆盖最远距离下标不是是集合终点,步数就加一,还需要继续走。
- 如果当前覆盖最远距离下标就是是集合终点,步数不用加一,因为不能再往后走了。

C++代码如下:(详细注释)

Expand All @@ -74,32 +76,34 @@ public:
for (int i = 0; i < nums.size(); i++) {
nextDistance = max(nums[i] + i, nextDistance); // 更新下一步覆盖最远距离下标
if (i == curDistance) { // 遇到当前覆盖最远距离下标
if (curDistance < nums.size() - 1) { // 如果当前覆盖最远距离下标不是终点
ans++; // 需要走下一步
curDistance = nextDistance; // 更新当前覆盖最远距离下标(相当于加油了)
if (nextDistance >= nums.size() - 1) break; // 下一步的覆盖范围已经可以达到终点,结束循环
} else break; // 当前覆盖最远距到达集合终点,不用做ans++操作了,直接结束
ans++; // 需要走下一步
curDistance = nextDistance; // 更新当前覆盖最远距离下标(相当于加油了)
if (nextDistance >= nums.size() - 1) break; // 当前覆盖最远距到达集合终点,不用做ans++操作了,直接结束
}
}
return ans;
}
};
```
* 时间复杂度: O(n)
* 空间复杂度: O(1)
## 方法二
依然是贪心,思路和方法一差不多,代码可以简洁一些。
**针对于方法一的特殊情况,可以统一处理**,即:移动下标只要遇到当前覆盖最远距离的下标,直接步数加一,不考虑是不是终点的情况。
想要达到这样的效果,只要让移动下标,最大只能移动到nums.size - 2的地方就可以了
想要达到这样的效果,只要让移动下标,最大只能移动到 nums.size - 2 的地方就可以了
因为当移动下标指向nums.size - 2时
因为当移动下标指向 nums.size - 2 时
* 如果移动下标等于当前覆盖最大距离下标, 需要再走一步(即ans++),因为最后一步一定是可以到的终点。(题目假设总是可以到达数组的最后一个位置),如图:
![45.跳跃游戏II2](https://code-thinking-1253855093.file.myqcloud.com/pics/20201201232445286.png)
- 如果移动下标等于当前覆盖最大距离下标, 需要再走一步(即 ans++),因为最后一步一定是可以到的终点。(题目假设总是可以到达数组的最后一个位置),如图:
![45.跳跃游戏II2](https://code-thinking-1253855093.file.myqcloud.com/pics/20201201232445286.png)
* 如果移动下标不等于当前覆盖最大距离下标,说明当前覆盖最远距离就可以直接达到终点了,不需要再走一步。如图:
- 如果移动下标不等于当前覆盖最大距离下标,说明当前覆盖最远距离就可以直接达到终点了,不需要再走一步。如图:
![45.跳跃游戏II1](https://code-thinking-1253855093.file.myqcloud.com/pics/20201201232338693.png)
Expand All @@ -125,9 +129,14 @@ public:
};
```

* 时间复杂度: O(n)
* 空间复杂度: O(1)



可以看出版本二的代码相对于版本一简化了不少!

**其精髓在于控制移动下标i只移动到nums.size() - 2的位置**,所以移动下标只要遇到当前覆盖最远距离的下标,直接步数加一,不用考虑别的了。
**其精髓在于控制移动下标 i 只移动到 nums.size() - 2 的位置**,所以移动下标只要遇到当前覆盖最远距离的下标,直接步数加一,不用考虑别的了。

## 总结

Expand All @@ -137,11 +146,10 @@ public:

理解本题的关键在于:**以最小的步数增加最大的覆盖范围,直到覆盖范围覆盖了终点**,这个范围内最小步数一定可以跳到,不用管具体是怎么跳的,不纠结于一步究竟跳一个单位还是两个单位。


## 其他语言版本

### Java

### Java
```Java
// 版本一
class Solution {
Expand Down Expand Up @@ -207,7 +215,7 @@ class Solution:
nextDistance = 0
for i in range(len(nums)):
nextDistance = max(i + nums[i], nextDistance)
if i == curDistance:
if i == curDistance:
if curDistance != len(nums) - 1:
ans += 1
curDistance = nextDistance
Expand All @@ -230,9 +238,25 @@ class Solution:
step += 1
return step
```
```python
# 贪心版本三 - 类似‘55-跳跃游戏’写法
class Solution:
def jump(self, nums) -> int:
if len(nums)==1: return 0
i = 0
count = 0
cover = 0
while i<=cover:
for i in range(i,cover+1):
cover = max(nums[i]+i,cover)
if cover>=len(nums)-1: return count+1
count+=1

```

```python
# 动态规划做法
class Solution:
class Solution:
def jump(self, nums: List[int]) -> int:
result = [10**4+1]*len(nums)
result[0]=0
Expand All @@ -244,7 +268,6 @@ class Solution:

```


### Go

```go
Expand Down Expand Up @@ -331,21 +354,21 @@ var jump = function(nums) {

```typescript
function jump(nums: number[]): number {
const length: number = nums.length;
let curFarthestIndex: number = 0,
nextFarthestIndex: number = 0;
let curIndex: number = 0;
let stepNum: number = 0;
while (curIndex < length - 1) {
nextFarthestIndex = Math.max(nextFarthestIndex, curIndex + nums[curIndex]);
if (curIndex === curFarthestIndex) {
curFarthestIndex = nextFarthestIndex;
stepNum++;
}
curIndex++;
const length: number = nums.length;
let curFarthestIndex: number = 0,
nextFarthestIndex: number = 0;
let curIndex: number = 0;
let stepNum: number = 0;
while (curIndex < length - 1) {
nextFarthestIndex = Math.max(nextFarthestIndex, curIndex + nums[curIndex]);
if (curIndex === curFarthestIndex) {
curFarthestIndex = nextFarthestIndex;
stepNum++;
}
return stepNum;
};
curIndex++;
}
return stepNum;
}
```

### Scala
Expand Down Expand Up @@ -427,7 +450,6 @@ impl Solution {
}
```


<p align="center">
<a href="https://programmercarl.com/other/kstar.html" target="_blank">
<img src="../pics/网站星球宣传海报.jpg" width="1000"/>
Expand Down
13 changes: 13 additions & 0 deletions problems/0047.全排列II.md
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,19 @@ if (i > 0 && nums[i] == nums[i - 1] && used[i - 1] == true) {

所以我通过举[1,1,1]的例子,把这两个去重的逻辑分别抽象成树形结构,大家可以一目了然:为什么两种写法都可以以及哪一种效率更高!

这里可能大家又有疑惑,既然 `used[i - 1] == false`也行而`used[i - 1] == true`也行,那为什么还要写这个条件呢?

直接这样写 不就完事了?

```cpp
if (i > 0 && nums[i] == nums[i - 1]) {
continue;
}
```

其实并不行,一定要加上 `used[i - 1] == false`或者`used[i - 1] == true`,因为 used[i - 1] 要一直是 true 或者一直是false 才可以,而不是 一会是true 一会又是false。 所以这个条件要写上。


是不是豁然开朗了!!

## 其他语言版本
Expand Down
Loading

0 comments on commit b124152

Please sign in to comment.