当前位置: 首页 > news >正文

代码随想录算法训练营第二十七天 | 122.买卖股票的最佳时机Ⅱ 55.跳跃游戏 45.跳跃游戏Ⅱ 1005.K次取反后最大化的数组和

LeetCode 122.买卖股票的最佳时机Ⅱ:

文章链接
题目链接:122。买卖股票的最佳时机Ⅱ

思路:

贪心:
不断尝试买进的时间。因为某一次买入卖出所得的利润可以分解为其中一天买进次日卖出,并不断进行累积的利润。
在这里插入图片描述
假设当前买进了,如果下一天价格比当前高,那么就真的买进卖出;
如果下一天价格比当前低,那么就当作当前没有买,那么就是假的买进卖出。
在这里插入图片描述
也就是只累积次日价格 > 当日价格,即正利润

class Solution:def maxProfit(self, prices: List[int]) -> int:lenPrices = len(prices)if lenPrices == 1:return 0pre, cur = prices[0], 0   # pre为假设买入的价格,cur为卖出的价格profit = 0  # 利润for i in range(1, lenPrices):cur = prices[i]if cur > pre:   # 只有当cur > pre,才真正买进卖出profit += cur - prepre = cur   # 不断买进卖出return profit"""
换一种写法
"""
class Solution:def maxProfit(self, prices: List[int]) -> int:lenPrices = len(prices)if lenPrices == 1:return 0profit = 0for i in range(1, lenPrices):profit += max(prices[i] - prices[i-1], 0)return profit

感悟:

逐天累积利润,且只累积正利润


LeetCode 55.跳跃游戏:

文章链接
题目链接:55.跳跃游戏

思路:

使用跳跃能到的最大覆盖范围,如果范围包括了最后一个下标,那么能够达到最后一个下标,否则不能达到。
最大覆盖范围cover = max(i+nums[i], cover)
逐个遍历数组中的元素,i 的范围为[0, cover],每次更新cover,如果cover >= len(nums) - 1,说明能够到达最后一个下标;如果 i 遍历完成,说明cover不能达到最后一个下标
在这里插入图片描述

class Solution:def canJump(self, nums: List[int]) -> bool:cover = 0i = 0while i <= cover:cover = max(i + nums[i], cover)if cover >= len(nums) - 1:  # 最大覆盖范围覆盖了最后一个下标return Truei += 1return False  

感悟:

转换跳跃距离为最大覆盖范围,从而能否跳到最后一个下标从怎么跳跃转换为了最大覆盖范围能否包括最后一个下标。
同时注意遍历数组的 i 范围为[0, cover],且为逐个遍历


LeetCode 45.跳跃游戏Ⅱ:

文章链接
题目链接:45.跳跃游戏Ⅱ

思路:

还是使用最大覆盖范围,不过因为要统计步数,因此要区分当前步数的最大覆盖范围和下一步的最大覆盖范围。
在这里插入图片描述

1)要想要跳的步数最少,那么在当前覆盖范围固定且不能到达数组最后一个位置时,需要下一步的覆盖范围最大。因此遍历当前覆盖时,使用max求下一步最大覆盖范围。
2)什么时候更新curcover:如果当前覆盖范围没能到达数组最后一个位置(可以设置成遍历时的当前覆盖范围是到不了最后一个位置的,通过初始化和排除只有一个元素的数组达到),且 i 已经遍历到了当前覆盖范围的最后一个位置,也就是下一步最大覆盖范围求出来了,那么就需要更新当前覆盖范围,同时增加步数,也就是跳一步。
3)什么时候跳出循环更新当前覆盖范围之后,判断更新后的当前覆盖范围是否包含数组最后一个元素(因为 i 遍历时的当前覆盖范围不包含数组最后一个元素)。如果包含,跳出循环;否则进行下一轮求下一步最大覆盖范围。
PS:由上图可知,curcover更新的次数,也就是最少步数

class Solution:def jump(self, nums: List[int]) -> int:lenNums = len(nums)if lenNums == 1:	# 排除了只有一个元素的数组return 0# curcover是当前覆盖范围,nextcover是下一次跳跃能覆盖的范围# curcover从0开始,也就是最开始的curcover只有nums[0]curcover, nextcover = 0, 0 jumpStep = 0    # 跳跃步数,也就是cover更新的次数i = 0# 遍历curcover时,curcover不包含数组最后一个位置for i in range(lenNums):nextcover =  max(nextcover, i + nums[i])if i == curcover:   # 更新覆盖范围jumpStep += 1   # 步数 + 1curcover = nextcover# 更新后的范围是否包括最后一个位置if curcover >= lenNums - 1:break   return jump

感悟:

当前最大覆盖范围和下一步最大覆盖范围,以及更新当前最大覆盖范围的次数也就是最少跳跃步数,在 i 遍历到当前最大覆盖范围最后一个元素时,更新curcover;i 遍历时的curcover默认不包含数组最后一个元素(因为curcover最开始只有nums[0],为了保证curcover不包含数组最后一个元素,因此将只有一个元素的数组单独判断)


LeetCode 1005.K次取反后最大化的数组和:

文章链接
题目链接

思路:

贪心的思路为:
第一次局部最优:首先对nums中的负数中的最小值进行取反,得到新的负数集合,再对新的负数集合中的最小值进行取反
如果还有取反次数,此时nums为非负整数数组
第二层局部最优:不断对nums的最小值进行取反

"""
方法1:直接按照思路来
首先对数组进行排序,得到顺序数组
然后不断对数组的前面负数进行取反
如果还有取反次数,对新的nums数组中的最小值进行取反但是需要注意的细节很多
1. 负数取反时,while条件为 i <= len(nums) - 1 and nums[i] < 0 and k > 0,
因为会出现nums全为负数且k > len(nums)的情况,所以要加 i <= len(nums) - 1
2. 如果还有取反次数,对新nums数组中的最小值进行取反,此时的最小值在数组中间
2.1 如果开始nums有正数也有负数,那么最小值为min(nums[i - 1], nums[i]),但是因为是对数组元素进行取反,所以要分开判断取反(记得加 i > 0条件)
2.2 如果nums只有负数,那么此时 k > len(nums),while循环后i = len(nums),此时最小值为nums[i - 1]
2.2 如果nums只有正数,由于i初始化为0,因此最小值为nums[i] 
"""
class Solution:def largestSumAfterKNegations(self, nums: List[int], k: int) -> int:nums.sort()i = 0lenNums = len(nums)while i <= lenNums - 1 and nums[i] < 0 and k > 0:    # 取反负数nums[i] = -nums[i]i += 1k -= 1if k > 0:   # 还有剩余的取反次数# 如果nums为全负数,那么while之后i = lenNums,最小值为nums[i - 1]# 如果nums部分负数部分正数,如果最小值为nums[i - 1],那么条件为i > 0 and nums[i - 1] < nums[i]if i == lenNums or (i > 0 and nums[i - 1] < nums[i]):nums[i - 1] = nums[i - 1] if k % 2 == 0 else -nums[i - 1]else:nums[i] = nums[i] if k % 2 == 0 else -nums[i] # 求数组最大和sumNums = 0for i in range(len(nums)):sumNums += nums[i]return sumNums"""
方法2:方法1的优化,或者说没有方法1直接
1. **对数组按照绝对值逆序排序**
2. 对负数求反,遍历数组,如果k > 0且nums[i]为负数,求反
3. 如果还有取反次数,对最小值不断取反,即对nums[-1]不断取反
(此时最小值只为nums[-1],与方法1的区别)
4. 求k次取反后数组的和可以在取反过程中得到,对负数取反遍历数组时记录数组和sumNums,
如果还有取反次数且取反次数为奇数,那么sumNums += nums[-1] * 2,其中nums[-1]是取反后的
"""
class Solution:def largestSumAfterKNegations(self, nums: List[int], k: int) -> int:nums.sort(key=lambda x: abs(x), reverse=True)   # 按绝对值反向排序sumNums = 0for i in range(len(nums)):if nums[i] < 0 and k > 0:   # 遇到负数取反nums[i] *= -1k -= 1sumNums += nums[i]  # 记录数组和if k % 2 == 1:  # 如果还有取反次数nums[-1] *= -1sumNums += nums[-1] * 2return sumNums

感悟:

  1. 需要清楚贪心的两次局部最优,以及方法1中的细节,对负数求反时注意不要超界,剩下取反次数对最小值取反时,什么时候最小值为num[i - 1],即开始nums全为负数,或 i > 0 and nums[i - 1] < nums[i]
  2. 方法2求k次取反后的数组和,在还有取反次数对正数取反时,如果剩下的取反次数为奇数,那么sumNums -= 2*abs( nums[-1]),(减去的两个:一个是前面对负数取反时加的abs(nums[-1]),一个是对nums[-1]取反后要减去的nums[-1])

学习收获:

买卖股票的最佳时机,以两天为周期,第一天买入,第二天卖出,只记录正利润
跳跃游戏:最大覆盖范围,如果要求最小步数,那么最大覆盖范围要拆分为当前curcover和下一步nextcover,且curcover不能到达数组最后一个位置
K次取反:方法1的细节,方法2对数组按照绝对值逆序排序可以去掉方法1中需要注意的细节


http://www.mrgr.cn/news/58919.html

相关文章:

  • Spring Boot实现的动态化酒店住宿管理系统
  • 分支限界法(Branch and Bound)详细解读
  • 雷池社区版那么火,为什么站长都使用雷池社区版??
  • 水轮发电机油压自动化控制系统解决方案介绍
  • IO多路复用小项目day01 ———— c语言版本
  • P1443 马的遍历
  • Web环境下的Spring Boot酒店房间预订系统
  • [答疑]是不是互联网更适合用DDD
  • 从零开始:构建一个高效的开源管理系统——使用 React 和 Ruoyi-Vue-Plus 的实战指南
  • Spring Boot驱动的Web版酒店客房管理系统
  • 项目需要,写了一个取出8位变量的2bit数据,引发了思考!
  • 「漏洞复现」JEPaaS 低代码平台 j_spring_security_check SQL注入漏洞
  • Element 的Table表格实现列合并(记得先排序、element-plus、列合并、线上已投入使用)
  • 信息安全工程师(72)网络安全风险评估概述
  • Java Web 开发:构建动态与交互式Web应用的基石
  • R语言机器学习算法实战系列(十四): CatBoost分类算法+SHAP值 (categorical data gradient boosting)
  • vscode配色主题与图标库推荐
  • 本地缓存库分析(一):golang-lru
  • 厨艺交流平台:Spring Boot技术实践案例
  • 最佳B站视频下载工具 完全免费+支持8k画质!
  • Hadoop:yarn的Rust API接口
  • Nodejs使用pkg打包为可执行文件
  • 检索增强型生成模型RichRAG:为多面查询提供丰富回应
  • 【Nginx系列】关于一次请求超时的思考
  • Java CompletableFuture
  • 计算机网络——开放系统互连参考模型