贪心的本质是选择每一阶段的局部最优,从而达到全局最优。
文章目录
- [455. 分发饼干](https://leetcode-cn.com/problems/assign-cookies/)
- [376. 摆动序列](https://leetcode-cn.com/problems/wiggle-subsequence/)
- [53. 最大子数组和](https://leetcode-cn.com/problems/maximum-subarray/)
- [406. 根据身高重建队列](https://leetcode-cn.com/problems/queue-reconstruction-by-height/)
- [452. 用最少数量的箭引爆气球](https://leetcode-cn.com/problems/minimum-number-of-arrows-to-burst-balloons/)
- [435. 无重叠区间](https://leetcode-cn.com/problems/non-overlapping-intervals/submissions/)
- [763. 划分字母区间](https://leetcode-cn.com/problems/partition-labels/)
- [56. 合并区间](https://leetcode-cn.com/problems/merge-intervals/submissions/)
- [738. 单调递增的数字](https://leetcode-cn.com/problems/monotone-increasing-digits/)
- [714. 买卖股票的最佳时机含手续费](https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-with-transaction-fee/)
- [968. 监控二叉树](https://leetcode-cn.com/problems/binary-tree-cameras/)
455. 分发饼干
class Solution {
public:
int findContentChildren(vector<int>& g, vector<int>& s) {
// 从小到大的排序,一一对应
// wrong:sort(g);
// wrong:sort(s);
sort(g.begin(), g.end());
sort(s.begin(), s.end());
int index = 0;
for (int i = 0; i < s.size(); i++) {
# wrong: if (g[index] <= s[i]) {
if (index < g.size() && g[index] <= s[i]) {
index++;
}
}
return index;
}
};
小结
- 忘了sort怎么用的了,以及手写排序
- 下标越界了
376. 摆动序列
class Solution {
public:
int wiggleMaxLength(vector<int>& nums) {
int cur = 0;
int pre = 0;
int result = 1;
for (int i = 0; i < nums.size() - 1; i++) {
cur = nums[i + 1] - nums[i];
// wrong: if ((nums[i + 1] - nums[i]) * (nums[i] - nums[index]) < 0)
if ((pre <= 0 && cur > 0) || (pre >= 0 && cur < 0)) {
result++;
pre = cur;
}
}
return result;
}
};
小结
- 必然存在一个波峰
- 然后只要前一个小于等于0,或前一个大于等于0,当前相反
- 我错在没考虑等于0的情况
53. 最大子数组和
class Solution {
public:
int maxSubArray(vector<int>& nums) {
// wrong:int result = INT_MIN32;
int result = INT32_MIN;
int count = 0;
for (int i = 0; i < nums.size(); i++) {
count += nums[i];
if (result < count) {
result = count;
}
if (count <= 0) {
count = 0;
}
}
return result;
}
};
小结
- 在思考都是负数的情况时,没有想明白
406. 根据身高重建队列
class Solution {
public:
static bool cmp(const vector<int>& a, const vector<int>& b) {
// 0维从大到小,1维从小到大
if (a[0] == b[0]) return a[1] < b[1];
return a[0] > b[0];
}
vector<vector<int>> reconstructQueue(vector<vector<int>>& people) {
// 先按照身高从大到小的顺序排列
// 问题1.排序用什么排
// 问题2.(->)和(.)的使用差异
// sort 默认升序
// 可以自定义cmp、也可以用标准库
// 二维的自定义cmp
// wrong
// sort(people.begin(), people.end(), greater<int>());
sort(people.begin(), people.end(), cmp);
// 然后相同身高,按k从小到大
// wrong
// for (auto p : people) {
// sort(p.begin(), p.end(), less<int>());
// }
// 指定位置插入
// list 和 vector 插入时有点区别
// list 效率高于 vector
// 最后重新插入,按照k的值,插入到指定下标
// vector<vector<int>> que;
// for (int i = 0; i < people.size(); i++) {
// int position = people[i][1];
// // 指定位置插入值
// que.insert(que.begin() + position, people[i]);
// }
// return que;
list<vector<int>> que;
for (int i = 0; i < people.size(); i++) {
int position = people[i][1]; // 插入到下标为position的位置
// std::list<vector<int>>::iterator it = que.begin();
auto it = que.begin();
while (position--) { // 寻找在插入位置
it++;
}
que.insert(it, people[i]);
// wrong: que.insert(que.begin() + position, people[i]);
}
return vector<vector<int>>(que.begin(), que.end());
}
};
小结
- cmp
- list和vector的insert
452. 用最少数量的箭引爆气球
class Solution {
private:
static bool cmp(const vector<int>& a, const vector<int>& b) {
return a[0] < b[0];
}
public:
int findMinArrowShots(vector<vector<int>>& points) {
// 首先进行排序,左边界、升序,
sort(points.begin(), points.end(), cmp);
// 然后比较交叉处
// 若不交叉,弓箭数++
// 若交叉,选择右边界小的
if (points.size() == 0) {
return 0;
}
int count = 1;
for (int i = 1; i < points.size(); i++) {
// wrong: points[i][0] > points[i - 1][0]
if (points[i][0] > points[i - 1][1]) {
count++;
} else {
// wrong: min(points[i][1], points[i - 1][0])
points[i][1] = min(points[i][1], points[i - 1][1]);
}
}
return count;
}
};
435. 无重叠区间
class Solution {
private:
// static
// const
// &
/*
按照右边界排序,就要从左向右遍历,因为右边界越小越好,只要右边界越小,留给下一个区间的空间就越大,所以从左向右遍历,优先选右边界小的。
*/
// static bool cmp( vector<int> a, vector<int> b) {
static bool cmp(const vector<int>& a, const vector<int>& b) {
//return a[0] < a[0];
return a[1] < b[1];
}
public:
int eraseOverlapIntervals(vector<vector<int>>& intervals) {
if (intervals.size() == 0) {
return 0;
}
sort(intervals.begin(), intervals.end(), cmp);
int count = 1;
int end = intervals[0][1];
for (int i = 1; i < intervals.size(); i++) {
if (end <= intervals[i][0]) {
count++;
end = intervals[i][1];
}
}
return intervals.size() - count;
}
};
小结
- 究竟是按照右边界排序,还是按照左边界排序
763. 划分字母区间
class Solution {
public:
vector<int> partitionLabels(string s) {
int hash[26] = {0};
// 记录每个字母出现最远的地方
for (int i = 0; i < s.size(); i++) {
hash[s[i] - 'a'] = i;
}
int left = 0;
int right = 0;
vector<int> result;
for (int i = 0; i < s.size(); i++) {
right = max(right, hash[s[i] - 'a']);
if (i == right) {
result.push_back(right - left + 1);
left = right + 1;
}
}
return result;
}
};
小结
- 难点在于right=max(right, hash[s[i]-‘a’]);
56. 合并区间
class Solution {
private:
static bool cmp(const vector<int>& a, const vector<int>& b) {
return a[0] < b[0];
}
public:
vector<vector<int>> merge(vector<vector<int>>& intervals) {
// 先按从小到大排序,左边
// 然后比较右边界,选择大的一边
// 把intervals存入结果,慢慢改变右边界
vector<vector<int>> result;
if (intervals.size() == 0) return result;
sort(intervals.begin(), intervals.end(), cmp);
result.push_back(intervals[0]);
for (int i = 1; i < intervals.size(); i++) {
// wrong:intervals[i - 1][1] >= intervals[i][0]
if (result.back()[1] >= intervals[i][0]) {
result.back()[1] = max(result.back()[1], intervals[i][1]);
} else {
result.push_back(intervals[i]);
}
}
return result;
}
};
小结
- 问题所在:合并比较的是result.back()[1] 和 intervals[i][0]
738. 单调递增的数字
class Solution {
public:
int monotoneIncreasingDigits(int n) {
// 首先确定遍历顺序是从后向前
// 当 strNum[i-1] > strNum[i] 时,将str[i-1]-1,strNum[i]置为9
// 怎么将int n 转换为 string strNum
// to_string(): 从后到前的取出每个数字,然后逆序输出,注意负数
// strNum << n
string strNum = to_string(n);
int flag = strNum.length();
for (int i = strNum.length() - 1; i > 0; i--) {
if (strNum[i - 1] > strNum[i]) {
strNum[i - 1]--;
flag = i;
}
}
for (int i = flag; i < strNum.length(); i++) {
strNum[i] = '9';
}
return stoi(strNum);
}
};
小结
- to_string(), stoi()
714. 买卖股票的最佳时机含手续费
class Solution {
public:
int maxProfit(vector<int>& prices, int fee) {
// 分三种情况
// 1. 买入,也是前面真正的卖出
// 2. 卖出,但不真正卖出
// 3. 不操作
int min = prices[0];
int result = 0;
for (int i = 1; i < prices.size(); i++) {
// 1.
if (prices[i] < min) {
min = prices[i];
}
// 2.
if (prices[i] > min + fee) {
result += prices[i] - min - fee;
min = prices[i] - fee;
}
// 3.
if (prices[i] > min && prices[i] < min + fee) {
continue;
}
}
return result;
}
};
小结
- 情况2中,并不是真正的卖出,因为是prices[i]>min, 此时将min更新
968. 监控二叉树
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
private:
int result;
int traversal(TreeNode* cur) {
// 从下往上遍历(后序)
// 0:无覆盖
// 1:摄像头
// 2:有覆盖
// 空节点,必须覆盖
if (cur == NULL) return 2;
int left = traversal(cur->left);
int right = traversal(cur->right);
// 左右只要有一个无覆盖,就增加一个摄像头,并且说明本节点需要一个摄像头
if (left == 0 || right == 0) {
result++;
return 1;
}
// 左右只要有一个有摄像头,说明本节点被覆盖了
if (left == 1 || right == 1) {
return 2;
}
// 左右都被覆盖,说明本节点无覆盖
if (left == 2 && right == 2) {
return 0;
}
return -1;
}
public:
int minCameraCover(TreeNode* root) {
result = 0;
if (traversal(root) == 0) {
result++;
}
return result;
}
};
小结
- 空节点设为被覆盖,在叶子节点的父节点装摄像头
- 隔两个结点放一个摄像头