Skip to content

Commit

Permalink
Update
Browse files Browse the repository at this point in the history
  • Loading branch information
youngyangyang04 committed Feb 8, 2023
1 parent d107b53 commit 1ec9d9b
Show file tree
Hide file tree
Showing 22 changed files with 146 additions and 148 deletions.
84 changes: 13 additions & 71 deletions problems/0056.合并区间.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,21 +25,15 @@

## 思路

大家应该都感觉到了,此题一定要排序,那么按照左边界排序,还是右边界排序呢?
本题的本质其实还是判断重叠区间问题。

都可以!
大家如果认真做题的话,话发现和我们刚刚讲过的[452. 用最少数量的箭引爆气球](https://programmercarl.com/0452.用最少数量的箭引爆气球.html)[435. 无重叠区间](https://programmercarl.com/0435.无重叠区间.html) 都是一个套路。

那么我按照左边界排序,排序之后局部最优:每次合并都取最大的右边界,这样就可以合并更多的区间了,整体最优:合并所有重叠的区间。
这几道题都是判断区间重叠,区别就是判断区间重叠后的逻辑,本题是判断区间重贴后要进行区间合并。

局部最优可以推出全局最优,找不出反例,试试贪心
所以一样的套路,先排序,让所有的相邻区间尽可能的重叠在一起,按左边界,或者右边界排序都可以,处理逻辑稍有不同

那有同学问了,本来不就应该合并最大右边界么,这和贪心有啥关系?

有时候贪心就是常识!哈哈

按照左边界从小到大排序之后,如果 `intervals[i][0] < intervals[i - 1][1]` 即intervals[i]左边界 < intervals[i - 1]右边界,则一定有重复,因为intervals[i]的左边界一定是大于等于intervals[i - 1]的左边界。

即:intervals[i]的左边界在intervals[i - 1]左边界和右边界的范围内,那么一定有重复!
按照左边界从小到大排序之后,如果 `intervals[i][0] <= intervals[i - 1][1]` 即intervals[i]的左边界 <= intervals[i - 1]的右边界,则一定有重叠。(本题相邻区间也算重贴,所以是<=)

这么说有点抽象,看图:(**注意图中区间都是按照左边界排序之后了**

Expand All @@ -51,83 +45,31 @@

C++代码如下:

```CPP
class Solution {
public:
// 按照区间左边界从小到大排序
static bool cmp (const vector<int>& a, const vector<int>& b) {
return a[0] < b[0];
}
vector<vector<int>> merge(vector<vector<int>>& intervals) {
vector<vector<int>> result;
if (intervals.size() == 0) return result;
sort(intervals.begin(), intervals.end(), cmp);
bool flag = false; // 标记最后一个区间有没有合并
int length = intervals.size();

for (int i = 1; i < length; i++) {
int start = intervals[i - 1][0]; // 初始为i-1区间的左边界
int end = intervals[i - 1][1]; // 初始i-1区间的右边界
while (i < length && intervals[i][0] <= end) { // 合并区间
end = max(end, intervals[i][1]); // 不断更新右区间
if (i == length - 1) flag = true; // 最后一个区间也合并了
i++; // 继续合并下一个区间
}
// start和end是表示intervals[i - 1]的左边界右边界,所以最优intervals[i]区间是否合并了要标记一下
result.push_back({start, end});
}
// 如果最后一个区间没有合并,将其加入result
if (flag == false) {
result.push_back({intervals[length - 1][0], intervals[length - 1][1]});
}
return result;
}
};
```

当然以上代码有冗余一些,可以优化一下,如下:(思路是一样的)

```CPP
class Solution {
public:
vector<vector<int>> merge(vector<vector<int>>& intervals) {
vector<vector<int>> result;
if (intervals.size() == 0) return result;
if (intervals.size() == 0) return result; // 区间集合为空直接返回
// 排序的参数使用了lambda表达式
sort(intervals.begin(), intervals.end(), [](const vector<int>& a, const vector<int>& b){return a[0] < b[0];});

result.push_back(intervals[0]);
// 第一个区间就可以放进结果集里,后面如果重叠,在result上直接合并
result.push_back(intervals[0]);

for (int i = 1; i < intervals.size(); i++) {
if (result.back()[1] >= intervals[i][0]) { // 合并区间
result.back()[1] = max(result.back()[1], intervals[i][1]);
if (result.back()[1] >= intervals[i][0]) { // 发现重叠区间
// 合并区间,只更新右边界就好,因为result.back()的左边界一定是最小值,因为我们按照左边界排序的
result.back()[1] = max(result.back()[1], intervals[i][1]);
} else {
result.push_back(intervals[i]);
result.push_back(intervals[i]); // 区间不重叠
}
}
return result;
}
};
```

* 时间复杂度:O(nlog n) ,有一个快排
* 空间复杂度:O(n),有一个快排,最差情况(倒序)时,需要n次递归调用。因此确实需要O(n)的栈空间


## 总结

对于贪心算法,很多同学都是:**如果能凭常识直接做出来,就会感觉不到自己用了贪心, 一旦第一直觉想不出来, 可能就一直想不出来了**

跟着「代码随想录」刷题的录友应该感受过,贪心难起来,真的难。

那应该怎么办呢?

正如我贪心系列开篇词[关于贪心算法,你该了解这些!](https://programmercarl.com/贪心算法理论基础.html)中讲解的一样,贪心本来就没有套路,也没有框架,所以各种常规解法需要多接触多练习,自然而然才会想到。

「代码随想录」会把贪心常见的经典题目覆盖到,大家只要认真学习打卡就可以了。




## 其他语言版本


Expand Down
2 changes: 1 addition & 1 deletion problems/0134.加油站.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ public:
for (int i = 0; i < cost.size(); i++) {
int rest = gas[i] - cost[i]; // 记录剩余油量
int index = (i + 1) % cost.size();
while (rest > 0 && index != i) { // 模拟以i为起点行驶一圈
while (rest > 0 && index != i) { // 模拟以i为起点行驶一圈(如果有rest==0,那么答案就不唯一了)
rest += gas[index] - cost[index];
index = (index + 1) % cost.size();
}
Expand Down
7 changes: 6 additions & 1 deletion problems/0135.分发糖果.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,15 @@ for (int i = 1; i < ratings.size(); i++) {

遍历顺序这里有同学可能会有疑问,为什么不能从前向后遍历呢?

因为如果从前向后遍历,根据 ratings[i + 1] 来确定 ratings[i] 对应的糖果,那么每次都不能利用上前一次的比较结果了。
因为 rating[5]与rating[4]的比较 要利用上 rating[5]与rating[6]的比较结果,所以 要从后向前遍历。

如果从前向后遍历,rating[5]与rating[4]的比较 就不能用上 rating[5]与rating[6]的比较结果了 。如图:

![](https://code-thinking-1253855093.file.myqcloud.com/pics/20230202102044.png)

**所以确定左孩子大于右孩子的情况一定要从后向前遍历!**


如果 ratings[i] > ratings[i + 1],此时candyVec[i](第i个小孩的糖果数量)就有两个选择了,一个是candyVec[i + 1] + 1(从右边这个加1得到的糖果数量),一个是candyVec[i](之前比较右孩子大于左孩子得到的糖果数量)。

那么又要贪心了,局部最优:取candyVec[i + 1] + 1 和 candyVec[i] 最大的糖果数量,保证第i个小孩的糖果数量既大于左边的也大于右边的。全局最优:相邻的孩子中,评分高的孩子获得更多的糖果。
Expand Down
5 changes: 5 additions & 0 deletions problems/0139.单词拆分.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@
* 输入: s = "catsandog", wordDict = ["cats", "dog", "sand", "and", "cat"]
* 输出: false

# 算法公开课

**《代码随想录》算法视频公开课:[你的背包如何装满?| LeetCode:139.单词拆分](https://www.bilibili.com/video/BV1pd4y147Rh/),相信结合视频再看本篇题解,更有助于大家对本题的理解**


## 思路

看到这道题目的时候,大家应该回想起我们之前讲解回溯法专题的时候,讲过的一道题目[回溯算法:分割回文串](https://programmercarl.com/0131.分割回文串.html),就是枚举字符串的所有分割情况。
Expand Down
17 changes: 11 additions & 6 deletions problems/0279.完全平方数.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,23 @@
完全平方数 是一个整数,其值等于另一个整数的平方;换句话说,其值等于一个整数自乘的积。例如,1、4、9 和 16 都是完全平方数,而 3 和 11 不是。

示例 1:
输入:n = 12
输出:3
解释:12 = 4 + 4 + 4
* 输入:n = 12
* 输出:3
* 解释:12 = 4 + 4 + 4

示例 2:
输入:n = 13
输出:2
解释:13 = 4 + 9
* 输入:n = 13
* 输出:2
* 解释:13 = 4 + 9

提示:
* 1 <= n <= 10^4

# 算法公开课

**《代码随想录》算法视频公开课:[换汤不换药!| LeetCode:279.完全平方数](https://www.bilibili.com/video/BV12P411T7Br/),相信结合视频再看本篇题解,更有助于大家对本题的理解**


## 思路

可能刚看这种题感觉没啥思路,又平方和的,又最小数的。
Expand Down
28 changes: 17 additions & 11 deletions problems/0322.零钱兑换.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,32 +13,38 @@
你可以认为每种硬币的数量是无限的。

示例 1:
输入:coins = [1, 2, 5], amount = 11
输出:3
解释:11 = 5 + 5 + 1
* 输入:coins = [1, 2, 5], amount = 11
* 输出:3
* 解释:11 = 5 + 5 + 1

示例 2:
输入:coins = [2], amount = 3
输出:-1
* 输入:coins = [2], amount = 3
* 输出:-1

示例 3:
输入:coins = [1], amount = 0
输出:0
* 输入:coins = [1], amount = 0
* 输出:0

示例 4:
输入:coins = [1], amount = 1
输出:1
* 输入:coins = [1], amount = 1
* 输出:1

示例 5:
输入:coins = [1], amount = 2
输出:2
* 输入:coins = [1], amount = 2
* 输出:2

提示:

* 1 <= coins.length <= 12
* 1 <= coins[i] <= 2^31 - 1
* 0 <= amount <= 10^4

# 算法公开课

**《代码随想录》算法视频公开课:[装满背包最少的物品件数是多少?| LeetCode:322.零钱兑换](https://www.bilibili.com/video/BV14K411R7yv/),相信结合视频再看本篇题解,更有助于大家对本题的理解**



## 思路

[动态规划:518.零钱兑换II](https://programmercarl.com/0518.零钱兑换II.html)中我们已经兑换一次零钱了,这次又要兑换,套路不一样!
Expand Down
3 changes: 2 additions & 1 deletion problems/0337.打家劫舍III.md
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,8 @@ return {val2, val1};
以示例1为例,dp数组状态如下:(**注意用后序遍历的方式推导**)
![337.打家劫舍III](https://code-thinking.cdn.bcebos.com/pics/337.打家劫舍III.jpg)
![](https://code-thinking-1253855093.file.myqcloud.com/pics/20230203110031.png)
**最后头结点就是 取下标0 和 下标1的最大值就是偷得的最大金钱**。
Expand Down
8 changes: 6 additions & 2 deletions problems/0377.组合总和Ⅳ.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@

示例:

nums = [1, 2, 3]
target = 4
* nums = [1, 2, 3]
* target = 4

所有可能的组合为:
(1, 1, 1, 1)
Expand All @@ -31,6 +31,10 @@ target = 4

因此输出为 7。

# 算法公开课

**《代码随想录》算法视频公开课:[装满背包有几种方法?求排列数?| LeetCode:377.组合总和IV](https://www.bilibili.com/video/BV1V14y1n7B6/),相信结合视频再看本篇题解,更有助于大家对本题的理解**

## 思路

对完全背包还不了解的同学,可以看这篇:[动态规划:关于完全背包,你该了解这些!](https://programmercarl.com/背包问题理论基础完全背包.html)
Expand Down
17 changes: 11 additions & 6 deletions problems/0416.分割等和子集.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,24 @@
数组的大小不会超过 200

示例 1:
输入: [1, 5, 11, 5]
输出: true
解释: 数组可以分割成 [1, 5, 5][11].
* 输入: [1, 5, 11, 5]
* 输出: true
* 解释: 数组可以分割成 [1, 5, 5][11].

示例 2:
输入: [1, 2, 3, 5]
输出: false
解释: 数组不能分割成两个元素和相等的子集.
* 输入: [1, 2, 3, 5]
* 输出: false
* 解释: 数组不能分割成两个元素和相等的子集.

提示:
* 1 <= nums.length <= 200
* 1 <= nums[i] <= 100

# 算法公开课

**《代码随想录》算法视频公开课:[动态规划之背包问题,这个包能装满吗?| LeetCode:416.分割等和子集](https://www.bilibili.com/video/BV1rt4y1N7jE/),相信结合视频再看本篇题解,更有助于大家对本题的理解**


## 思路

这道题目初步看,和如下两题几乎是一样的,大家可以用回溯法,解决如下两题
Expand Down
3 changes: 2 additions & 1 deletion problems/0455.分发饼干.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@

如图:

![455.分发饼干](https://img-blog.csdnimg.cn/20201123161809624.png)
![](https://code-thinking-1253855093.file.myqcloud.com/pics/20230203105634.png)


这个例子可以看出饼干9只有喂给胃口为7的小孩,这样才是整体最优解,并想不出反例,那么就可以撸代码了。

Expand Down
17 changes: 11 additions & 6 deletions problems/0474.一和零.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,16 @@

示例 1:

输入:strs = ["10", "0001", "111001", "1", "0"], m = 5, n = 3
输出:4
* 输入:strs = ["10", "0001", "111001", "1", "0"], m = 5, n = 3
* 输出:4

解释:最多有 5 个 0 和 3 个 1 的最大子集是 {"10","0001","1","0"} ,因此答案是 4 。
* 解释:最多有 5 个 0 和 3 个 1 的最大子集是 {"10","0001","1","0"} ,因此答案是 4 。
其他满足题意但较小的子集包括 {"0001","1"} 和 {"10","1","0"} 。{"111001"} 不满足题意,因为它含 4 个 1 ,大于 n 的值 3 。

示例 2:
输入:strs = ["10", "0", "1"], m = 1, n = 1
输出:2
解释:最大的子集是 {"0", "1"} ,所以答案是 2 。
* 输入:strs = ["10", "0", "1"], m = 1, n = 1
* 输出:2
* 解释:最大的子集是 {"0", "1"} ,所以答案是 2 。

提示:

Expand All @@ -34,6 +34,11 @@
* strs[i] 仅由 '0' 和 '1' 组成
* 1 <= m, n <= 100

# 算法公开课

**《代码随想录》算法视频公开课:[装满这个背包最多用多少个物品?| LeetCode:474.一和零](https://www.bilibili.com/video/BV1rW4y1x7ZQ/),相信结合视频再看本篇题解,更有助于大家对本题的理解**


## 思路

如果对背包问题不都熟悉先看这两篇:
Expand Down
21 changes: 13 additions & 8 deletions problems/0494.目标和.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@



# 494. 目标和
# 494.目标和

[力扣题目链接](https://leetcode.cn/problems/target-sum/)

Expand All @@ -19,15 +19,15 @@

示例:

输入:nums: [1, 1, 1, 1, 1], S: 3
输出:5
* 输入:nums: [1, 1, 1, 1, 1], S: 3
* 输出:5

解释:
-1+1+1+1+1 = 3
+1-1+1+1+1 = 3
+1+1-1+1+1 = 3
+1+1+1-1+1 = 3
+1+1+1+1-1 = 3
* -1+1+1+1+1 = 3
* +1-1+1+1+1 = 3
* +1+1-1+1+1 = 3
* +1+1+1-1+1 = 3
* +1+1+1+1-1 = 3

一共有5种方法让最终目标和为3。

Expand All @@ -37,6 +37,11 @@
* 初始的数组的和不会超过 1000 。
* 保证返回的最终结果能被 32 位整数存下。

# 算法公开课

**《代码随想录》算法视频公开课:[装满背包有多少种方法?| LeetCode:494.目标和](https://www.bilibili.com/video/BV1o8411j73x/),相信结合视频再看本篇题解,更有助于大家对本题的理解**


## 思路

如果对背包问题不都熟悉先看这两篇:
Expand Down
Loading

0 comments on commit 1ec9d9b

Please sign in to comment.