算法力扣刷题记录 八十八【122.买卖股票的最佳时机 II】

news2024/11/27 5:42:51

前言

在本文之前,纠正了回溯章节中去重方式和排序之间的关系,可以去:记录 八十【491.递增子序列及去重方式——排序关系】中回顾。

贪心章节第5篇。动态规划第11篇。记录 八十八【122.买卖股票的最佳时机 II】


一、题目阅读

给你一个整数数组 prices ,其中 prices[i] 表示某支股票第 i 天的价格。

在每一天,你可以决定是否购买和/或出售股票。你在任何时候 最多 只能持有 一股 股票。你也可以先购买,然后在 同一天 出售。

返回 你能获得的 最大 利润 。

示例 1:

输入:prices = [7,1,5,3,6,4]
输出:7
解释:在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5 - 1 = 4。
随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6 - 3 = 3。
最大总利润为 4 + 3 = 7 。

示例 2:

输入:prices = [1,2,3,4,5]
输出:4
解释:在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5 - 1 = 4。
最大总利润为 4 。

示例 3:

输入:prices = [7,6,4,3,1]
输出:0
解释:在这种情况下, 交易无法获得正利润,所以不参与交易可以获得最大利润,最大利润为 0。

提示:

1 <= prices.length <= 3 * 10^4
0 <= prices[i] <= 10^4

二、尝试实现

2.1 分析题目,确定方法

  1. 题目要求最大利润。虽然可以同一天购买,同一天出售,但是这个利润是0。所以先不考虑同一天买同一天卖这种情况;

  2. 分析示例,确定如何获得最大值:启发——记录 七十九【376. 摆动序列】,所以才想到改成折线图看看
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

  3. 所以:贪心算法可以解决。

  • 局部最优:每个单调递增坡的最低点买入,最高点卖出;一个递增坡获得最大利润
  • 全局最优:所有的递增坡获得整体的最大利润。

2.2 贪心思路实现

  1. 沿用记录 七十九【376. 摆动序列】的思路和细节进行处理;用pridiff和curdiff表示递增还是递减。
  2. 定义 int pay = 0;代表股票买入的价格。int sale = 0;表示股票卖出的价格。
  3. 以示例1,较为规律的序列
  • if(pridiff < 0 && curdiff > 0) ,代表一个单调递增坡的起点,应该买入。类似节点1,节点3;

  • if(pridiff > 0 && curdiff < 0),代表一个单调递增坡的终点,应该卖出。类似节点5,节点6;

  • for循环:从0开始到prices.size()-1结束;

  • pridiff初始为0;curdiff = prices[i+1]-prices[i];。

  • 同时result代表全局的最大利润,应该求和每一个单调递增坡的最大利润。result += sale-pay;

  • 所以这种情况下:代码如下——

    class Solution {
    public:
        int maxProfit(vector<int>& prices) {
            int result = 0;
            int pridiff = 0;
            int curdiff = 0;
            int pay = 0;//购入的价格
            int sale = 0;//出售的价格
            for(int i = 0;i < prices.size()-1;i++){
                curdiff = prices[i+1]-prices[i];
                if(pridiff < 0 && curdiff > 0) {
                    pay = prices[i];
                }
                if(pridiff > 0 && curdiff < 0){
                    sale = prices[i];
                    result += sale-pay; //求和每次买卖的价格
                }
                pridiff = curdiff;
            }
            
            return result;
        }
    };
    
  1. 补充情况一:起点和终点如何处理
  • 在记录 七十九【376. 摆动序列】起点构造了一个平坡,pridiff = 0。在这里起点应该构造一个下降的坡。理由如下:
    在这里插入图片描述
  • 终点处理:扩展for循环到prices.size()-1停止。终止的curdiff = -1,表示终点是一个单调坡的最高点。理由如下:
    在这里插入图片描述
  • 所以:起点和重点的处理,使得始终单调递增(示例2)和始终单调递减(示例3)得以解决,所以代码更改:
    • pridiff = -1;初始化。//构造起点的下降坡
    • for循环范围到i < prices.size();
    • if(i == prices.size()-1) curdiff = -1;//构造终点的下降坡
    class Solution {
    public:
        int maxProfit(vector<int>& prices) {
            int result = 0;
            int pridiff = -1;//构造起点的下降坡
            int curdiff = 0;
            int pay = 0;//购入的价格
            int sale = 0;//出售的价格
            for(int i = 0;i < prices.size();i++){
                if(i == prices.size()-1) {
                    curdiff = -1;//构造终点的下降坡
                }else{
                    curdiff = prices[i+1]-prices[i];
                }
                if(pridiff < 0 && curdiff > 0) {
                    pay = prices[i];
                }
                if(pridiff > 0 && curdiff < 0){
                    sale = prices[i];
                    result += sale-pay; //求和每次买卖的价格
                }
                pridiff = curdiff;
            }
            
            return result;
        }
    };
    
  1. 补充情况二:如果遇到平坡怎么办?以平坡的最后一个元素作为买/卖的节点。
  • 所以两个pridiff和curdiff的判断条件改成:

  • if(pridiff <= 0 && curdiff > 0) 和 if(pridiff >= 0 && curdiff < 0)
    在这里插入图片描述
    所以代码改变:

    class Solution {
    public:
        int maxProfit(vector<int>& prices) {
            int result = 0;
            int pridiff = -1;//构造起点的下降坡
            int curdiff = 0;
            int pay = 0;//购入的价格
            int sale = 0;//出售的价格
            for(int i = 0;i < prices.size();i++){
                if(i == prices.size()-1) {
                    curdiff = -1;//构造终点的下降坡
                }else{
                    curdiff = prices[i+1]-prices[i];
                }
                if(pridiff <= 0 && curdiff > 0) {
                    pay = prices[i];
                }
                if(pridiff >= 0 && curdiff < 0){
                    sale = prices[i];
                    result += sale-pay; //求和每次买卖的价格
                }
                pridiff = curdiff;
            }
            
            return result;
        }
    };
    
  1. 补充情况三:单调坡中有平坡怎么办?所以pridiff = curdiff;不应该每个节点都更新。
  • 更新pay时,更新pridiff;更新sale时,更新pridiff。

  • 所以pridiff = curdiff;放到两个if条件里面。
    在这里插入图片描述
    代码更新:

    class Solution {
    public:
        int maxProfit(vector<int>& prices) {
            int result = 0;
            int pridiff = -1;
            int curdiff = 0;
            int pay = 0;//购入的价格
            int sale = 0;//出售的价格
            for(int i = 0;i < prices.size();i++){
                if(i == prices.size()-1) {
                    curdiff = -1;
                }else{
                    curdiff = prices[i+1]-prices[i];
                }
                if(pridiff <= 0 && curdiff > 0) {
                    pay = prices[i];
                    pridiff = curdiff;
                }
                if(pridiff >= 0 && curdiff < 0){
                    sale = prices[i];
                    result += sale-pay;
                    pridiff = curdiff;
                }
                
            }
            
            return result;
        }
    };
    

2.3 代码实现【贪心算法】

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int result = 0;
        int pridiff = -1;
        int curdiff = 0;
        int pay = 0;//购入的价格
        int sale = 0;//出售的价格
        for(int i = 0;i < prices.size();i++){
            if(i == prices.size()-1) {
                curdiff = -1;
            }else{
                curdiff = prices[i+1]-prices[i];
            }
            if(pridiff <= 0 && curdiff > 0) {
                pay = prices[i];
                pridiff = curdiff;
            }
            if(pridiff >= 0 && curdiff < 0){
                sale = prices[i];
                result += sale-pay;
                pridiff = curdiff;
            }
            
        }
        
        return result;
    }
};

三、参考学习

【122.买卖股票的最佳时机 II】 参考学习链接

3.1 【122.买卖股票的最佳时机 II】贪心思路

  1. 回到题目中:“在每一天,你可以决定是否购买 和/或出售股票。”——含义是:当天可以只买入或者只卖出或者既买入又卖出。很明显:同一天买入同一天卖出没有利润,因此至少需要两天完成一个交易。所以利润可以进行拆分
  2. 如何进行利润拆分?如下图:
    在这里插入图片描述
  3. 总结:
  • 个人思路:从记录 七十九【376. 摆动序列】中获得区间单调坡的概念,所以使用区间的利润可以找到一个最低点买入,最高点卖出。但是情况思考比较多,处理细节也繁琐。
  • 参考思路:发现利润可以拆分,所以只搜集正数即可。

3.2 代码实现【贪心:参考思路】

边遍历边搜集利润中的正数。

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;
    }
};

3.3 动态规划思路

3.3.1思考

  1. 买卖股票系列是动态规划章节内容,但是贪心算法可以帮助一些题目巧妙解决,并不代表贪心可以解决所有买卖股票系列的题目。
  2. 那么尝试动态规划实现:
  3. 定义dp数组和下标:dp[i]代表在第i天卖出股票,所能获得的最大利润是dp[i]
  4. 递推公式:内层循环 j 遍历第i天之前的每一天,dp[i] = max(dp[i] , dp[j] + prices[i] - prices[j]);
    在这里插入图片描述
  5. 初始化:dp[0] = 0;在第0天卖出只能第0天买入,所以利润是0.
  6. 遍历顺序:从递推公式知道外层for循环从前往后。
  7. 返回值是dp数组中什么?所以dp数组的含义设定不正确。那么该如何设定dp数组

3.3.2 正确思路

  1. dp数组含义:
  • dp[i][0]:在第i天持有股票得到的现金dp[i][0];
  • dp[i][1]:在第i天不持有股票得到的现金dp[i][1];
  • 因为最多持股一个,所以当天要么有股票(1个),要么没有股票。
  • 所得现金 = 利润是一个累积值。返回值:最后一天不持有股票时所得现金,即dp[最后一个][1]
  1. 递推公式:
  • 求dp[i][0],在第i天持有股票,得到多少钱?
    • 第i天持有股票,要么第i-1天持有股票,今天继续持有不卖出,钱不增加不减少。所得现金:dp[i-1][0]。
    • 第i天持有股票,,要么第i-1天不持有股票,今天重新买入。钱在dp[i-1][1]的基础上减去买入的prices[i]。所得现金:dp[i-1][1] - prices[i]。
    • 取两者的最大值。dp[i][0] = max(dp[i-1][0],dp[i-1][1] - prices[i])
  • 求dp[i][1],在第i天不持有股票,得到多少钱?
    • 第i天不持有股票,要么第i-1天持有股票,今天以prices[i]卖出。钱在dp[i-1][0]基础上多了prices[i]。所得现金:dp[i-1][0] + prices[i];
    • 第i天不持有股票,要么第i-1天不持有股票,今天也不买入。钱依然是第i-1天不持有股票所得的钱。所以现金:dp[i-1][1]。
    • 取两者的最大值。dp[i][1] = max(dp[i-1][0] + prices[i],dp[i-1][1])
  1. 初始化:
  • dp[0][0] = -prices[0]。先买入,花掉了-prices[0]。
  • dp[0][1] = 0;相当于不买入。钱没花没赚。
  1. 遍历顺序:从递推公式看,从前往后。

3.4 代码实现【动态规划】

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int len = prices.size();
        //定义dp数组
        vector<vector<int>> dp(len,vector<int>(2,0));
        //初始化
        dp[0][0] = -prices[0];
        dp[0][1] = 0;
        //遍历,递推公式
        for(int i = 1;i <len;i++){
            dp[i][0] = max(dp[i-1][0],dp[i-1][1]- prices[i]);
            dp[i][1] = max(dp[i-1][0] + prices[i],dp[i-1][1]);
        }
        return dp[len-1][1];
    }
};

总结

本文:

  • 纠正回溯章节中去重方式和排序之间的关系:记录 八十【491.递增子序列及去重方式——排序关系】
  • 【122.买卖股票的最佳时机 II】三种方法实现:
    在这里插入图片描述
    (欢迎指正,转载标明出处)

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

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

相关文章

RestTemplate-源码阅读

参考资料&#xff1a;https://github.com/mercyblitz/java-training-camp/blob/main/stage-1/docs/04.%20%E7%AC%AC%E5%9B%9B%E8%8A%82%EF%BC%9AREST%20API%20%E5%AE%A2%E6%88%B7%E7%AB%AF%E8%AE%BE%E8%AE%A1.md RestTemplate 源码阅读 GET POST EXCHANGE 我们可以看到其实没…

web前端之vue+element+select实现多选、两个数组排序、保持源数据、查找索引、过滤、克隆、复制、findIndex、filter

MENU 前言效果图templatestyleJavaScript 前言 1、代码段是一个Vue组件的模板、脚本和样式的完整实现。它结合element的一些组件(如el-select和el-radio-group)&#xff0c;实现一个带有下拉选择、多选功能以及可选择“是/否”状态的组件。 2、组件实现一个复杂的选择和管理界面…

Redis之使用Java代码操作Redis

目录 配置Redis远程访问 安装Redis桌面管理工具 Java操作Redis 创建maven工程 案例演示 连接redis String字符串 Hash哈希 List列表 Set集合 Zset有序集合 配置Redis远程访问 修改访问IP地址 #跳转到redis安装目录 cd /usr/local/redis-6.2.1 #修改redis.con…

SOL项目开发代币DApp的基本要求、模式创建与海外宣发策略

Solana&#xff08;SOL&#xff09;作为一个高性能区块链平台&#xff0c;以其快速的交易速度和低交易成本吸引了大量开发者和投资者。基于Solana开发的去中心化应用程序&#xff08;DApp&#xff09;和代币项目正逐步成为区块链领域的重要组成部分。要成功开发并推广一个SOL项…

通过FHIR和EPIC进行对接

说明 Epic是全美最大电子病历供应商。 FHIR (Fast Health Interoperability Resources)旨在实现医疗健康信息的交换。其中包括了临床数据和健康相关的行政管理数据、公共卫生和临床研究数据。既包括了人类和兽医&#xff0c;又适合大多数应用场景——住院、慢病管理、社区护理…

windows平台的postgresql主从数据库流备份

主: 操作系统:windows10 数据库版本&#xff1a;postgresql-16.2 ip&#xff1a;192.168.3.254 从: 操作系统&#xff1a;windows10 数据库版本&#xff1a;postgresql-16.2 ip&#xff1a;192.168.3.253 配置主库 配置 pg_hba.conf 文件 在 pg 的安装目录下&#xff0c;找到 …

算法_字符串专题---持续更新

文章目录 前言最长公共前缀题目要求题目解析代码如下 最长回文子串题目要求题目解析代码如下 二进制求和题目要求题目解析 字符串相乘题目要求题目解析代码如下 前言 本文将会向你介绍有关字符串的相关题目&#xff1a;最长公共前缀、最长回文子串、二进制求和、字符串相乘。本…

CISAW安全集成和别的类型有什么区别

信息安全人员认证&#xff08;Certified Information Security Assurance Worker, CISAW&#xff09;是由中国网络安全审查技术认证中心依据信息安全领域的不同专业、技术和应用方向以及安全岗位需求&#xff0c;按照ISO/IEC 17024标准建立的多层次认证体系。 CISAW分为三个主…

web前端之选项卡的实现、动态添加类名、动态移除类名、动态添加样式、激活、tabBar

MENU 原生vue 原生 效果图 html 代码 <div class"card"><div class"tab_bar"><div class"item" onclick"handleTabBar(this)">tabBar1</div><div class"item" onclick"handleTabBar(this…

哈佛大学年中回顾2024年ESG发展近况

--- 兼谈新能源企业出海之ESG营销战略 2024年上半年&#xff0c;环境、社会和治理&#xff08;ESG&#xff09;问题以及对方法的不同意见继续成为全球头条新闻。今年年初&#xff0c;公司及其利益相关者在ESG的支持者和批评者之间进行了权衡&#xff0c;虽然近几个月ESG的势头…

cesium添加弹窗,跟随实体移动

先看效果&#xff0c;弹窗会跟随实体移动 1、首先我们先写一个弹窗样式&#xff0c;如果是vue开发&#xff0c;css最好写到公共引入的css &#xff0c;组件内css会编译后会加hash值 2、然后我们 开启 cesium的监听事件&#xff0c; 注意的是 initBubbleWindow方法在地球加载后…

控制阶段在DMAIC中的主要目标是什么?

在探讨DMAIC&#xff08;定义Define、测量Measure、分析Analyze、改进Improve、控制Control&#xff09;这一持续改进流程时&#xff0c;控制阶段作为整个循环的尾声&#xff0c;其重要性不言而喻。控制阶段的主要目标不仅在于巩固前期努力所取得的成果&#xff0c;更在于确保这…

EXCEL 分段排序--Excel难题#86

Excel某表格有3列。 ABC1A1B1512A2B27213A3B33824A4B495A5B5736A6B65777A7B7918A13B131509A14B144910A17B1770211A18B1870512A34B343313A35B3540914A36B3657915A37B3710 现在要求对表格按照第3列进行分段排序&#xff0c;由小到大排列。第1段&#xff1a;第3列小于等于50&…

UE5 datetime 创建日期时间节点 进行加法减法。个人理解

以下均为个人实验和个人理解&#xff0c;仅供参考。 目录 目标节点&#xff1a; 年月日 时分秒毫秒 目标节点&#xff1a; 年月日 年月日以1 为基底。若填的数字<0&#xff0c;该节点会失效。 试验&#xff1a; year基底为1&#xff0c;正常 year基底为0&#xff0c;异…

主流AI绘画工具StableDiffusion最新模型sd3本地部署方法(附工作流)

前言/introduction Stable Diffusion 3&#xff08;简称SD3&#xff09;是Stability AI最新推出的文本到图像生成模型。相比前代模型&#xff0c;SD3在生成质量、细节表现以及运行效率上有了显著提升&#xff0c;尤其在细腻的图像渲染和复杂的场景构建方面表现出色。SD3模型提…

【Harmony OS 4.0】页面路由跳转代码示例

ets/pages/Index.ets import router from ohos.router;Entry Component struct Index {State title: string Index Page;State message: string onPageShow(): void { // 页面每次显示时触发。使用aboutToAppear页面没反应。let record router.getParams() as Record<st…

微服务多个模块启动,端口被占用,yml配置文件读不到

刚刚提交到gitee自己的仓库&#xff0c;拉下来还是报错&#xff0c;然后看到一个解决方法&#xff1a; <build><resources><resource><directory>src/main/java</directory><includes><include>**/*.yml</include><includ…

医院信息管理系统设计与实现(源码+lw+部署文档+讲解等)

文章目录 前言具体实现截图详细视频演示技术栈系统测试为什么选择我官方认证玩家&#xff0c;服务很多代码文档&#xff0c;百分百好评&#xff0c;战绩可查&#xff01;&#xff01;入职于互联网大厂&#xff0c;可以交流&#xff0c;共同进步。有保障的售后 代码参考数据库参…

神经重建在自动驾驶模拟中的应用

验证自动驾驶软件需要数百万公里的测试。这不仅意味着系统开发周期长&#xff0c;而且系统的复杂度也会不断增加&#xff0c;同时&#xff0c;大规模的实车测试也会耗费巨量的资源并且可能会面临未知的安全问题。aiSim这样的虚拟仿真工具可以减轻真实世界测试的负担。 AD和ADA…

学习ComfyUI的一个不错网站:www.comflowy.com/basics

学习ComfyUI&#xff0c;不仅仅是照搬别人的工作流来使用&#xff0c;重要的是能自己搭建工作流&#xff0c;而要能够熟练搭建&#xff0c;离不开对工作流中节点&#xff08;特别是重要节点&#xff09;的透彻理解。比如我自己&#xff0c;原来对 Lora 就十分陌生&#xff0c;不…