mirror of
https://github.com/youngyangyang04/leetcode-master.git
synced 2025-07-08 00:43:04 +08:00
Update
This commit is contained in:
@ -357,6 +357,7 @@
|
||||
|[0491.递增子序列](https://github.com/youngyangyang04/leetcode/blob/master/problems/0491.递增子序列.md) |深度优先搜索 |中等|**深度优先搜索/回溯算法** 这个去重有意思|
|
||||
|[0496.下一个更大元素I](https://github.com/youngyangyang04/leetcode/blob/master/problems/0496.下一个更大元素I.md) |栈 |中等|**单调栈** 入门题目,但是两个数组还是有点绕的|
|
||||
|[0501.二叉搜索树中的众数](https://github.com/youngyangyang04/leetcode/blob/master/problems/0501.二叉搜索树中的众数.md) |二叉树 |简单|**递归/中序遍历**|
|
||||
|[0509.斐波那契数](https://github.com/youngyangyang04/leetcode/blob/master/problems/0509.斐波那契数.md) |数组 |简单|**递归** **动态规划**|
|
||||
|[0513.找树左下角的值](https://github.com/youngyangyang04/leetcode/blob/master/problems/0513.找树左下角的值.md) |二叉树 |中等|**递归** **迭代**|
|
||||
|[0515.在每个树行中找最大值](https://github.com/youngyangyang04/leetcode/blob/master/problems/0515.在每个树行中找最大值.md) |二叉树 |简单|**广度优先搜索/队列**|
|
||||
|[0518.零钱兑换II](https://github.com/youngyangyang04/leetcode/blob/master/problems/0518.零钱兑换II.md) |动态规划 |中等|**动态规划** dp里求组合|
|
||||
@ -383,6 +384,7 @@
|
||||
|[0763.划分字母区间](https://github.com/youngyangyang04/leetcode/blob/master/problems/0763.划分字母区间.md) |贪心 |中等|**双指针/贪心** 体现贪心尽可能多的思想|
|
||||
|[0738.单调递增的数字](https://github.com/youngyangyang04/leetcode/blob/master/problems/0738.单调递增的数字.md) |贪心算法 |中等|**贪心算法** 思路不错,贪心好题|
|
||||
|[0739.每日温度](https://github.com/youngyangyang04/leetcode/blob/master/problems/0739.每日温度.md) |栈 |中等|**单调栈** 适合单调栈入门|
|
||||
|[0746.使用最小花费爬楼梯](https://github.com/youngyangyang04/leetcode/blob/master/problems/0746.使用最小花费爬楼梯.md) |动态规划 |简单|**动态规划** 入门题目和斐波那契额数列类似|
|
||||
|[0767.重构字符串](https://github.com/youngyangyang04/leetcode/blob/master/problems/0767.重构字符串.md) |字符串 |中等|**字符串** + 排序+一点贪心|
|
||||
|[0841.钥匙和房间](https://github.com/youngyangyang04/leetcode/blob/master/problems/0841.钥匙和房间.md) |孤岛问题 |中等|**bfs** **dfs**|
|
||||
|[0844.比较含退格的字符串](https://github.com/youngyangyang04/leetcode/blob/master/problems/0844.比较含退格的字符串.md) |字符串 |简单|**栈** **双指针优化** 使用栈的思路但没有必要使用栈|
|
||||
|
@ -1,19 +1,69 @@
|
||||
<p align='center'>
|
||||
<img src="https://img-blog.csdnimg.cn/20201215214102642.png" width=400 >
|
||||
</p>
|
||||
<p align="center">
|
||||
<a href="https://github.com/youngyangyang04/leetcode-master"><img src="https://img.shields.io/badge/Github-leetcode--master-lightgrey" alt=""></a>
|
||||
<a href="https://img-blog.csdnimg.cn/20201115103410182.png"><img src="https://img.shields.io/badge/刷题-微信群-green" alt=""></a>
|
||||
<a href="https://img-blog.csdnimg.cn/20201210231711160.png"><img src="https://img.shields.io/badge/公众号-代码随想录-brightgreen" alt=""></a>
|
||||
<a href="https://space.bilibili.com/525438321"><img src="https://img.shields.io/badge/B站-代码随想录-orange" alt=""></a>
|
||||
<a href="https://www.zhihu.com/people/sun-xiu-yang-64"><img src="https://img.shields.io/badge/知乎-代码随想录-blue" alt=""></a>
|
||||
<a href="https://www.toutiao.com/c/user/60356270818/#mid=1633692776932365"><img src="https://img.shields.io/badge/头条-代码随想录-red" alt=""></a>
|
||||
</p>
|
||||
|
||||
> 思路很直接,但代码并不好写
|
||||
|
||||
# 452. 用最少数量的箭引爆气球
|
||||
|
||||
在二维空间中有许多球形的气球。对于每个气球,提供的输入是水平方向上,气球直径的开始和结束坐标。由于它是水平的,所以纵坐标并不重要,因此只要知道开始和结束的横坐标就足够了。开始坐标总是小于结束坐标。
|
||||
|
||||
一支弓箭可以沿着 x 轴从不同点完全垂直地射出。在坐标 x 处射出一支箭,若有一个气球的直径的开始和结束坐标为 xstart,xend, 且满足 xstart ≤ x ≤ xend,则该气球会被引爆。可以射出的弓箭的数量没有限制。 弓箭一旦被射出之后,可以无限地前进。我们想找到使得所有气球全部被引爆,所需的弓箭的最小数量。
|
||||
|
||||
给你一个数组 points ,其中 points [i] = [xstart,xend] ,返回引爆所有气球所必须射出的最小弓箭数。
|
||||
|
||||
|
||||
示例 1:
|
||||
输入:points = [[10,16],[2,8],[1,6],[7,12]]
|
||||
|
||||
输出:2
|
||||
解释:对于该样例,x = 6 可以射爆 [2,8],[1,6] 两个气球,以及 x = 11 射爆另外两个气球
|
||||
|
||||
示例 2:
|
||||
输入:points = [[1,2],[3,4],[5,6],[7,8]]
|
||||
输出:4
|
||||
|
||||
示例 3:
|
||||
输入:points = [[1,2],[2,3],[3,4],[4,5]]
|
||||
输出:2
|
||||
|
||||
示例 4:
|
||||
输入:points = [[1,2]]
|
||||
输出:1
|
||||
|
||||
示例 5:
|
||||
输入:points = [[2,3],[2,3]]
|
||||
输出:1
|
||||
|
||||
提示:
|
||||
|
||||
* 0 <= points.length <= 10^4
|
||||
* points[i].length == 2
|
||||
* -2^31 <= xstart < xend <= 2^31 - 1
|
||||
|
||||
# 思路
|
||||
|
||||
接下来在想一下如何使用最少的弓箭。
|
||||
如何使用最少的弓箭呢?
|
||||
|
||||
直觉上来看,貌似只射重叠最多的气球,用的弓箭一定最少,那么有没有当前重叠了三个,我射两个,留下一个和后面的一起射这样弓箭用的更少的情况呢?
|
||||
直觉上来看,貌似只射重叠最多的气球,用的弓箭一定最少,那么有没有当前重叠了三个气球,我射两个,留下一个和后面的一起射这样弓箭用的更少的情况呢?
|
||||
|
||||
尝试一下举反例,发现没有这种情况,**那么就试一试贪心吧!**
|
||||
尝试一下举反例,发现没有这种情况。
|
||||
|
||||
算法确定下来了,那么如何模拟气球涉爆的过程呢?是在数组中移除元素还是做标记呢?
|
||||
那么就试一试贪心吧!局部最优:当气球出现重叠,一起射,所用弓箭最少。全局最优:把所有气球射爆所用弓箭最少。
|
||||
|
||||
**算法确定下来了,那么如何模拟气球射爆的过程呢?是在数组中移除元素还是做标记呢?**
|
||||
|
||||
如果真实的模拟射气球的过程,应该射一个,气球数组就remove一个元素,这样最直观,毕竟气球被射了。
|
||||
|
||||
但又想一下,如果把气球排序之后,从前到后遍历气球,被射过的气球仅仅跳过就行了,没有必要让气球数组remove气球,记录一下箭的数量就可以了。
|
||||
|
||||
> PS:本文[leetcode刷题攻略](https://github.com/youngyangyang04/leetcode-master)已收录,更多[精彩算法文章](https://mp.weixin.qq.com/mp/appmsgalbum?__biz=MzUxNjY5NTYxNA==&action=getalbum&album_id=1485825793120387074&scene=173#wechat_redirect)尽在公众号:[代码随想录](https://img-blog.csdnimg.cn/20200815195519696.png),关注后就会发现和「代码随想录」相见恨晚!
|
||||
但仔细思考一下就发现:如果把气球排序之后,从前到后遍历气球,被射过的气球仅仅跳过就行了,没有必要让气球数组remote气球,只要记录一下箭的数量就可以了。
|
||||
|
||||
以上为思考过程,已经确定下来使用贪心了,那么开始解题。
|
||||
|
||||
@ -27,7 +77,7 @@
|
||||
|
||||
从前向后遍历遇到重叠的气球了怎么办?
|
||||
|
||||
如果气球重叠了,重叠气球中右边边界的最小值 之前的区间一定需要一个弓箭。
|
||||
**如果气球重叠了,重叠气球中右边边界的最小值 之前的区间一定需要一个弓箭**。
|
||||
|
||||
以题目示例: [[10,16],[2,8],[1,6],[7,12]]为例,如图:(方便起见,已经排序)
|
||||
|
||||
@ -62,8 +112,10 @@ public:
|
||||
};
|
||||
```
|
||||
|
||||
时间复杂度O(nlogn)
|
||||
空间复杂度O(1)
|
||||
* 时间复杂度O(nlogn),因为有一个快排
|
||||
* 空间复杂度O(1)
|
||||
|
||||
可以看出代码并不复杂。
|
||||
|
||||
# 注意事项
|
||||
|
||||
@ -71,8 +123,23 @@ public:
|
||||
|
||||
所以代码中 `if (points[i][0] > points[i - 1][1])` 不能是>=
|
||||
|
||||
# 总结
|
||||
|
||||
> **我是[程序员Carl](https://github.com/youngyangyang04),可以找我[组队刷题](https://img-blog.csdnimg.cn/20201115103410182.png),也可以在[B站上找到我](https://space.bilibili.com/525438321),本文[leetcode刷题攻略](https://github.com/youngyangyang04/leetcode-master)已收录,更多[精彩算法文章](https://mp.weixin.qq.com/mp/appmsgalbum?__biz=MzUxNjY5NTYxNA==&action=getalbum&album_id=1485825793120387074&scene=173#wechat_redirect)尽在公众号:[代码随想录](https://img-blog.csdnimg.cn/20200815195519696.png),关注后就会发现和「代码随想录」相见恨晚!**
|
||||
这道题目贪心的思路很简单也很直接,就是重复的一起射了,但本题我认为是有难度的。
|
||||
|
||||
**如果感觉题解对你有帮助,不要吝啬给一个👍吧!**
|
||||
就算思路都想好了,模拟射气球的过程,很多同学真的要去模拟了,实时把气球从数组中移走,这么写的话就复杂了。
|
||||
|
||||
而且寻找重复的气球,寻找重叠气球最小右边界,其实都有代码技巧。
|
||||
|
||||
贪心题目有时候就是这样,看起来很简单,思路很直接,但是一写代码就感觉贼复杂无从下手。
|
||||
|
||||
这里其实是需要代码功底的,那代码功底怎么练?
|
||||
|
||||
**多看多写多总结!**
|
||||
|
||||
|
||||
**循序渐进学算法,认准[「代码随想录」](https://img-blog.csdnimg.cn/20200815195519696.png),Carl手把手带你过关斩将!**
|
||||
|
||||
<p align='center'>
|
||||
<img src="https://img-blog.csdnimg.cn/20201216002707465.jpg" width=200 >
|
||||
</p>
|
||||
|
15
problems/0509.斐波那契数.md
Normal file
15
problems/0509.斐波那契数.md
Normal file
@ -0,0 +1,15 @@
|
||||
```
|
||||
class Solution {
|
||||
public:
|
||||
int fib(int N) {
|
||||
if (N <= 1) return N;
|
||||
vector<int> dp(N + 1);
|
||||
dp[0] = 0;
|
||||
dp[1] = 1;
|
||||
for (int i = 2; i <= N; i++) {
|
||||
dp[i] = dp[i - 1] + dp[i - 2];
|
||||
}
|
||||
return dp[N];
|
||||
}
|
||||
};
|
||||
```
|
106
problems/0746.使用最小花费爬楼梯.md
Normal file
106
problems/0746.使用最小花费爬楼梯.md
Normal file
@ -0,0 +1,106 @@
|
||||
|
||||
这个爬楼梯的问题和斐波那契数列问题很像。
|
||||
|
||||
读完题大家应该知道指定需要动态规划的,贪心是不可能了。
|
||||
|
||||
本题在动态规划里相对简单一些,以至于大家可能看个公式就会了,但为了讲清楚思考过程,我按照我总结的动规四步曲来详细讲解:
|
||||
|
||||
1. 确定dp数组以及下标的含义
|
||||
2. 确定递推公式
|
||||
3. dp数组如何初始化
|
||||
4. 确定遍历顺序
|
||||
|
||||
|
||||
* 确定dp数组以及下标的含义
|
||||
|
||||
使用动态规划,就要有一个数组来记录状态,本题只需要一个一维数组dp[i]就可以了。
|
||||
|
||||
dp[i]的定义:第i个台阶所花费的最少体力为dp[i]。
|
||||
|
||||
**对于dp数组的定义,大家一定要清晰!**
|
||||
|
||||
* 确定递推公式
|
||||
|
||||
**可以有两个途径得到dp[i],一个是dp[i-1] 一个是dp[i-2]**。
|
||||
|
||||
那么究竟是选dp[i-1]还是dp[i-2]呢?
|
||||
|
||||
一定是选最小的,所以dp[i] = min(dp[i - 1], dp[i - 2]) + cost[i];
|
||||
|
||||
**注意这里为什么是加cost[i],而不是cost[i-1],cost[i-2]之类的**,因为题目中说了:第i个阶梯对应着一个非负数的体力花费值 cost[i]
|
||||
|
||||
* dp数组如何初始化
|
||||
|
||||
根据dp数组的定义,dp数组初始化其实是比较难的,因为不可能初始化为第i台阶所花费的最少体力。
|
||||
|
||||
那么看一下递归公式,dp[i]由dp[i-1],dp[i-2]推出,既然初始化所有的dp[i]是不可能的,那么只初始化dp[0]和dp[1]就够了,其他的最终都是dp[0]dp[1]推出。
|
||||
|
||||
所以初始化代码为:
|
||||
|
||||
```
|
||||
vector<int> dp(cost.size());
|
||||
dp[0] = cost[0];
|
||||
dp[1] = cost[1];
|
||||
```
|
||||
|
||||
* 确定遍历顺序
|
||||
|
||||
最后一步,递归公式有了,初始化有了,如何遍历呢?
|
||||
|
||||
本题的遍历顺序其实比较简单,简单到很多同学都忽略了思考这一步直接就把代码写出来了。
|
||||
|
||||
因为是模拟台阶,而且dp[i]又dp[i-1]dp[i-2]推出,所以是从前到后遍历cost数组就可以了。
|
||||
|
||||
但是稍稍有点难度的动态规划,其遍历顺序并不容易确定下来。
|
||||
|
||||
例如01背包,都知道两个for循环,一个for遍历物品嵌套一个for遍历背包容量,那么为什么不是一个for遍历背包容量嵌套一个for遍历物品呢? 以及在使用一维dp数组的时候遍历背包容量为什么要倒叙呢? 这些都是遍历顺序息息相关。
|
||||
|
||||
公众号「代码随想录」后面在讲解动态规划的时候,还会详细说明这些细节,大家可以微信搜一波「代码随想录」,关注后就会发现相见恨晚!
|
||||
|
||||
分析完动规四步曲,整体C++代码如下:
|
||||
|
||||
```C++
|
||||
// 版本一
|
||||
class Solution {
|
||||
public:
|
||||
int minCostClimbingStairs(vector<int>& cost) {
|
||||
vector<int> dp(cost.size());
|
||||
dp[0] = cost[0];
|
||||
dp[1] = cost[1];
|
||||
for (int i = 2; i < cost.size(); i++) {
|
||||
dp[i] = min(dp[i - 1], dp[i - 2]) + cost[i];
|
||||
}
|
||||
return min(dp[cost.size() - 1], dp[cost.size() - 2]);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
* 时间复杂度:O(n)
|
||||
* 空间复杂度:O(n)
|
||||
|
||||
当然还可以优化空间复杂度,因为dp[i]就是由前两位推出来的,那么也不用dp数组了,C++代码如下:
|
||||
|
||||
```C++
|
||||
// 版本二
|
||||
class Solution {
|
||||
public:
|
||||
int minCostClimbingStairs(vector<int>& cost) {
|
||||
vector<int> dp(cost.size());
|
||||
int dp0 = cost[0];
|
||||
int dp1 = cost[1];
|
||||
for (int i = 2; i < cost.size(); i++) {
|
||||
dp[i] = min(dp0, dp1) + cost[i];
|
||||
dp0 = dp1; // 记录一下前两位
|
||||
dp1 = dp[i];
|
||||
}
|
||||
return min(dp[cost.size() - 1], dp[cost.size() - 2]);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
* 时间复杂度:O(n)
|
||||
* 空间复杂度:O(n)
|
||||
|
||||
当然我不建议这么写,能写出版本一就可以了,直观简洁!
|
||||
|
||||
|
Reference in New Issue
Block a user