Skip to content

Commit 1e0c5e6

Browse files
Update
1 parent aca2679 commit 1e0c5e6

8 files changed

+321
-71
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,8 @@
297297
|[0349.两个数组的交集](https://github.com/youngyangyang04/leetcode/blob/master/problems/0349.两个数组的交集.md) |哈希表 |简单|**哈希**|
298298
|[0350.两个数组的交集II](https://github.com/youngyangyang04/leetcode/blob/master/problems/0350.两个数组的交集II.md) |哈希表 |简单|**哈希**|
299299
|[0383.赎金信](https://github.com/youngyangyang04/leetcode/blob/master/problems/0383.赎金信.md) |数组 |简单|**暴力** **字典计数** **哈希**|
300+
|[0404.左叶子之和](https://github.com/youngyangyang04/leetcode/blob/master/problems/0404.左叶子之和.md) |树/二叉树 |简单|**递归** **迭代**|
301+
|[0416.分割等和子集](https://github.com/youngyangyang04/leetcode/blob/master/problems/0416.分割等和子集.md) |动态规划 |中等|**背包问题/01背包**|
300302
|[0429.N叉树的层序遍历](https://github.com/youngyangyang04/leetcode/blob/master/problems/0429.N叉树的层序遍历.md) ||简单|**队列/广度优先搜索**|
301303
|[0434.字符串中的单词数](https://github.com/youngyangyang04/leetcode/blob/master/problems/0434.字符串中的单词数.md) |字符串 |简单|**模拟**|
302304
|[0450.删除二叉搜索树中的节点](https://github.com/youngyangyang04/leetcode/blob/master/problems/0450.删除二叉搜索树中的节点.md) ||中等|**递归**|

pics/416.分割等和子集.png

32.5 KB
Loading

pics/416.分割等和子集1.png

137 KB
Loading
122 KB
Loading

pics/617.合并二叉树.png

30.9 KB
Loading

problems/0416.分割等和子集.md

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
2+
和Leetcode 473:火柴拼正方形和Leetcode 698:划分为k个相等的子集是
3+
4+
5+
## 思路
6+
7+
这道题目是要找是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。
8+
9+
那么只要找到集合里能够出现 sum / 2 的集合,就算是可以分割成两个相同元素和子集了。
10+
11+
本来是我是想用回溯暴力搜索出所有答案的,各种剪枝,还是超时了,不想在调了,放弃回溯,直接上01背包吧。
12+
13+
如下的讲解中,我讲的重点是如何把01背包应用到此题,而不是讲01背包,如果对01背包本身还不理解的同学,需要额外学习一下基础知识,我后面也会在[代码随想录](https://img-blog.csdnimg.cn/20200815195519696.png)里深度讲解背包问题。
14+
15+
### 背包问题
16+
17+
背包问题,大家都知道,就是书包,书包可以容纳的体积n, 然后有各种商品,每一种商品体积为m,价值为z,问如果把书包塞满(不一定能塞满),书包里的商品最大价值总和是多少。
18+
19+
**背包问题有多种背包方式,常见的有:01背包、完全背包、多重背包、分组背包和混合背包等等。**
20+
21+
要注意背包问题问题中商品是不是可以重复放入。
22+
23+
**即一个商品如果可以重复多次放入是完全背包,而只能放入一次是01背包,写法还是不一样的。**
24+
25+
**要明确本题中我们要使用的是01背包,因为元素我们只能用一次。**
26+
27+
为了让大家对背包问题有一个整体的了解,可以看如下图:
28+
29+
<img src='../pics/416.分割等和子集1.png' width=600> </img></div>
30+
31+
回归主题:首先,本题要求集合里能否出现总和为 sum / 2 的子集。
32+
33+
那么来一一对应一下本题,看看背包问题如果来解决。
34+
35+
**只有确定了如下四点,才能把背包问题,套到本题上来。**
36+
37+
* 背包的体积为sum / 2
38+
* 背包要放入的商品(集合里的元素)体积为 元素的数值,价值也为元素的数值
39+
* 背包如何正好装满,说明找到了总和为 sum / 2 的子集。
40+
* 背包中每一个元素一定是不可重复放入。
41+
42+
43+
定义里数组为dp[],dp[i] 表示 背包中放入体积为i的商品,最大价值为dp[i]
44+
45+
套到本题,dp[i]表示 背包中总和是i,最大可以凑成总和为i的元素总和为dp[i]
46+
47+
dp[i]一定是小于等于i的,因为背包不能装入超过自身体积的商品(这里理解为元素数值)。
48+
49+
**如果dp[i] == i 说明,集合中的元素正好可以凑成总和i,理解这一点很重要。**
50+
51+
## C++代码如下(详细注释 )
52+
```
53+
class Solution {
54+
public:
55+
bool canPartition(vector<int>& nums) {
56+
int sum = 0;
57+
58+
// dp[i]中的i表示背包内总和
59+
// 题目中说:每个数组中的元素不会超过 100,数组的大小不会超过 200
60+
// 那么背包内总和不会大于20000,所以定义一个20000大的数组。
61+
vector<int> dp(20001, 0);
62+
for (int i = 0; i < nums.size(); i++) {
63+
sum += nums[i];
64+
}
65+
if (sum % 2 == 1) return false;
66+
int target = sum / 2;
67+
68+
// 开始 01背包
69+
for(int i = 0; i < nums.size(); i++) {
70+
for(int j = target; j >= nums[i]; j--) { // 每一个元素一定是不可重复放入,所以从大到小遍历
71+
dp[j] = max(dp[j], dp[j - nums[i]] + nums[i]);
72+
}
73+
}
74+
// 集合中的元素正好可以凑成总和target
75+
if (dp[target] == target) return true;
76+
return false;
77+
}
78+
};
79+
```
80+
81+
### 暴力
82+
83+
本来是想用回溯暴力搜索出所有答案的,各种剪枝,还是超时了,不想在调了,放弃回溯,直接上01背包吧。
84+
85+
回溯搜索超时的代码如下:
86+
87+
```
88+
class Solution {
89+
private:
90+
int target;
91+
bool backtracking(vector<int>& nums, int startIndex, int pathSum, vector<bool>& used) {
92+
for (int i = startIndex; i < nums.size(); i++) {
93+
if (pathSum > target) break; // 剪枝
94+
if (target < nums[i]) break; // 剪枝
95+
if (i > 0 && nums[i] == nums[i - 1] && used[i - 1] == false) { // 去重
96+
continue;
97+
}
98+
pathSum += nums[i];
99+
used[i] = true;
100+
if (pathSum == target) return true;
101+
if (backtracking(nums, i + 1, pathSum, used)) return true;
102+
used[i] = false;
103+
pathSum -= nums[i];
104+
}
105+
return false;
106+
}
107+
108+
public:
109+
bool canPartition(vector<int>& nums) {
110+
vector<bool> used(nums.size(), false);
111+
sort(nums.begin(), nums.end());
112+
int sum = 0;
113+
for (int i = 0; i < nums.size(); i++) sum += nums[i];
114+
if (sum % 2 == 1) return false;
115+
target = sum / 2;
116+
cout << "sum:" << sum << " target:" << target << endl;
117+
return backtracking(nums, 0, 0, used);
118+
}
119+
};
120+
121+
```
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
2+
## 题目地址
3+
4+
https://leetcode-cn.com/problems/minimum-absolute-difference-in-bst/
5+
6+
## 思路
7+
8+
题目中要求在二叉搜索树上任意两节点的差的绝对值的最小值。
9+
10+
**注意是二叉搜索树,**二叉搜索树可是有序的。
11+
12+
遇到在二叉搜索树上求什么最值啊,差值之类的,就把它想成在一个有序数组上求最值,求差值,这样就简单多了。
13+
14+
### 递归
15+
16+
那么二叉搜索树如果采用中序遍历,其实就是一个有序数组。
17+
18+
**在一个有序数组上求两个数最小差值,这是不是就是一道送分题了。**
19+
20+
最直观的想法,就是把二叉搜索树转换成有序数组,然后遍历一遍数组,就统计出来最小差值了
21+
22+
代码如下:
23+
24+
```
25+
class Solution {
26+
private:
27+
vector<int> vec;
28+
void traversal(TreeNode* root) {
29+
if (root == NULL) return;
30+
traversal(root->left);
31+
vec.push_back(root->val); // 将二叉搜索树转换为有序数组
32+
traversal(root->right);
33+
}
34+
public:
35+
int getMinimumDifference(TreeNode* root) {
36+
vec.clear();
37+
traversal(root);
38+
if (vec.size() < 2) return 0;
39+
int result = INT_MAX;
40+
for (int i = 1; i < vec.size(); i++) { // 统计有序数组的最小差值
41+
result = min(result, vec[i] - vec[i-1]);
42+
}
43+
return result;
44+
}
45+
};
46+
```
47+
48+
以上代码是把二叉搜索树转化为有序数组了,其实在二叉搜素树中序遍历的过程中,我们就可以直接计算了。
49+
50+
需要用一个pre节点记录一下,当前节点的前一个节点。
51+
52+
如图:
53+
54+
<img src='../pics/530.二叉搜索树的最小绝对差.png' width=600> </img></div>
55+
56+
代码如下:
57+
58+
```
59+
class Solution {
60+
private:
61+
int result = INT_MAX;
62+
TreeNode* pre;
63+
void traversal(TreeNode* cur) {
64+
if (cur == NULL) return;
65+
traversal(cur->left);
66+
if (pre != NULL){
67+
result = min(result, cur->val - pre->val);
68+
}
69+
pre = cur; // 记录前一个
70+
traversal(cur->right);
71+
}
72+
public:
73+
int getMinimumDifference(TreeNode* root) {
74+
traversal(root);
75+
return result;
76+
}
77+
};
78+
```
79+
80+
### 迭代
81+
82+
看过这两篇[二叉树:听说递归能做的,栈也能做!](https://mp.weixin.qq.com/s/c_zCrGHIVlBjUH_hJtghCg)[二叉树:前中后序迭代方式的写法就不能统一一下么?](https://mp.weixin.qq.com/s/WKg0Ty1_3SZkztpHubZPRg)文章之后,不难写出两种中序遍历的迭代法。
83+
84+
下面我给出其中的一种,代码如下:
85+
86+
```
87+
class Solution {
88+
public:
89+
int getMinimumDifference(TreeNode* root) {
90+
stack<TreeNode*> st;
91+
TreeNode* cur = root;
92+
TreeNode* pre = NULL;
93+
int result = INT_MAX;
94+
while (cur != NULL || !st.empty()) {
95+
if (cur != NULL) { // 指针来访问节点,访问到最底层
96+
st.push(cur); // 将访问的节点放进栈
97+
cur = cur->left; // 左
98+
} else {
99+
cur = st.top();
100+
st.pop();
101+
if (pre != NULL) {
102+
result = min(result, cur->val - pre->val); // 中
103+
}
104+
pre = cur;
105+
cur = cur->right; // 右
106+
}
107+
}
108+
return result;
109+
}
110+
};
111+
```

0 commit comments

Comments
 (0)