代码随想录-笔记-其七
我们来到了贪心算法的章节。
贪心算法和其他部分不太一样的是,他更多的是突出一种思路:通过求局部最优解来求全局最优解。因为只是一个大的思想逻辑,针对不同题型总是有不同的解决方案,所以贪心算法也不想其他算法那样有一个很经典的模板。
455. 分发饼干 - 力扣(LeetCode)
假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。
对每个孩子 i
,都有一个胃口值 g[i]
,这是能让孩子们满足胃口的饼干的最小尺寸;并且每块饼干 j
,都有一个尺寸 s[j]
。如果 s[j] >= g[i]
,我们可以将这个饼干 j
分配给孩子 i
,这个孩子会得到满足。你的目标是满足尽可能多的孩子,并输出这个最大数值。
class Solution {
public:
int findContentChildren(vector<int>& g, vector<int>& s) {
sort(g.begin(), g.end());
sort(s.begin(), s.end());
int child_i=0;
for(int i=0;i<s.size();++i){
if(child_i>=g.size())break;
if(s[i]>=g[child_i]){
++child_i;
}
}
return child_i;
}
};
376. 摆动序列 - 力扣(LeetCode)
如果连续数字之间的差严格地在正数和负数之间交替,则数字序列称为 摆动序列 。第一个差(如果存在的话)可能是正数或负数。仅有一个元素或者含两个不等元素的序列也视作摆动序列。
-
例如,
[1, 7, 4, 9, 2, 5]
是一个 摆动序列 ,因为差值(6, -3, 5, -7, 3)
是正负交替出现的。 - 相反,
[1, 4, 7, 2, 5]
和[1, 7, 4, 5, 5]
不是摆动序列,第一个序列是因为它的前两个差值都是正数,第二个序列是因为它的最后一个差值为零。
子序列 可以通过从原始序列中删除一些(也可以不删除)元素来获得,剩下的元素保持其原始顺序。
给你一个整数数组 nums
,返回 nums
中作为 摆动序列 的 最长子序列的长度 。
class Solution {
public:
int wiggleMaxLength(vector<int>& nums) {
if (nums.size() < 2) return nums.size();
int prediff = 0,curdiff=0;
int res = 1;
for (int i = 1; i < nums.size(); ++i) {
curdiff = nums[i] - nums[i - 1];
if (curdiff * prediff <= 0 && curdiff != 0) {
++res;
prediff = curdiff;
}
}
return res;
}
};
53. 最大子数组和 - 力扣(LeetCode)
给你一个整数数组 nums
,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。子数组是数组中的一个连续部分。
class Solution {
public:
int maxSubArray(vector<int>& nums) {
int presum=0,res=INT_MIN;
for(int num:nums){
presum=max(num,presum+num);
res=max(res,presum);
}
return res;
}
};
122. 买卖股票的最佳时机 II - 力扣(LeetCode)
给你一个整数数组 prices
,其中 prices[i]
表示某支股票第 i
天的价格。
在每一天,你可以决定是否购买和/或出售股票。你在任何时候 最多 只能持有 一股 股票。你也可以先购买,然后在 同一天 出售。
返回 你能获得的 最大 利润 。
class Solution {
public:
int maxProfit(vector<int>& prices) {
int result = 0;
for (int i = 1; i < prices.size(); i++) {
result += max(prices[i] - prices[i - 1], 0);
}
return result;
}
};
55. 跳跃游戏 - 力扣(LeetCode)
给你一个非负整数数组 nums
,你最初位于数组的 第一个下标 。数组中的每个元素代表你在该位置可以跳跃的最大长度。
判断你是否能够到达最后一个下标,如果可以,返回 true
;否则,返回 false
。
class Solution {
public:
bool canJump(vector<int>& nums) {
int rightmost=0;
for(int i=0;i<=rightmost;++i){
rightmost=max(rightmost,i+nums[i]);
if(rightmost>=nums.size()-1){return true;}
}
return false;
}
};
45. 跳跃游戏 II - 力扣(LeetCode)
给定一个长度为 n
的 0 索引整数数组 nums
。初始位置为 nums[0]
。
每个元素 nums[i]
表示从索引 i
向前跳转的最大长度。换句话说,如果你在 nums[i]
处,你可以跳转到任意 nums[i + j]
处:
0 <= j <= nums[i]
i + j < n
返回到达 nums[n - 1]
的最小跳跃次数。生成的测试用例可以到达 nums[n - 1]
。
class Solution {
public:
int jump(vector<int>& nums) {
int rightmost=0,end=0,step=0;
for(int i=0;i<nums.size()-1;++i){
rightmost=max(rightmost,i+nums[i]);
if(i==end){
end=rightmost;
++step;
}
}
return step;
}
};
这里可能乍一看比较难理解的是为什么i要小于nums.size()-1,但其实很简单:我们要的就是执行到nums.size()-1的位置,如果我们写小于nums.size()就会多执行一次循环,或者说我们就是利用(i==end)而i不取nums.size()-1的条件来判断。
1005. K 次取反后最大化的数组和 - 力扣(LeetCode)
给你一个整数数组 nums
和一个整数 k
,按以下方法修改该数组:
- 选择某个下标
i
并将nums[i]
替换为-nums[i]
。
重复这个过程恰好 k
次。可以多次选择同一个下标 i
。
以这种方式修改数组后,返回数组 可能的最大和 。
class Solution {
public:
int largestSumAfterKNegations(vector<int>& nums, int k) {
int res=0;
sort(nums.begin(),nums.end());
for(int i=0;i<nums.size();++i){
if(k>0&&nums[i]<0){
nums[i]=-nums[i];
--k;
}
res+=nums[i];
}
int minabs=*min_element(nums.begin(), nums.end());
if(k%2==1)res-=2*minabs;
return res;
}
};
134. 加油站 - 力扣(LeetCode)
在一条环路上有 n
个加油站,其中第 i
个加油站有汽油 gas[i]
升。
你有一辆油箱容量无限的的汽车,从第 i
个加油站开往第 i+1
个加油站需要消耗汽油 cost[i]
升。你从其中的一个加油站出发,开始时油箱为空。
给定两个整数数组 gas
和 cost
,如果你可以按顺序绕环路行驶一周,则返回出发时加油站的编号,否则返回 -1
。如果存在解,则 保证 它是 唯一 的。
class Solution {
public:
int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {
int cursum=0,totalsum=0,start=0;
for(int i=0;i<gas.size();++i){
cursum+=gas[i]-cost[i];
if(cursum<0){
start=i+1;
cursum=0;
}
totalsum+=gas[i]-cost[i];
}
if(totalsum<0)return -1;
return start;
}
};
135. 分发糖果 - 力扣(LeetCode)
n
个孩子站成一排。给你一个整数数组 ratings
表示每个孩子的评分。
你需要按照以下要求,给这些孩子分发糖果:
- 每个孩子至少分配到
1
个糖果。 - 相邻两个孩子评分更高的孩子会获得更多的糖果。
请你给每个孩子分发糖果,计算并返回需要准备的 最少糖果数目 。
class Solution {
public:
int candy(vector<int>& ratings) {
int n=ratings.size();
vector<int> candy(n,1);
for(int i=1;i<n;++i){
if(ratings[i]>ratings[i-1]){
candy[i]=candy[i-1]+1;
}
}
for(int i=n-2;i>=0;--i){
if(ratings[i]>ratings[i+1]){
candy[i]=max(candy[i],candy[i+1]+1);
}
}
return accumulate(candy.begin(), candy.end(),0);
}
};
860. 柠檬水找零 - 力扣(LeetCode)
在柠檬水摊上,每一杯柠檬水的售价为 5
美元。顾客排队购买你的产品,(按账单 bills
支付的顺序)一次购买一杯。
每位顾客只买一杯柠檬水,然后向你付 5
美元、10
美元或 20
美元。你必须给每个顾客正确找零,也就是说净交易是每位顾客向你支付 5
美元。
注意,一开始你手头没有任何零钱。
给你一个整数数组 bills
,其中 bills[i]
是第 i
位顾客付的账。如果你能给每位顾客正确找零,返回 true
,否则返回 false
。
class Solution {
public:
bool lemonadeChange(vector<int>& bills) {
int five=0,ten=0,twenty=0;
for(int pay:bills){
if(pay==5){
five++;
}
else if(pay==10){
if(!five)return false;
else{
five--;
ten++;
}
}
else if(pay==20){
if(!five||!ten&&five<3)return false;
else{
if(!ten){
five-=3;
}
else{
five--;
ten--;
}
}
}
}
return true;
}
};
406. 根据身高重建队列 - 力扣(LeetCode)
假设有打乱顺序的一群人站成一个队列,数组 people
表示队列中一些人的属性(不一定按顺序)。每个 people[i] = [hi, ki]
表示第 i
个人的身高为 hi
,前面 正好 有 ki
个身高大于或等于 hi
的人。
请你重新构造并返回输入数组 people
所表示的队列。返回的队列应该格式化为数组 queue
,其中 queue[j] = [hj, kj]
是队列中第 j
个人的属性(queue[0]
是排在队列前面的人)。
class Solution {
public:
static bool cmp(const vector<int>& a,const vector<int>& b){
if(a[0]==b[0])return a[1]<b[1];
return a[0]>b[0];
}
vector<vector<int>> reconstructQueue(vector<vector<int>>& people) {
sort(people.begin(), people.end(),cmp);
vector<vector<int>> queue;
for(int i=0;i<people.size();++i){
int pos=people[i][1];
queue.insert(queue.begin()+pos, people[i]);
}
return queue;
}
};
452. 用最少数量的箭引爆气球 - 力扣(LeetCode)
有一些球形气球贴在一堵用 XY 平面表示的墙面上。墙面上的气球记录在整数数组 points
,其中points[i] = [xstart, xend]
表示水平直径在 xstart
和 xend
之间的气球。你不知道气球的确切 y 坐标。
一支弓箭可以沿着 x 轴从不同点 完全垂直 地射出。在坐标 x
处射出一支箭,若有一个气球的直径的开始和结束坐标为 x
start
,x
end
, 且满足 xstart ≤ x ≤ x
end
,则该气球会被 引爆 。可以射出的弓箭的数量 没有限制 。 弓箭一旦被射出之后,可以无限地前进。
给你一个数组 points
,返回引爆所有气球所必须射出的 最小 弓箭数 。
class Solution {
public:
static bool cmp(const vector<int>& a,const vector<int>& b){
return a[0]<b[0];
}
int findMinArrowShots(vector<vector<int>>& points) {
sort(points.begin(), points.end(), cmp);
int res=1;
for(int i=1;i<points.size();++i){
if(points[i][0]>points[i-1][1]){
res++;
}
else{
points[i][1]=min(points[i-1][1],points[i][1]);
}
}
return res;
}
};
435. 无重叠区间 - 力扣(LeetCode)
给定一个区间的集合 intervals
,其中 intervals[i] = [starti, endi]
。返回 需要移除区间的最小数量,使剩余区间互不重叠 。
注意 只在一点上接触的区间是 不重叠的。例如 [1, 2]
和 [2, 3]
是不重叠的。
class Solution {
public:
static bool cmp(const vector<int>& a,const vector<int>& b){
return a[1]<b[1];
}
int eraseOverlapIntervals(vector<vector<int>>& intervals) {
sort(intervals.begin(), intervals.end(), cmp);
int res=0,end=intervals[0][1];
for(int i=1;i<intervals.size();++i){
if(intervals[i][0]<end){
res++;
}
else end=intervals[i][1];
}
return res;
}
};
763. 划分字母区间 - 力扣(LeetCode)
给你一个字符串 s
。我们要把这个字符串划分为尽可能多的片段,同一字母最多出现在一个片段中。
注意,划分结果需要满足:将所有划分结果按顺序连接,得到的字符串仍然是 s
。
返回一个表示每个字符串片段的长度的列表。
class Solution {
public:
vector<int> partitionLabels(string s) {
int last[26];
for(int i=0;i<s.size();++i){
last[s[i]-'a']=i;
}
int start=0,end=0;
vector<int> res;
for(int i=0;i<s.size();++i){
end=max(end,last[s[i]-'a']);
if(i==end){
res.push_back(end-start+1);
start=end+1;
}
}
return res;
}
};
56. 合并区间 - 力扣(LeetCode)
以数组 intervals
表示若干个区间的集合,其中单个区间为 intervals[i] = [starti, endi]
。请你合并所有重叠的区间,并返回 一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间 。
class Solution {
public:
vector<vector<int>> merge(vector<vector<int>>& intervals) {
sort(intervals.begin(), intervals.end());
vector<vector<int>> res;
res.push_back(intervals[0]);
for(int i=0,j=i+1;j<intervals.size();++j){
if(res[i][1]>=intervals[j][0]){
res[i][1]=max(res[i][1],intervals[j][1]);
}
else{
res.push_back(intervals[j]);
++i;
}
}
return res;
}
};
738. 单调递增的数字 - 力扣(LeetCode)
当且仅当每个相邻位数上的数字 x
和 y
满足 x <= y
时,我们称这个整数是单调递增的。
给定一个整数 n
,返回 小于或等于 n
的最大数字,且数字呈 单调递增 。
class Solution {
public:
int monotoneIncreasingDigits(int n) {
string str=to_string(n);
int flag=str.size();
for(int i=str.size()-1;i>0;--i){
if(str[i]<str[i-1]){
flag=i;
str[i-1]--;
}
}
for(int i=flag;i<str.size();++i){
str[i]='9';
}
return stoi(str);
}
};
968. 监控二叉树 - 力扣(LeetCode)
给定一个二叉树,我们在树的节点上安装摄像头。
节点上的每个摄影头都可以监视其父对象、自身及其直接子对象。
计算监控树的所有节点所需的最小摄像头数量。
/**
* 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 {
public:
int res;
int traversal(TreeNode* cur){
if(!cur)return 2;
int left=traversal(cur->left);
int right=traversal(cur->right);
if(left==2&&right==2)return 0;
if(left==0||right==0){
res++;
return 1;
}
if(left==1||right==1)return 2;
return -1;
}
int minCameraCover(TreeNode* root) {
res=0;
if(traversal(root)==0){
res++;
}
return res;
}
};