C++算法学习心得七.贪心算法(2)

1.跳跃游戏(55题)

题目描述:

给定一个非负整数数组,你最初位于数组的第一个位置。

数组中的每个元素代表你在该位置可以跳跃的最大长度。

判断你是否能够到达最后一个位置。

示例  1:

  • 输入: [2,3,1,1,4]
  • 输出: true
  • 解释: 我们可以先跳 1 步,从位置 0 到达 位置 1, 然后再从位置 1 跳 3 步到达最后一个位置。

贪心算法: 跳跃覆盖范围究竟可不可以覆盖到终点,

每次移动取最大跳跃步数(得到最大的覆盖范围),每移动一个单位,就更新最大覆盖范围。

贪心算法局部最优解:每次取最大跳跃步数(取最大覆盖范围),整体最优解:最后得到整体最大覆盖范围,看是否能到终点

i 每次移动只能在 cover 的范围内移动,每移动一个元素,cover 得到该元素数值(新的覆盖范围)的补充,让 i 继续移动下去。cover 每次只取 max(该元素数值补充后的范围, cover 本身范围)如果 cover 大于等于了终点下标,直接 return true 就可以了。

class Solution {
public:bool canJump(vector<int>& nums) {int cover = 0;//定义一个覆盖范围if(nums.size() == 1)return true;//如果只有一个元素我们直接就返回正确//这里需要注意i不能超过cover的覆盖范围,在这里遍历for(int i = 0;i <= cover;i++){cover = max(i + nums[i],cover);//我们更新下标范围,不断递归覆盖范围里的i和数值之和比大小if(cover >= nums.size() - 1)return true;//如果可以覆盖到最后,返回正确}return false;}
};
  • 时间复杂度: O(n)
  • 空间复杂度: O(1)

2.跳跃游戏 II(45题)

 题目描述:

给定一个非负整数数组,你最初位于数组的第一个位置。

数组中的每个元素代表你在该位置可以跳跃的最大长度。

你的目标是使用最少的跳跃次数到达数组的最后一个位置。

示例:

  • 输入: [2,3,1,1,4]
  • 输出: 2
  • 解释: 跳到最后一个位置的最小跳跃数是 2。从下标为 0 跳到下标为 1 的位置,跳  1  步,然后跳  3  步到达数组的最后一个位置。

说明: 假设你总是可以到达数组的最后一个位置。

贪心算法:

要从覆盖范围出发,不管怎么跳,覆盖范围内一定是可以跳到的,以最小的步数增加覆盖范围,覆盖范围一旦覆盖了终点,得到的就是最少步数!需要统计两个覆盖范围,当前这一步的最大覆盖和下一步最大覆盖 

class Solution {
public:int jump(vector<int>& nums) {if(nums.size() == 1)return 0;int cur = 0;//当前最大覆盖范围int next = 0;//下一个最大最大覆盖范围int result = 0;//这里注意i的范围for(int i = 0;i < nums.size();i++){next = max(i + nums[i],next);//覆盖范围的更新//如果遍历到最大当前范围if(i == cur){result++;//我们要记录结果cur = next;//把最大范围更新成下一个最大范围if(next >= nums.size()-1)break;//如果满足条件跳出}}return result;}
};
  • 时间复杂度: O(n)
  • 空间复杂度: O(1)

3.K次取反后最大化的数组和(1005题)

题目描述:

给定一个整数数组 A,我们只能用以下方法修改该数组:我们选择某个索引 i 并将 A[i] 替换为 -A[i],然后总共重复这个过程 K 次。(我们可以多次选择同一个索引 i。)

以这种方式修改数组后,返回数组可能的最大和。

示例 1:

  • 输入:A = [4,2,3], K = 1
  • 输出:5
  • 解释:选择索引 (1,) ,然后 A 变为 [4,-2,3]。

贪心算法: 

那么本题的解题步骤为:

  • 第一步:将数组按照绝对值大小从大到小排序,注意要按照绝对值的大小
  • 第二步:从前向后遍历,遇到负数将其变为正数,同时K--
  • 第三步:如果K还大于0,那么反复转变数值最小的元素,将K用完
  • 第四步:求和
class Solution {
//自定义一个函数实现绝对值大小的排序且是倒序
static bool cmp(int a,int b){return abs(a) > abs(b);
}
public:int largestSumAfterKNegations(vector<int>& nums, int k) {sort(nums.begin(),nums.end(),cmp);//按照该规则进行排序//这样倒序的排序就可以首先去实现反转数值绝对值最大的数,因为本题是可以对一个数进行多次操作,所以正数就没必要进行多次反转,负数需要全部反转for(int i = 0;i < nums.size();i++){//循环判断条件,且K次反转if(nums[i] < 0 && k > 0){nums[i] *= -1;//把数字反转k--;//反转次数}}//当全部负数转换为正数,正数情况下对最后一个数进行多次的反转if(k % 2 == 1)nums[nums.size()-1] *= -1;int result = 0;for(int a : nums){result += a;//加和操作}return result;}
};
  • 时间复杂度: O(nlogn)
  • 空间复杂度: O(1)

4.加油站(134题)

题目描述:

在一条环路上有 N 个加油站,其中第 i 个加油站有汽油 gas[i] 升。

你有一辆油箱容量无限的的汽车,从第 i 个加油站开往第 i+1 个加油站需要消耗汽油 cost[i] 升。你从其中的一个加油站出发,开始时油箱为空。

如果你可以绕环路行驶一周,则返回出发时加油站的编号,否则返回 -1。

说明:

  • 如果题目有解,该答案即为唯一答案。
  • 输入数组均为非空数组,且长度相同。
  • 输入数组中的元素均为非负数。

示例 1: 输入:

  • gas = [1,2,3,4,5]
  • cost = [3,4,5,1,2]

输出: 3 解释:

  • 从 3 号加油站(索引为 3 处)出发,可获得 4 升汽油。此时油箱有 = 0 + 4 = 4 升汽油
  • 开往 4 号加油站,此时油箱有 4 - 1 + 5 = 8 升汽油
  • 开往 0 号加油站,此时油箱有 8 - 2 + 1 = 7 升汽油
  • 开往 1 号加油站,此时油箱有 7 - 3 + 2 = 6 升汽油
  • 开往 2 号加油站,此时油箱有 6 - 4 + 3 = 5 升汽油
  • 开往 3 号加油站,你需要消耗 5 升汽油,正好足够你返回到 3 号加油站。
  • 因此,3 可为起始索引。

暴力写法:for循环适合模拟从头到尾的遍历,而while循环适合模拟环形遍历,要善于使用while,注意下标就可以实现

class Solution {
public:int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {for(int i = 0;i < cost.size();i++){int rest = gas[i] - cost[i];//剩余汽int index = (i+1)%cost.size();//这里的下标是用于一个取余的操作来实现,注意i+1是下一个坐标开始记录//这里的循环条件汽有剩余并且下标不为iwhile(rest > 0 && index != i){rest += gas[index] - cost[index];//下标的剩余汽加入剩余汽index = (index+1)%cost.size();//更新下标}if(rest >= 0 && index == i)return i;//最后判断如果满足条件我们返回下标位置}return -1;}
};
  • 时间复杂度:O(n^2)
  • 空间复杂度:O(1)

 回溯算法:

class Solution {
public:int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {int cursum = 0;int min = INT_MAX;for(int i = 0;i < gas.size();i++){int rest = gas[i] - cost[i];cursum += rest;if(cursum < min){min = cursum;}}if(cursum < 0)return -1;if(min >= 0)return 0;for(int i = gas.size() - 1;i > 0;i--){int rest = gas[i] - cost[i];min += rest;if(min >= 0){return i;}}return -1;}
};
  • 时间复杂度:O(n)
  • 空间复杂度:O(1)

贪心方法:

局部最优:当前累加rest[i]的和curSum一旦小于0,起始位置至少要是i+1,因为从i之前开始一定不行。全局最优:找到可以跑一圈的起始位置。 

class Solution {
public:int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {int cursum = 0;//当前总和int totalsum = 0;//总和int start = 0;//下标开始位置//遍历汽站for(int i = 0;i < gas.size();i++){cursum += gas[i] - cost[i];//当前汽和消耗之间差的总和totalsum += gas[i] - cost[i];//总和//如果当前总和小于0,抛弃,从下一个位置开始重新开始if(cursum < 0){start = i + 1;cursum = 0;//将该值归0}}//最后判断条件,如果小于0,返回-1if(totalsum < 0)return -1;return start;}
};
  • 时间复杂度:O(n)
  • 空间复杂度:O(1)

5.分发糖果(135题)

题目描述: 

老师想给孩子们分发糖果,有 N 个孩子站成了一条直线,老师会根据每个孩子的表现,预先给他们评分。

你需要按照以下要求,帮助老师给这些孩子分发糖果:

  • 每个孩子至少分配到 1 个糖果。
  • 相邻的孩子中,评分高的孩子必须获得更多的糖果。

那么这样下来,老师至少需要准备多少颗糖果呢?

示例 1:

  • 输入: [1,0,2]
  • 输出: 5
  • 解释: 你可以分别给这三个孩子分发 2、1、2 颗糖果。

确定右边评分大于左边的情况(也就是从前向后遍历),

此时局部最优:只要右边评分比左边大,右边的孩子就多一个糖果,全局最优:相邻的孩子中,评分高的右孩子获得比左边孩子更多的糖果

局部最优可以推出全局最优。

如果ratings[i] > ratings[i - 1] 那么[i]的糖 一定要比[i - 1]的糖多一个,所以贪心:candyVec[i] = candyVec[i - 1] + 1

 确定左孩子大于右孩子的情况一定要从后向前遍历

如果 ratings[i] > ratings[i + 1],此时candyVec[i](第i个小孩的糖果数量)就有两个选择了,一个是candyVec[i + 1] + 1(从右边这个加1得到的糖果数量),一个是candyVec[i](之前比较右孩子大于左孩子得到的糖果数量)。

那么又要贪心了,局部最优:取candyVec[i + 1] + 1 和 candyVec[i] 最大的糖果数量,保证第i个小孩的糖果数量既大于左边的也大于右边的。全局最优:相邻的孩子中,评分高的孩子获得更多的糖果。

局部最优可以推出全局最优。

所以就取candyVec[i + 1] + 1 和 candyVec[i] 最大的糖果数量,candyVec[i]只有取最大的才能既保持对左边candyVec[i - 1]的糖果多,也比右边candyVec[i + 1]的糖果多

class Solution {
public:int candy(vector<int>& ratings) {vector<int>candyvec(ratings.size(),1);//这里我们定义一个数组来接收糖果//从前向后遍历,注意i的遍历位置for(int i = 1;i < ratings.size();i++){if(ratings[i] > ratings[i - 1])candyvec[i] = candyvec[i-1]+1;//右边孩子大于左边孩子,糖果+1}//从后向前遍历,注意i的位置for(int i = ratings.size() - 2;i >= 0;i--){if(ratings[i] > ratings[i+1])candyvec[i] = max(candyvec[i+1]+1,candyvec[i]);//左边孩子大于右边孩子,且做一个操作取两次遍历的最大值}int result = 0;//结果for(int i = 0;i < candyvec.size();i++){result += candyvec[i];//我们结算整个糖果的和}return result;}
};
  • 时间复杂度: O(n)
  • 空间复杂度: O(n)

6.柠檬水找零( 860题)

题目描述:

在柠檬水摊上,每一杯柠檬水的售价为 5 美元。

顾客排队购买你的产品,(按账单 bills 支付的顺序)一次购买一杯。

每位顾客只买一杯柠檬水,然后向你付 5 美元、10 美元或 20 美元。你必须给每个顾客正确找零,也就是说净交易是每位顾客向你支付 5 美元。

注意,一开始你手头没有任何零钱。

如果你能给每位顾客正确找零,返回 true ,否则返回 false 。

示例 1:

  • 输入:[5,5,5,10,20]
  • 输出:true
  • 解释:
    • 前 3 位顾客那里,我们按顺序收取 3 张 5 美元的钞票。
    • 第 4 位顾客那里,我们收取一张 10 美元的钞票,并返还 5 美元。
    • 第 5 位顾客那里,我们找还一张 10 美元的钞票和一张 5 美元的钞票。
    • 由于所有客户都得到了正确的找零,所以我们输出 true。

贪心算法:

只需要维护三种金额的数量,5,10和20。

有如下三种情况:

  • 情况一:账单是5,直接收下。
  • 情况二:账单是10,消耗一个5,增加一个10
  • 情况三:账单是20,优先消耗一个10和一个5,如果不够,再消耗三个5
class Solution {
public:bool lemonadeChange(vector<int>& bills) {int five = 0,ten = 0,twenty = 0;for(int bill : bills){if(bill == 5)five++;if(bill == 10){if(five <= 0)return false;ten++,five--;}if(bill == 20){//优先消耗10和5组合if(ten > 0 && five > 0){ten--;five--;twenty++;}else if(five >=3){five -=3;//消耗三张5twenty++;}else{return false;}}}return true;}
};
  • 时间复杂度: O(n)
  • 空间复杂度: O(1)

总结: 

跳跃游戏:此题就是看跳跃范围是否能覆盖终点,每次移动一个单位就更新一个最大覆盖范围即可,i 每次移动只能在 cover 的范围内移动,每移动一个元素,cover 得到该元素数值(新的覆盖范围)的补充,让 i 继续移动下去。cover 每次只取 max(该元素数值补充后的范围, cover 本身范围)如果 cover 大于等于了终点下标,直接 return true 就可以了

跳跃游戏II:要从覆盖范围出发,不管怎么跳,覆盖范围内一定是可以跳到的,以最小的步数增加覆盖范围,覆盖范围一旦覆盖了终点,得到的就是最少步数!需要统计两个覆盖范围,当前这一步的最大覆盖和下一步最大覆盖 ,需要定义两个覆盖范围,当前覆盖范围和下一个覆盖范围,我们首先需要更新下一个最大覆盖范围,然后按照当前覆盖范围来确定后续,更新当前覆盖范围和结果,满足条件就跳出。

K次取反后最大化的数组和:首先按照绝对值大小来把数组排序,按照从大到小排序,因为要最大和,所以k首先需要反转绝对值大的负数,我们也方便从前向后遍历,我们首先消耗负数,剩余的K去消耗,这个可以多次对一个数进行操作,对k剩下数值判断如果奇数,反转最后一个数,然后得到结果

加油站:暴力的写法就是需要从两个循环来实现,一层循环来遍历整个数组,定义一个初始位置不变,另一个循环去遍历元素,看是否可以回到之前位置,贪心定义两个变量,一个是当前的总和,另一个是总和,如果当前总和小于0,我们从该下标+1处开始下一次遍历,记录下标,如果总和>=0则可以走一圈,返回开始下标就可以

分发糖果:这题我们需要先根据从从左向右遍历,其实需要考虑两个方向的大小都需要满足,所以需要从左向右遍历根据题目条件来规定糖果数量,再根据从右向左遍历根据条件来计算糖果,选择更多的糖果。要注意遍历的开始范围,

柠檬水找零:单纯模拟题,定义三个变量5,10,20,如果只有一张5,直接记录变量,如果是10,我们需要对5--,10++操作,如果20块,则首先考虑一张10和一张5组合,第二种情况是三张5,如果前两种情况都不满足则返回错误,简单题秒了。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【前端基础--4】

定位属性 position 可以将元素定位到你想要放到位置&#xff0c;使用方位值来进行移动(top,left,right,bottom)。 1.相对定位 position: relative; top: 20px; left: 20px; 以自身为定点进行移动&#xff0c;不会脱离文档流。 不会影响元素本身的性质&#xff1b;块级…

性能优化-OpenCL 介绍

「发表于知乎专栏《移动端算法优化》」 本文首先对 GPU 进行了概述&#xff0c;然后着重地对移动端的 GPU 进行了分析&#xff0c;随后我们又详细地介绍了 OpenCL 的背景知识和 OpenCL 的四大编程模型。希望能帮助大家更好地进行移动端高性能代码的开发。 &#x1f3ac;个人简介…

RisingWave 多流 Join 实现高效实时数据打宽

在公司业务场景中&#xff0c;通常需要使用流计算引擎从多个数据源获取数据、进行 ETL 操作&#xff0c;并将清洗后的数据导入到数据分析系统或数据湖中。由于最后产生出来的表是一张宽表&#xff0c;我们通常也称这个过程为“数据打宽“。 数据打宽在流处理系统中对应的操作便…

34.在排序数组中查找元素的第一个和最后一个位置(力扣LeetCode)

文章目录 34.在排序数组中查找元素的第一个和最后一个位置题目描述二分 34.在排序数组中查找元素的第一个和最后一个位置 题目描述 给你一个按照非递减顺序排列的整数数组 nums&#xff0c;和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。 如果数组…

将小游戏打包成单一exe文件的原理及应用

哈喽大家好&#xff0c;我是咕噜美乐蒂&#xff0c;很高兴又见面啦&#xff01;本文将介绍将小游戏原版文件打包成单一exe文件的原理&#xff0c;并探讨了这种打包方式的优点和应用场景。通过对打包工具和编译器的介绍&#xff0c;解释了它们如何将游戏的所有相关文件合并到一个…

Fiddler 过滤地址设置及导出JMeter脚本插件原理

Fiddler 过滤地址设置 1、在fiddler右边工具栏中找到Filters过滤器->勾选&#xff1a;Use Filters->在hosts中选择过滤规则和要过滤的地址。 如下图&#xff1a; 2、点击【Actions】按钮&#xff0c;选择&#xff1a;Run Filterset now&#xff0c;就会立即生效&#x…

nginx限制ip访问

先看一下被禁止的效果 如何配置 禁止访问的话直接在location模块增加类似如下配置 deny all; 完整示例 location / {deny all;root html;index index.html index.htm;} 默认是allow all就是允许所有ip访问,如果只配置指定ip可以访问是无效的,还是所有的ip可以访问 无效示例…

Unity动画桢事件

1&#xff0c;使用原因 在新项目内部审核的时候&#xff0c;说什么动画节奏不匹配&#xff0c;所以决定用动画桢事件来处理技能释放。当释放技能的时候&#xff0c;先播放技能动画&#xff0c;然后再动画桢所在的时间戳执行技能的逻辑。 2&#xff0c;具体实现 1&#xff0c;…

v3+ECharts 地图实现多个自定义图片的图标

备注&#xff1a;地图json, 图标图片 准备替换下 <template><div ref"mapEcharts" class"map-echart"></div> </template><script setup langts> import * as echarts from echarts import mapcity from "/tool/map/m…

【SpringBoot篇】springboot的自动装配原理

文章目录 &#x1f3f3;️‍&#x1f308;什么是自动装配&#x1f339;SpringBoot的自动装配&#x1f354;具体操作 &#x1f3f3;️‍&#x1f308;什么是自动装配 自动装配&#xff08;Auto-Configuration&#xff09;是Spring Boot框架的一个核心特性之一&#xff0c;它通过…

走进课本中的知名景区,猿辅导带你“读课文 游中国”

课本里&#xff0c;你能“看见”的世界不止一面&#xff0c;一座山川&#xff0c;崇高肃穆下孕育出千年的文明&#xff1b;一座桥梁&#xff0c;连接起时间的变迁和技术的传承&#xff1b;一座古楼&#xff0c;飞檐斗拱间凝聚着中国文化的精髓。近日&#xff0c;猿辅导素养课宣…

2024年新提出的算法:一种新的基于数学的优化算法——牛顿-拉夫森优化算法|Newton-Raphson-based optimizer,NRBO

1、简介 开发了一种新的元启发式算法——Newton-Raphson-Based优化器&#xff08;NRBO&#xff09;。NRBO受到Newton-Raphson方法的启发&#xff0c;它使用两个规则&#xff1a;Newton-Raphson搜索规则&#xff08;NRSR&#xff09;和Trap Avoidance算子&#xff08;TAO&#…

光电耦合隔离器在医疗行业的应用全面分析

光电耦合隔离器作为一种关键的电子元件&#xff0c;在医疗行业中扮演着重要的角色。光电耦合隔离器通过将电信号转换为光信号&#xff0c;并在隔离通道中传输&#xff0c;能够有效地隔离输入和输出端&#xff0c;保证医疗设备在电气上的安全性。 光电耦合隔离器的医疗应用领域 …

简述云原生基础定义及关键技术

云原生是什么 云原生是面向“云”而设计的应用,因此技术部分依赖于传统云计算的 3 层概念,基础设施即服务(IaaS)、平台即服务(PaaS)和软件即服务(SaaS)。 例如,敏捷的不可变基础设施交付类似于 IaaS,用来提供计算网络存储等基础资源,这些资源是可编程且不可变的,直…

【VBA代码解决方案】md文档转Word后,全自动转换为标准的Word公式格式

【VBA解决方案】全自动将Word中的文本公式转换为标准公式 写在最前面VBA代码全自动方法将md文档导出为word代码如何运行VBA代码注意事项 一些如何实现的回忆记录步骤解析手动将文本转换为Word公式代码逻辑步骤设想代码解析代码解释总结 其他背景介绍应用场景VBA脚本介绍如何使用…

C#中的HashSet

在C#中&#xff0c;HashSet是一种集合类型&#xff0c;它实现了哈希表的数据结构。HashSet用于存储一组唯一的对象&#xff0c;并提供高效的查找、插入和删除操作。 HashSet的主要特点如下&#xff1a; 唯一性&#xff1a;HashSet中的元素是唯一的&#xff0c;不允许重复的元素…

Adb windows脚本

#由于测试不太会用adb指令 所以自己写了一个adb的脚本&#xff0c;简化adb的使用 echo off set SAVE_DIRD:\screen if not exist "%SAVE_DIR%" ( mkdir "%SAVE_DIR%" echo %SAVE_DIR%创建成功 )set ADB_PATHD:\SDK\sdk\platform-tools\adb.exeif &quo…

【原创教程】轻松搞定川崎机器人调试

1、确定川崎机器人坐标系 机器人坐标系一般有以下四种&#xff1a;世界坐标&#xff08;大地坐标&#xff09;、基坐标、工具坐标、工件坐标。在前面的文章中&#xff0c;我们已经有所阐述&#xff0c;大家可以参考前面文章。 下面川崎以工具坐标系为例&#xff08;现场川崎机…

python二进制读取文件快速定位及半个中文字符问题处理

1. 打开文件快速定位读取的方法 在Python中&#xff0c;可以使用open()函数来打开文件。要快速定位到特定的字节位置并读取或写入内容&#xff0c;可以结合使用seek()方法。 下面是一个示例代码&#xff0c;展示了如何使用seek()方法在二进制模式下打开文件并快速定位到指定位…

4.Doris数据导入导出

导入 Doris支持多种数据源导入如S3 HDFS Kafka 本地文件 Binlog 等 官方文档 导入的原子性保证 Doris 的每一个导入作业&#xff0c;不论是使用 Broker Load 进行批量导入&#xff0c;还是使用 INSERT 语句进行单条导入&#xff0c;都是一个完整的事务操作。导入事务可以保证…