力扣贪心算法专题(三)力扣题 452、435、763、56、738、968、714 思路及C++实现

文章目录

  • 贪心算法
  • 452. 用最少数量的箭引爆气球
  • 435. 无重叠区间
    • 做法1 右边界排序 不重叠区间
    • 做法2 右边界排序 不重叠区间
    • 做法3 左边界排序 重叠区间
  • 763.划分字母区间
    • 做法1
    • 做法2
  • 56. 合并区间
  • 738.单调递增的数字
    • 暴力解法
    • 贪心算法
  • 968.监控二叉树
  • 714.买卖股票的最佳时机含手续费

贪心算法

  1. 贪心的本质是选择每一阶段的局部最优,从而达到全局最优。如何通过局部最优,推出整体最优。
  2. 贪心算法的套路就是常识性推导加上举反例
  3. 贪心算法解题思路:想清楚局部最优是什么,如果推导出全局最优,就够了。

452. 用最少数量的箭引爆气球

在这里插入图片描述

思路:

  • 只射重叠最多的气球,用的弓箭一定最少。
  • 贪心算法局部最优:当气球出现重叠,一起射,所用弓箭最少。全局最优:把所有气球射爆所用弓箭最少。

做法:

  • 把气球排序,从前到后(从后向前)遍历气球,仅跳过被射过的气球,记录箭的数量。
  • 按照气球的起始位置排序,从前向后遍历气球数组,靠左尽可能让气球重复。如果气球重叠了,重叠气球中右边边界的最小值 之前的区间一定需要一个弓箭。
  • 例如,[[10,16],[2,8],[1,6],[7,12]]为例。排序后,[[1,6], [2,8], [7,12], [10,16]]。首先第一组重叠气球[1,6]和[2,8],一定需要一支箭;[7,12]气球3的左边界大于了 第一组重叠气球[1,6]的最小右边界,所以还需要一支箭来射气球3。

代码:

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) {if(points.size()==0) return 0;//如果没有气球 不需要箭sort(points.begin(), points.end(), cmp);//排序int result = 1;//至少需要一支箭for(int i=1; i<points.size(); i++)//注意从第二个气球开始{//如果气球不重叠 当前气球的头 在 上一个气球的尾后面 需要一只箭//不取= 是因为题目中满足xstart ≤ x ≤ xend,则该气球会被引爆。//那么说明两个气球挨在一起不重叠也可以一起射爆if(points[i][0] > points[i-1][1]) result++;//如果气球重叠  更新重叠气球最小右边界else points[i][1] = min(points[i-1][1], points[i][1]);}return result;}
};

435. 无重叠区间

做法1 右边界排序 不重叠区间

思路: 这和452.用最少数量的箭引爆气球非常像,弓箭数相当于是非交叉区间的数量。不同的是,题目中认为[0,1]和[1,2]不是相邻区间,注意判断条件的修改,然后用总区间数减去弓箭数量 就是要移除的区间数量了。

代码:

class Solution {
public:static bool cmp_r(const vector<int>& a, const vector<int>& b){return a[1] < b[1];}static bool cmp_l(const vector<int>& a, vector<int>& b){return a[0] < b[0];}int eraseOverlapIntervals(vector<vector<int>>& intervals) {if(intervals.size()==0) return 0;//做法1 右边界排序 重叠区间sort(intervals.begin(), intervals.end(), cmp_r);int count = 1;//不重叠区间数for(int i=1; i<intervals.size(); i++){//注意 [0,1]和[1,2]不是相邻区间if(intervals[i][0] >= intervals[i-1][1]) count++;else intervals[i][1] = min(intervals[i][1], intervals[i-1][1]);}return intervals.size() - count;}
};

做法2 右边界排序 不重叠区间

思路: 让区间尽可能重叠,首先右边界按照从小到大排序,从左向右遍历记录非交叉区间的个数,最后用区间总数减去非交叉区间的个数就是需要移除的区间个数了。

代码:

class Solution {
public:static bool cmp_r(const vector<int>& a, const vector<int>& b){return a[1] < b[1];}static bool cmp_l(const vector<int>& a, vector<int>& b){return a[0] < b[0];}int eraseOverlapIntervals(vector<vector<int>>& intervals) {if(intervals.size()==0) return 0;//做法2 右边界排序 不重叠区间sort(intervals.begin(), intervals.end(), cmp_r);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;}
};

做法3 左边界排序 重叠区间

代码:

class Solution {
public:static bool cmp_r(const vector<int>& a, const vector<int>& b){return a[1] < b[1];}static bool cmp_l(const vector<int>& a, vector<int>& b){return a[0] < b[0];}int eraseOverlapIntervals(vector<vector<int>>& intervals) {if(intervals.size()==0) return 0;//做法3 左边界排序 重叠区间sort(intervals.begin(), intervals.end(), cmp_l);int count = 0;//记录重叠区间for(int i=1; i<intervals.size(); i++){//如果当前区间的头 在 上一个区间的尾巴前面 说明重叠//注意 [0,1]和[1,2]不是相邻区间if(intervals[i][0] < intervals[i-1][1]){intervals[i][1] = min(intervals[i][1], intervals[i-1][1]);//更新最小右边界count++; }}return count;}
};

763.划分字母区间

在这里插入图片描述

做法1

思路:
用最远出现距离模拟了圈字符的行为,要找每一个字母的边界,如果找到之前遍历过的所有字母的最远边界,说明这个边界就是分割点了,可以分为如下两步:

  • 统计每一个字符最后出现的位置;
  • 从头遍历字符,并更新字符的最远出现下标,如果找到字符最远出现位置下标和当前下标相等了,则找到了分割点。

代码:

class Solution {
public:vector<int> partitionLabels(string s) {int hash[27] = {0};//保存字母最后出现的位置 //统计字母最后出现的位置//i为字符,hash[i]为字符出现的最后位置for(int i=0; i<s.size(); i++){hash[s[i] - 'a'] = i;}vector<int> result;int left = 0;int right = 0;//找到最远边界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 = i + 1;//left要更新 跳到i+1位置开始新的划分起点}}return result;}
};

做法2

思路:
统计字符串中所有字符的起始和结束位置,记录这些区间,实际上也就是435.无重叠区间题目里的输入,将区间按左边界从小到大排序,找到边界将区间划分成组,互不重叠,找到的边界就是答案。
首先要获得435.无重叠区间题目里的输入,然后按照435.无重叠区间题目里的思路去写。

代码:

class Solution {
public://做法2//首先获得区间输入vector<vector<int>> countLabels(string s){vector<vector<int>> hash(26, vector<int>(2, INT_MIN));//记录区间 默认26哥字母vector<vector<int>> hash_filter;//记录每个字母出现的起始位置for(int i=0; i<hash.size(); ++i){if(hash[s[i] - 'a'][0] == INT_MIN) hash[s[i] - 'a'][0] = i;//最开始位置hash[s[i] - 'a'][1] = i;//最末尾位置}//去掉s中没有出现的字母的区间for(int i=0; i<s.size(); ++i){if(hash[i][0] != INT_MIN) hash_filter.push_back(hash[i]);//存放出现字母对应区间}return hash_filter;}//排序 左边界排序static bool cmp(vector<int>& a, vector<int>& b){return a[0] < b[0];}//找出重叠区间的长度个数vector<int> partitionLabels(string s){vector<int> result;//记录区间分割点//获得s各字母的起始区间vector<vector<int>> hash = countLabels(s);sort(hash.begin(), hash.end(), cmp);//按照左边界排序int right = hash[0][1];// 记录最大右边界int left = 0;//找到分割点for(int i=1; i<hash.size(); ++i){//当前区间的头 在 上一区间的尾部 前面if(hash[i][0] > right){result.push_back(right - left + 1);//存放长度left = hash[i][0];//left左边界更新 找下一个重叠区间长度}right = max(right, hash[i][1]);//right最远右边界更新 找下一个重叠区间长度}result.push_back(right - left + 1);//最右端return result;}
};

56. 合并区间

在这里插入图片描述

思路:
和435.无重叠区间题目思路类似,找重叠区间,不同的是用合并区间后左边界和右边界,作为一个新的区间,加入到result数组里就可以了。如果没有合并就把原区间加入到result数组。

代码:

class Solution {
public:vector<vector<int>> merge(vector<vector<int>>& intervals) {vector<vector<int>> result;//lambda表达式 左边界排序sort(intervals.begin(), intervals.end(), [](const vector<int>& a, const vector<int>& b){return a[0] < b[0];});//左边界排序后 result.back()的左边界一定是最小值 只需要跟新右区间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]);else result.push_back(intervals[i]);// 区间不重叠}return result;}
};

738.单调递增的数字

在这里插入图片描述

暴力解法

依次取位,从个位开始向高位依次判断,数字是否递增;从大到小遍历

代码:

class Solution {
private:bool isup(int num){int max = 10, temp = 0;//个位最大取9while(num){temp = num % 10;if(max >= temp) max = temp;else return false;num = num / 10;//前进一位}return true;}
public:int monotoneIncreasingDigits(int n) {for(int i=n; i>=0; i--)//从大到小逐个判断{if(isup(i)) return i;}return 0;}
};

贪心算法

一个两位数xy,找小于等于xy的最大单调递增整数,如果x>y,让x–,y=9,即(x-1)9;如果x≤y,不变。从后向前遍历数字。
不可以从前往后遍历,如果按照上述操作,对于三位数等多位数有可能会导致结果不对,例如543,从前往后遍历就变成了439,百位大于十位,不是单调递增数字。从后往前遍历,就是543→539→499

代码:

class Solution {
//贪心算法
public:int monotoneIncreasingDigits(int n) {//先转成字符串string strnum = to_string(n);//标记赋值9从哪里开始,设置默认值为strnum.size()//防止第二个for循环在flag没有被赋值的情况下执行int mark = strnum.size();for(int i = strnum.size()-1; i>0; i--){if(strnum[i-1] > strnum[i]) {mark = i;//标记strnum[i-1]--;}}//在标记位置赋值为9for(int i = mark; i<strnum.size(); i++){strnum[i] = '9';}return stoi(strnum);}};

968.监控二叉树

分析:

  1. 摄像头都没有在叶子节点上。如果放在叶子节点上,就会浪费一层覆盖;放在叶子节点的父节点位置,才能充分利用摄像头的覆盖面积,即摄像头可以覆盖上中下三层。
  2. 从下往上看。如果从上往下看,头结点不放置,可以省一个摄像头。

思路:
从下往上遍历,后序遍历,左右中顺序,回溯时先给叶子节点父节点放个摄像头,然后隔两个节点放一个摄像头,直至到二叉树头结点。
局部最优:让叶子节点的父节点安摄像头,所用摄像头最少,整体最优:全部摄像头数量所用最少。

如何放置摄像头?
状态转移的公式,三个数字来表示:
0:该节点无覆盖
1:本节点有摄像头
2:本节点有覆盖
空节点的状态只能是有覆盖,无摄像头就是无覆盖或者有覆盖的状态

单层递归的四种情况:

  1. 情况1:左右节点都有覆盖,那么中间节点就是无覆盖了
  2. 情况2:左右节点至少有一个无覆盖的情况,则中间节点(父节点)应该放摄像头:
  • left == 0 && right == 0 左右节点无覆盖
  • left == 1 && right == 0 左节点有摄像头,右节点无覆盖
  • left == 0 && right == 1 左节点有无覆盖,右节点摄像头
  • left == 0 && right == 2 左节点无覆盖,右节点覆盖
  • left == 2 && right == 0 左节点覆盖,右节点无覆盖
    此时摄像头的数量要加一,并且return 1,代表中间节点放摄像头。
  1. 情况3:左右节点至少有一个有摄像头,那么其父节点就是覆盖的状态
  • left == 1 && right == 2 左节点有摄像头,右节点有覆盖
  • left == 2 && right == 1 左节点有覆盖,右节点有摄像头
  • left == 1 && right == 1 左右节点都有摄像头
  1. 头结点没有覆盖,递归结束之后,还要判断根节点,如果没有覆盖,result++

代码:

class Solution {
private:int result;/*0---无覆盖1---有摄像头2---有覆盖*/int traversal(TreeNode* cur){//空节点,该节点有覆盖if(cur == NULL) return 2;int left = traversal(cur->left);int right = traversal(cur->right);//1.左右都有节点 中间节点无覆盖if(left == 2 && right == 2) return 0;//2.左右节点至少有一个是覆盖  中间节点放摄像头if(left == 0 || right == 0){result++;return 1;}//3.左右节点至少有一个有摄像头 中间节点有覆盖if(left == 1 || right == 1) return 2;return -1;}public:int minCameraCover(TreeNode* root) {result = 0;//4.判断头结点无覆盖 +1if(traversal(root) == 0) result++;return result;}
};

714.买卖股票的最佳时机含手续费

在这里插入图片描述
思路:
涉及手续费,要考虑什么时候买卖股票,因为有可能买卖利润不足以支付手续费。
最低价买股票,即买入日期,此时股价最小值。
扣除手续费的最高价卖股票,即卖出日期。只要当前价格大于最低价格+手续费,就可以收获利润。而最终的卖出日期是连续收获利润区间里的最后一天。
收获利润操作时的三种情况:
情况一:最低价时,买入股票
情况二:不买不卖,保持原有状态,买不便宜,卖亏本
情况三:当前价格大于最低价格+手续费时卖出股票,计算利润,不是真的卖出股票,同时记录最小价格,最后一次计算计算利润才是真的卖出股票

代码:

class Solution {
public:int maxProfit(vector<int>& prices, int fee) {int result = 0;int minprice = prices[0];for(int i=1; i<prices.size(); i++){//低价买入 买入日期if(prices[i] < minprice) minprice = prices[i];//不买不卖 买不便宜 卖亏本if(prices[i] >= minprice && prices[i] <= minprice+fee) continue;//计算利润 最后一天计算利润才是真的卖出日期if(prices[i] > minprice+fee){result += prices[i] - minprice - fee;minprice = prices[i] - fee;//每天要更新最低价格}}return result;}
};

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://xiahunao.cn/news/142914.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

HyDE、UDAPDR(LLM大模型用于信息检索)

本篇博文继续整理LLM在搜索推荐领域的应用&#xff0c;往期文章请往博主主页查看更多。 Precise Zero-Shot Dense Retrieval without Relevance Labels 这篇文章主要做zero-shot场景下的稠密检索&#xff0c;通过借助LLM的力量不需要Relevance Labels&#xff0c;开箱即用。作…

matlab2018a 安装指南

安装步骤&#xff1a; 如下截图&#xff0c;先安装1&#xff0c; 输入密钥09806-07443-53955-64350-21751-41297 安装完了弹出1 弹出1后&#xff0c;双击2 双击2后&#xff08;此时2已载入&#xff09;&#xff0c;会有新的盘出现&#xff0c;如下&#xff1a; 在安装1…

Matlab R2022a安装

1、在“Mathworks Matlab R2022a\R2022a_Windows”目录中找到setup.exe双击。选择“我有文件安装密钥”&#xff0c; 2、输入文件安装密钥&#xff1a;“50874-33247-14209-37962-45495-25133-28159-33348-18070-60881-29843-35694-31780-18077-36759-35464-51270-19436-54668-…

RGB图片像素点随机化——Matlab实现

在分析照片各个区域的色度、亮度平均值时&#xff0c;为了使每行/每列/整体的像素点特征分布均匀以加快分析速度、减小误差时&#xff0c;这时候就要对像素点进行随机化操作&#xff0c;也就是洗牌。 用Matlab来完成此任务再合适不过了。对于RGB类型的彩色图片&#xff0c;它在…

Matlab2018a安装教程

Matlab2018a 安装教程 1. 安装之旅 第一步&#xff1a;下载好压缩包后&#xff0c;对压缩包进行解压&#xff0c;由于文件比较大&#xff0c;需要花费一些时间。 第二步&#xff1a;打开解压后的文件&#xff0c;找到 setup&#xff0c;右击以管理员身份运行&#xff0c;稍后…

Matlab R2020b安装

matlab2020b安装 一&#xff0c;下载 百度网盘 链接&#xff1a;https://pan.baidu.com/s/18iLFaAbWt8IntUefX3eWfA 提取码&#xff1a;p6in 如果下载很慢的话应该是没开p2p加速(最近度盘良心发现加了个p2p下载) 开启方法&#xff1a; 打开设置 开启提速模式 开启后会提供…

Matlab2018破解方法

原文&#xff1a;http://www.zhanshaoyi.com/6938.html 软件下载&#xff1a; Matlab R2018a_64位中文破解版&#xff1a;【点击下载】 安装前须知&#xff1a; 1.安装全程须断开电脑网络&#xff0c;否则安装不成功&#xff1b; Matlab 2018a的安装包必须使用虚拟光驱加载&…

MATLAB模拟QKD,基于STK和MATLAB的星地量子密钥分发仿真系统的制作方法

本发明属于量子信息处理技术领域,涉及一种基于stk和matlab的星地量子密钥分发仿真系统。 背景技术: 目前,自由空间量子密钥分发的研究仍局限于在星地平台的实验探索,相比于信道状态相对稳定的光纤量子密钥分发系统,自由空间量子密钥分发信道动态开放,并且缺乏有效的主动监…

基于MATLAB的混沌数字图像加密技术研究与仿真实现

摘 要 近年来&#xff0c;图像数据信息的安全性逐渐受到人们的关注&#xff0c;为了保证图像的可靠传输&#xff0c;混沌系统被引入图像加密技术。本文主要研究了两种基于混沌系统的图像加密方案。第一种方案是基于超混沌系统和 DNA 编解码运算相结合的图像加密算法&#xff0c…

MATLAB 2018a安装

matlab2018a的安装 matlab的各个版本的安装过程都是大同小异&#xff0c;安装过程中都需要断网。 第一步&#xff1a;如下所示&#xff0c;双击.iOS文件 第二步&#xff1a;以管理员的身份运行setup 第三步&#xff1a;选择使用文件安装密钥&#xff0c;之后点击下一步 第四…

archlinux 安装matlab

最近在学matlab使用的是windows版本的&#xff0c;比起windows我更喜欢在linux中写代码。于是乎就想在Linux中安装一下。 主要过程参考此篇文章&#xff1a; 《【首发】 ubuntu20.04安装matlab2021b/matlab2020b》 https://blog.csdn.net/hanjuefu5827/article/details/1151677…

2019matlab安装

本文转载自Matlab R2019a Win64位 迅雷下载链接_Yohaoa-CSDN博客_matlab迅雷下载 和MATLAB 2019a安装教程和破解方法(附Crack文件) | 我爱分享网 1.下载安装包18G&#xff0c;迅雷磁力链&#xff1a; magnet:?xturn:btih:733DFBA6CCC23DB9FFD6287C169A15664897E78D 2.在打开…

matlab更改安全密钥,Linux下设置安全密钥登录

步骤&#xff1a; 1、本地生成密钥(公钥和私钥)&#xff1a; (1) 本地是 Windows 系统&#xff1a; 打开 XShell &#xff0c;选择 Tool >> New User Key Wizard 密钥长度可以是1024或2048&#xff0c;导出公钥&#xff0c;假设文件名为&#xff1a; Ubuntu_59_rsa_2048.…

Matlab R2014b安装教程

1&#xff0c;下载Matlab R2014b ISO格式安装包 2&#xff0c;用虚拟光驱加载下载的ISO格式安装包 3&#xff0c;双击setup&#xff0c;开始安装&#xff0c;选择不联网安装&#xff0c;许可证安装密钥为11111111111111111111 4&#xff0c;选择安装位置&#xff0c;这里…

微信公众号网页授权步骤过程

微信公众号网页授权 准备工作网页授权 准备工作 登录微信公众平台&#xff0c;启用“服务器配置”并添加相关配置 &#xff08;1&#xff09;代码中加入token校验的验证&#xff0c;这时可正确配置服务器&#xff0c;如下图&#xff1a; 其中url和token值要相对应。 GetMappi…

Django+微信公众号开发小项目

最近搞了点事情&#xff0c;因为web.py对微信公众号开发时不方便扩展和复用&#xff0c;使用Django开发微信公众号。使用celery推送模板消息到用户微信上&#xff0c;最终方便以后重复利用和功能增加。 01 准备 python3环境 微信公众号 可用域名 Mysql数据库 redis数据库 …

微信公众号开发流程

1、首先注册微信公众号&#xff0c;要根据实际需求考虑清楚应该申请哪一种公众号 以下是官方给出的建议&#xff0c;大家可以多参考参考 1&#xff09;如果想简单的发送消息&#xff0c;达到宣传效果&#xff0c;建议可选择订阅号&#xff1b; 2&#xff09;如果想用公众号获得…

微信公众号程序开发接入流程

文章目录 文章简介微信公众号程序介绍传统H5网页&#xff0c;无需微信支持建立在微信支持下开发的微信公众号程序第一步第二步 文章简介 公司常有微信公众号程序开发的项目&#xff0c;每次接入微信时都要四处查找以前的代码&#xff0c;百度接入微信公众号的流程。浪费大量时间…

微信公众号白名单配置

微信公众号白名单配置 微信公众号升级之后&#xff0c;在获取access_token的时候需要配置IP白名单&#xff0c;如下图&#xff1a; 那么这个白名单是干什么的呢&#xff1f;微信给的解释是&#xff1a;为了提高公众平台开发者接口调用的安全性&#xff0c;避免一旦开发者ID和密…

uni-app开发微信公众号

一、公众号JSSDK使用 【1】验证后端返回的签名是否正确 https://mp.weixin.qq.com/debug/cgi-bin/sandbox?tjsapisign &#xff08;1&#xff09;jsapi_ticket获取方法&#xff1a;&#xff08;注意把本地IP放入白名单&#xff09; 1&#xff09; https://api.weixin.qq.com…