mirror of
https://github.com/youngyangyang04/leetcode-master.git
synced 2025-07-07 15:45:40 +08:00
Update
This commit is contained in:
@ -55,6 +55,7 @@
|
||||
* [关于时间复杂度,你不知道的都在这里!](https://mp.weixin.qq.com/s/LWBfehW1gMuEnXtQjJo-sw)
|
||||
* [O(n)的算法居然超时了,此时的n究竟是多大?](https://mp.weixin.qq.com/s/73ryNsuPFvBQkt6BbhNzLA)
|
||||
* [通过一道面试题目,讲一讲递归算法的时间复杂度!](https://mp.weixin.qq.com/s/I6ZXFbw09NR31F5CJR_geQ)
|
||||
* [本周小结!(算法性能分析系列一)](https://mp.weixin.qq.com/s/5m8xDbGUeGgYJsESeg5ITQ)
|
||||
|
||||
* 数组
|
||||
* [必须掌握的数组理论知识](https://mp.weixin.qq.com/s/X7R55wSENyY62le0Fiawsg)
|
||||
@ -327,6 +328,7 @@
|
||||
|[0454.四数相加II](https://github.com/youngyangyang04/leetcode/blob/master/problems/0454.四数相加II.md) |哈希表 |中等| **哈希**|
|
||||
|[0455.分发饼干](https://github.com/youngyangyang04/leetcode/blob/master/problems/0455.分发饼干.md) |贪心 |简单| **贪心**|
|
||||
|[0459.重复的子字符串](https://github.com/youngyangyang04/leetcode/blob/master/problems/0459.重复的子字符串.md) |字符创 |简单| **KMP**|
|
||||
|[0473.火柴拼正方形](https://github.com/youngyangyang04/leetcode/blob/master/problems/0473.火柴拼正方形.md) |深度优先搜索|中等| **回溯算法** 和698.划分为k个相等的子集差不多|
|
||||
|[0474.一和零](https://github.com/youngyangyang04/leetcode/blob/master/problems/0474.一和零.md) |动态规划 |中等| **多重背包** 好题目|
|
||||
|[0486.预测赢家](https://github.com/youngyangyang04/leetcode/blob/master/problems/0486.预测赢家.md) |动态规划 |中等| **递归** **记忆递归** **动态规划**|
|
||||
|[0491.递增子序列](https://github.com/youngyangyang04/leetcode/blob/master/problems/0491.递增子序列.md) |深度优先搜索 |中等|**深度优先搜索/回溯算法** 这个去重有意思|
|
||||
@ -345,9 +347,11 @@
|
||||
|[0590.N叉树的后序遍历](https://github.com/youngyangyang04/leetcode/blob/master/problems/0590.N叉树的后序遍历.md) |N叉树 |简单|**递归** **栈/迭代**|
|
||||
|[0617.合并二叉树](https://github.com/youngyangyang04/leetcode/blob/master/problems/0617.合并二叉树.md) |树 |简单|**递归** **迭代**|
|
||||
|[0637.二叉树的层平均值](https://github.com/youngyangyang04/leetcode/blob/master/problems/0637.二叉树的层平均值.md) |树 |简单|**广度优先搜索/队列**|
|
||||
|[0649.Dota2参议院](https://github.com/youngyangyang04/leetcode/blob/master/problems/0649.Dota2参议院.md) |贪心 |简单|**贪心算法** 简单的贪心策略但代码实现很有技巧|
|
||||
|[0654.最大二叉树](https://github.com/youngyangyang04/leetcode/blob/master/problems/0654.最大二叉树.md) |树 |中等|**递归**|
|
||||
|[0685.冗余连接II](https://github.com/youngyangyang04/leetcode/blob/master/problems/0685.冗余连接II.md) | 并查集/树/图 |困难|**并查集**|
|
||||
|[0669.修剪二叉搜索树](https://github.com/youngyangyang04/leetcode/blob/master/problems/0669.修剪二叉搜索树.md) | 二叉搜索树/二叉树 |简单|**递归** **迭代**|
|
||||
|[0698.划分为k个相等的子集](https://github.com/youngyangyang04/leetcode/blob/master/problems/0698.划分为k个相等的子集.md) |回溯算法|中等|动态规划 **回溯算法** 这其实是组合问题,使用了两次递归,好题|
|
||||
|[0700.二叉搜索树中的搜索](https://github.com/youngyangyang04/leetcode/blob/master/problems/0700.二叉搜索树中的搜索.md) |二叉搜索树 |简单|**递归** **迭代**|
|
||||
|[0701.二叉搜索树中的插入操作](https://github.com/youngyangyang04/leetcode/blob/master/problems/0701.二叉搜索树中的插入操作.md) |二叉搜索树 |简单|**递归** **迭代**|
|
||||
|[0705.设计哈希集合](https://github.com/youngyangyang04/leetcode/blob/master/problems/0705.设计哈希集合.md) |哈希表 |简单|**模拟**|
|
||||
|
@ -1,6 +1,11 @@
|
||||
|
||||
和Leetcode 473:火柴拼正方形和Leetcode 698:划分为k个相等的子集是
|
||||
|
||||
* 473. 火柴拼正方形 (回溯算法)
|
||||
* 698. 划分为k个相等的子集
|
||||
|
||||
一起再回忆一下回溯算法
|
||||
|
||||
|[0473.火柴拼正方形](https://github.com/youngyangyang04/leetcode/blob/master/problems/0473.火柴拼正方形.md) |深度优先搜索|中等| **回溯算法** 和698.划分为k个相等的子集差不多|
|
||||
|
||||
## 思路
|
||||
|
||||
@ -82,6 +87,8 @@ public:
|
||||
|
||||
本来是想用回溯暴力搜索出所有答案的,各种剪枝,还是超时了,不想在调了,放弃回溯,直接上01背包吧。
|
||||
|
||||
**需要尝试一下记忆化递归**
|
||||
|
||||
回溯搜索超时的代码如下:
|
||||
|
||||
```
|
||||
@ -119,3 +126,45 @@ public:
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
```
|
||||
class Solution {
|
||||
private:
|
||||
bool backtracking(vector<int>& nums,
|
||||
int k,
|
||||
int target, // 子集目标和
|
||||
int cur, // 当前目标和
|
||||
int startIndex, // 起始位置
|
||||
vector<bool>& used) { // 标记是否使用过
|
||||
if (k == 0) return true; // 找到了k个相同子集
|
||||
if (cur == target) { // 发现一个合格子集,然后重新开始寻找
|
||||
return backtracking(nums, k - 1, target, 0, 0, used); // k-1
|
||||
}
|
||||
for (int i = startIndex; i < nums.size(); i++) {
|
||||
if (cur + nums[i] <= target && !used[i]) {
|
||||
used[i] = true;
|
||||
if (backtracking(nums, k, target, cur + nums[i], i + 1, used)) {
|
||||
return true;
|
||||
}
|
||||
used[i] = false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public:
|
||||
bool canPartition(vector<int>& nums) {
|
||||
if (nums.size() < 2) return false; // 火柴数量小于4凑不上正方形
|
||||
int sum = 0;
|
||||
for (int i = 0; i < nums.size(); i++) {
|
||||
sum += nums[i];
|
||||
}
|
||||
if (sum % 2 != 0) return false;
|
||||
int target = sum / 2;
|
||||
vector<bool> used(nums.size(), false);
|
||||
|
||||
return backtracking(nums, 2, target, 0, 0, used);
|
||||
|
||||
}
|
||||
};
|
||||
```
|
||||
|
42
problems/0473.火柴拼正方形.md
Normal file
42
problems/0473.火柴拼正方形.md
Normal file
@ -0,0 +1,42 @@
|
||||
|
||||
698.划分为k个相等的子集 的代码几乎不用改动,就可以AC
|
||||
```
|
||||
class Solution {
|
||||
private:
|
||||
bool backtracking(vector<int>& nums,
|
||||
int k,
|
||||
int target, // 子集目标和
|
||||
int cur, // 当前目标和
|
||||
int startIndex, // 起始位置
|
||||
vector<bool>& used) { // 标记是否使用过
|
||||
if (k == 0) return true; // 找到了k个相同子集
|
||||
if (cur == target) { // 发现一个合格子集,然后重新开始寻找
|
||||
return backtracking(nums, k - 1, target, 0, 0, used); // k-1
|
||||
}
|
||||
for (int i = startIndex; i < nums.size(); i++) {
|
||||
if (cur + nums[i] <= target && !used[i]) {
|
||||
used[i] = true;
|
||||
if (backtracking(nums, k, target, cur + nums[i], i + 1, used)) {
|
||||
return true;
|
||||
}
|
||||
used[i] = false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
public:
|
||||
bool makesquare(vector<int>& nums) {
|
||||
if (nums.size() < 4) return false; // 火柴数量小于4凑不上正方形
|
||||
int sum = 0;
|
||||
for (int i = 0; i < nums.size(); i++) {
|
||||
sum += nums[i];
|
||||
}
|
||||
if (sum % 4 != 0) return false;
|
||||
int target = sum / 4;
|
||||
vector<bool> used(nums.size(), false);
|
||||
|
||||
return backtracking(nums, 4, target, 0, 0, used);
|
||||
|
||||
}
|
||||
};
|
||||
```
|
70
problems/0649.Dota2参议院.md
Normal file
70
problems/0649.Dota2参议院.md
Normal file
@ -0,0 +1,70 @@
|
||||
|
||||
## 思路
|
||||
|
||||
这道题 题意太绕了,我举一个更形象的例子给大家捋顺一下。
|
||||
|
||||
例如输入"RRDDD",执行过程应该是什么样呢?
|
||||
|
||||
* 第一轮:senate[0]的R消灭senate[2]的D,senate[1]的R消灭senate[3]的D,senate[4]的D消灭senate[0]的R,此时剩下"RD",第一轮结束!
|
||||
* 第二轮:senate[0]的R消灭senate[1]的D,第二轮结束
|
||||
* 第三轮:只有R了,R胜利
|
||||
|
||||
估计不少同学都困惑,R和D数量相同怎么办,究竟谁赢,**其实这是一个持续消灭的过程!** 即:如果同时存在R和D就继续进行下一轮消灭,轮数直到只剩下R或者D为止!
|
||||
|
||||
那么每一轮消灭的策略应该是什么呢?
|
||||
|
||||
例如:RDDRD
|
||||
|
||||
第一轮:senate[0]的R消灭senate[1]的D,那么senate[2]的D,是消灭senate[0]的R还是消灭senate[3]的R呢?
|
||||
|
||||
当然是消灭senate[3]的R,因为当轮到这个R的时候,它可以消灭senate[4]的D。
|
||||
|
||||
**所以消灭的策略是,尽量消灭自己后面的对手,因为前面的对手已经使用过权利了,而后序的对手依然可以使用权利消灭自己的同伴!**
|
||||
|
||||
那么局部最优:有一次权利机会,就消灭自己后面的对手。全局最优:为自己的阵营赢取最大利益。
|
||||
|
||||
局部最优可以退出全局最优,举不出反例,那么试试贪心。
|
||||
|
||||
如果对贪心算法理论基础还不了解的话,可以看看这篇:[关于贪心算法,你该了解这些!](https://mp.weixin.qq.com/s/O935TaoHE9Eexwe_vSbRAg) ,相信看完之后对贪心就有基本的了解了。
|
||||
|
||||
## 代码实现
|
||||
|
||||
实现代码,在每一轮循环的过程中,去过模拟优先消灭身后的对手,其实是比较麻烦的。
|
||||
|
||||
这里有一个技巧,就是用一个变量记录当前参议员之前有几个敌对对手了,进而判断自己是否被消灭了。这个变量我用flag来表示。
|
||||
|
||||
C++代码如下:
|
||||
|
||||
|
||||
```
|
||||
class Solution {
|
||||
public:
|
||||
string predictPartyVictory(string senate) {
|
||||
// R = true表示本轮循环结束后,字符串里依然有R。D同理
|
||||
bool R = true, D = true;
|
||||
// 当flag大于0时,R在D前出现,R可以消灭D。当flag小于0时,D在R前出现,D可以消灭R
|
||||
int flag = 0;
|
||||
while (R && D) { // 一旦R或者D为false,就结束循环,说明本轮结束后只剩下R或者D了
|
||||
R = false;
|
||||
D = false;
|
||||
for (int i = 0; i < senate.size(); i++) {
|
||||
if (senate[i] == 'R') {
|
||||
if (flag < 0) senate[i] = 0; // 消灭R,R此时为false
|
||||
else R = true; // 如果没被消灭,本轮循环结束有R
|
||||
flag++;
|
||||
}
|
||||
if (senate[i] == 'D') {
|
||||
if (flag > 0) senate[i] = 0;
|
||||
else D = true;
|
||||
flag--;
|
||||
}
|
||||
}
|
||||
}
|
||||
// 循环结束之后,R和D只能有一个为true
|
||||
return R == true ? "Radiant" : "Dire";
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
**如果感觉题解对你有帮助,不要吝啬给一个👍吧!**
|
||||
|
42
problems/0698.划分为k个相等的子集.md
Normal file
42
problems/0698.划分为k个相等的子集.md
Normal file
@ -0,0 +1,42 @@
|
||||
|
||||
使用回溯法,首先要明确这是组合问题,即集合里是不强调顺序的,所以要用startIndex
|
||||
|
||||
```
|
||||
class Solution {
|
||||
private:
|
||||
bool backtracking(vector<int>& nums,
|
||||
int k,
|
||||
int target, // 子集目标和
|
||||
int cur, // 当前目标和
|
||||
int startIndex, // 起始位置
|
||||
vector<bool>& used) { // 标记是否使用过
|
||||
if (k == 0) return true; // 找到了k个相同子集
|
||||
if (cur == target) { // 发现一个合格子集,然后重新开始寻找
|
||||
return backtracking(nums, k - 1, target, 0, 0, used); // k-1
|
||||
}
|
||||
for (int i = startIndex; i < nums.size(); i++) {
|
||||
if (cur + nums[i] <= target && !used[i]) {
|
||||
used[i] = true;
|
||||
if (backtracking(nums, k, target, cur + nums[i], i + 1, used)) {
|
||||
return true;
|
||||
}
|
||||
used[i] = false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
public:
|
||||
bool canPartitionKSubsets(vector<int>& nums, int k) {
|
||||
//sort(nums.begin(), nums.end()); 不需要排序
|
||||
int sum = 0;
|
||||
for (int i = 0; i < nums.size(); i++) {
|
||||
sum += nums[i];
|
||||
}
|
||||
if (sum % k != 0) return false;
|
||||
int target = sum / k;
|
||||
vector<bool> used(nums.size(), false);
|
||||
|
||||
return backtracking(nums, k, target, 0, 0, used);
|
||||
}
|
||||
};
|
||||
```
|
Reference in New Issue
Block a user