《LeetCode》—— 买卖股票的最佳时机

news2024/11/28 12:49:39

本期,我将给大家讲解的是有关动态规划类的题——买卖股票的最佳时机。这个系列总共有四道题。接下来,让我们一起去看看!!!


目录

(一)买卖股票的最佳时机

(二)买卖股票的最佳时机 II

(三)买卖股票的最佳时机 III

(四)买卖股票的最佳时机 IV


(一)买卖股票的最佳时机

LeetCode题目链接:买卖股票的最佳时机

题目如下:

 

题目分析:

第一题,我们先来看最简单的(题目的难度也是逐级提升的)。

  • 思路一:

首先,我们有的小伙伴一读题,最先想到的可能就是暴力去求解这道题目,但是很遗憾当我们提交代码的时候显示的是代码超时了。因此,很显然暴力解法显然不是出题者要考察我们的地方。

  • 思路二:

那么暴力求解不行,还有没有其他思路呢?我们通过分析题目,主要意思就是找到最多一次买卖股票可以获得的最大利润的问题的解决方案。我们可以利用贪心的思路来分析,主要原理是跟踪到目前为止看到的最低价格和以当前价格卖出可以获得的最大利润,通过迭代价格并相应地更新这些值,我们可以找到可以获得的最大利润。

  • 思路三:

那么除了上述的贪心去求解还有没有其他思路呢?其实还有一种我们今天主要讲的动态规划算法。

解题思路:

在这里,我只给出动态规划的具体实现过程。

  • step1:确定【arry】数组以及下标的含义

首先,我们初始化一个二维数组 arry,其中大小是价格向量的大小(size为给出的数组 prices 的大小),arry 中每行的第一个元素表示买入或者卖出的天数,第二个元素表示当天的状态。

arry[i][0]

  • 表示第i天持有股票所得最多现金 。因为刚开始时没钱买,所以开始现金是0,因此第i天买入股票现金就是 -prices[i], 这是一个负数;
  • arry[0][0]表示第0天持有股票,此时的持有股票就一定是买入股票了,因为不可能有前一天推出来,所以arry[0][0] -= prices[0];

arry[i][1]

  • 表示第i天不持有股票所得最多现金,即也许是一直都还没有没有买入,或者要卖出;
  • arry[0][1]表示第0天不持有股票,不持有股票那么现金就是0,所以arry[0][1] = 0;

  • step2:确定递推公式

如果第i天持有股票即arry[i][0], 那么递归公式可以像如下这样:

  • 如果在当前前的前一天即( i-1) 天就持有股票的话,那么当前所持有的现金就是昨天持有股票的所得现金 即:arry[i - 1][0]
  • 如果是在当前这一天买入股票即(i),那么此时所持有的就是买入今天的股票后所得现金即:-prices[i](为负数)

💨 arry[i][0] = max(arry[i - 1][0], -prices[i])

如果第i天不持有股票即arry[i][1], 那么递归公式可以像如下这样:

  • 如果在第(i-1)天未持有股票,那么就保持现状,所得现金就是昨天不持有股票的所得现金 即:arry[i - 1][1]
  • 如果在第( i )天卖出股票,所得现金就是按照今天股票价格卖出后所得现金即:prices[i] + arry[i - 1][0]

💨 arry[i][1] = max(arry[i - 1][1], prices[i] + arry[i - 1][0])


  • step3:确定遍历顺序

从递推公式可以看出arry[i]都是由arry[i - 1]推导出来的,因此是从前向后遍历

  • step4:图解展示

 

 

代码展示:

  • 暴力解法:
class Solution {
public:
    int maxProfit(vector<int>& prices) {
    int n = (int)prices.size();
    int res = 0;
        for (int i = 0; i < n; ++i){
            for (int j = i + 1; j < n; ++j) {
                res = max(res, prices[j] - prices[i]);
            }
        }
        return res;
   }
};

性能分析:

  • 时间复杂度:O(n^2)
  • 空间复杂度:O(1)
  • 贪心算法:
class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int num = INT_MAX;
        int res = 0;
        for (int i = 0; i < prices.size(); i++) {
            num = min(num, prices[i]); 
            res = max(res, prices[i] - num); 
        }
        return res;
    }
};

性能分析:

  • 时间复杂度:O(n)
  • 空间复杂度:O(1)
  • 动态规划:
class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int size = prices.size();
        if (size == 0) 
            return 0;
        vector<vector<int>> arry(size, vector<int>(2));
        arry[0][0] -= prices[0];
        arry[0][1] = 0;
        for (int i = 1; i < size; i++) {
            arry[i][0] = max(arry[i - 1][0], -prices[i]);
            arry[i][1] = max(arry[i - 1][1], prices[i] + arry[i - 1][0]);
        }
        return arry[size - 1][1];

    }
};

 性能分析:

  • 时间复杂度:O(n)
  • 空间复杂度:O(n)

(二)买卖股票的最佳时机 II

LeetCode题目链接:买卖股票的最佳时机 II

题目如下:

 

题目分析:

大家通过分析这道题,我们可以发现这道题跟上道题目的差别就在于本题股票可以进行多次的买卖操作(注意只有一只股票,所以再次购买前要出售掉之前的股票)。

  • 思路一:

小伙伴们看到这道题目想的就是 -- 选一个低的买入,再选个高的卖,再选一个低的买入.....循环反复。

其实,我们对这种思路进行分解 -- 只收集正利润,即最终的最大利润就是各天的正利润之和。

对于每次迭代,我们可以计算出价格向量中当前元素与前一个元素之间的差值,并取该差值和0中较大的值,这样做确保仅将正差异添加到 res 变量中,仅使用单个循环来迭代价格向量并计算最大利润

 

  • 思路二:

还是利用动态规划的思想对其进行操作。

解题思路:

因为本题跟上一题是类似的,不同的在关于递推公式,接下来,我就只讲递推公式的演化,其余的跟上题相同。

如果第i天持有股票即arry[i][0], 那么递归公式可以像如下这样:

这题因为是可以进行多次的买卖的,因此递归公式主要的变化就发生在对于 arry[i][0] 的处理,所以当第(i)天买入股票的时候,所持有的现金可能是在这之前已经经过买卖交易所得到的。

因此,对于第(i)天持有股票即 arry[i][0]:如果是第(i)天买入股票,所得现金就是昨天不持有股票的所得现金 减去 当天的股票价格 即:arry[i - 1][1] - prices[i]。

💨 arry[i][0] =  max(arry[i - 1][0], arry[i - 1][1] - prices[i])

如果第i天持有股票即arry[i][1], 递归公式跟上题相同

💨 arry[i][1] = max(arry[i - 1][1], prices[i] + arry[i - 1][0])


代码展示:

  • 贪心算法:
class Solution {
public:
    int maxProfit(vector<int>& prices) {
         int res=0;
         for(int i=1; i<prices.size(); ++i)
         {
             res+=max(prices[i]-prices[i-1],0);
         }
         return res;
    }
};

 性能分析:

  • 时间复杂度:O(n)
  • 空间复杂度:O(1)

  • 动态规划:
class Solution {
    public:
        int maxProfit(vector<int>& prices) {
        int size = prices.size();
        vector<vector<int>> arry(size, vector<int>(2, 0));
        arry[0][0] -= prices[0];
        arry[0][1] = 0;
        for (int i = 1; i < size; i++) {
            arry[i][0] = max(arry[i - 1][0], arry[i - 1][1] - prices[i]); 
            arry[i][1] = max(arry[i - 1][1], arry[i - 1][0] + prices[i]);
        }
        return arry[size - 1][1];
    }
};

 性能分析:

  • 时间复杂度:O(n)
  • 空间复杂度:O(n)

(三)买卖股票的最佳时机 III

LeetCode题目链接:买卖股票的最佳时机 III

题目如下:

 

题目分析:

这道题跟上述两道题的不同之处在于对买卖的次数限制为 至多买卖两次,这意味着可以买卖一次,可以买卖两次,也可以不买卖。

解题思路:

  • step1确定【arry】数组以及下标的含义

在这道题目,我们就需要设置 5个状态位来表示了,因为题目已经明确给出了之多买卖两次,因此在加上操作的情况,总共就有5种。

  • 0、表示没有任何操作 
  • 1、第一次买入股票
  • 2、第一次卖出股票
  • 3、第二次买入股票
  • 4、第二次卖出股票

💨 arry[i][j] 中 i 表示第i天,j为 [0 - 4] 五个状态,arry[i][j] 表示第i天状态j所剩最大现金。

arry[0][0]:

开始时没有任何操作,因此应该为0,即:arry[0][0] = 0;

arry[0][1]:

在第0天准备进行第一次购买操作,所以此时应为:arry[0][1] = -prices[0];

arry[0][2]:

 大家可以理解当天买入,当天卖出,所以 arry[0][2] = 0;

arry[0][3]:

第0天第二次买入操作,初始值应该是多少呢?应该不少同学疑惑,第一次还没买入呢,怎么初始化第二次买入呢?

第二次买入依赖于第一次卖出的状态,其实相当于第0天第一次买入了,第一次卖出了,然后再买入一次(第二次买入),那么现在手头上没有现金,只要买入,现金就做相应的减少。

所以第二次买入操作,初始化为:arry[0][3] = -prices[0];

arry[0][4]:

第二次卖出股票,应为arry[0][4] = 0;


  • step2:确定递推公式

如果第i天第一次买进股票即arry[i][1], 那么递归公式可以像如下这样:

  • 如果第i天第一次买入股票了,那么 arry[i][1] = arry[i-1][0] - prices[i]
  • 如果第i天没有进行任何操作,则保持前一天买入的状态,即:arry[i][1] = arry[i - 1][1]

💨 arry[i][1] = max(arry[i-1][0] - prices[i], arry[i - 1][1])

如果第i天第一次卖出股票即arry[i][2], 那么递归公式可以像如下这样:

  • 如果第i天第一次卖出股票了,那么 arry[i][2] = arry[i - 1][1] + prices[i]
  • 如果第i天没有操作,则保持前一天卖出股票的状态,即:arry[i][2] = arry[i - 1][2]

💨 arry[i][2] = max(arry[i - 1][1] + prices[i], arry[i - 1][2])

如果第i天第二次买进股票即arry[i][3], 那么递归公式可以像如下这样:

  • 如果第i天第二次买入股票了,那么 arry[i][3] = arry[i-1][2] - prices[i]
  • 如果第i天没有进行任何操作,则保持前一天买入的状态,即:arry[i][3] = arry[i - 1][3]

💨 arry[i][3] = max(arry[i - 1][2] - prices[i], arry[i - 1][3])
           

如果第i天第二次卖出股票即arry[i][4], 那么递归公式可以像如下这样:

  • 如果第i天第二次卖出股票了,那么 arry[i][4] = arry[i - 1][3] + prices[i]
  • 如果第i天没有操作,则保持前一次卖出股票的状态,即:arry[i][4] = arry[i - 1][4]

💨arry[i][4] = max(arry[i - 1][3] + prices[i], arry[i - 1][4])


  • step3:遍历顺序

从递归公式可以看出,一定是从前向后遍历,因为arry[i],依靠arry[i - 1]的数值。

  • step4图解展示

 

代码展示:

以下给出的是优化后的版本(大家可以根据理解写出原始版本)

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        if(prices.size() == 0) return 0;

        vector<int> arry(5,0);
        arry[1] = -prices[0];
        arry[3] = -prices[0];

        for(int i=1; i<prices.size(); ++i){
            arry[1] = max(arry[1], arry[0] - prices[i]);
            arry[2] = max(arry[2], arry[1] + prices[i]);
            arry[3] = max(arry[3], arry[2] - prices[i]);
            arry[4] = max(arry[4], arry[3] + prices[i]);     
            
        }
        return arry[4];
      
    }
};

性能分析:

  • 时间复杂度:O(n)
  • 空间复杂度:O(1)

(四)买卖股票的最佳时机 IV

LeetCode题目链接:买卖股票的最佳时机 IV

题目如下:

题目分析:

这道题总体说来就是上一题的进阶版本,对于买卖的次数又进行了限制,这里要求至多有k次交易。

解题思路:

整体思路还是跟上题一样,只是本题对于次数是k次

  • step1确定【arry】数组以及下标的含义

此时状态就不仅仅有4种了(除去没有任何操作),而是2*k种,如果加上不做任何操作的状态,那此处的状态就有 2*k+1 次。

  • step2:确定递推公式

同上(大家自己推倒看看能否推倒出来)

  • step3:遍历顺序

同上

  • step4图解展示

大家可以对比上一题的图自己画图试试

代码展示:

class Solution {
public:
    int maxProfit(int k, vector<int>& prices) {
        if (prices.size() == 0) return 0;
        vector<vector<int>> arry(prices.size(), vector<int>(2 * k + 1, 0));
        for (int j = 1; j < 2 * k; j += 2) {
            arry[0][j] = -prices[0];
        }
        for (int i = 1;i < prices.size(); i++) {
            for (int j = 0; j < 2 * k - 1; j += 2) {
                arry[i][j + 1] = max(arry[i - 1][j + 1], arry[i - 1][j] - prices[i]);
                arry[i][j + 2] = max(arry[i - 1][j + 2], arry[i - 1][j + 1] + prices[i]);
            }
        }
        return arry[prices.size() - 1][2 * k];
    }
};

 


到此,关于购买股票的几个题便讲解完毕了。希望对大家有所帮助,感谢各位的观看!!!

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

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

相关文章

应用案例 | 使用dataFEED OPC Tunnel解决基于DCOM的OPC Classic通信难题

一 背景 OPC&#xff08;OLE for Process Control&#xff09;Classic是一种基于COM&#xff08;Component Object Model&#xff09;的协议&#xff0c;用于在工业控制系统中实现数据通信和集成。它为工业自动化提供了一种标准接口&#xff0c;可使不同厂商的设备和系统之间进…

使用transformers框架导入t5-small模型做文本翻译

前言 在上一篇的博客基于transformer的Seq2Seq机器翻译模型训练、预测教程讲述了怎么训练一个翻译的Seq2Seq模型&#xff0c;这篇博客则来说说怎么使用huggingface中训练好的模型来完成翻译的任务。 环境和模型说明 要想使用huggingface中的预训练模型&#xff0c;首先要安装…

并行分布式计算 并行算法常用设计技术

文章目录 并行分布式计算 并行算法常用设计技术划分设计技术均匀划分 - PSRS方根划分- Valiant 归并算法对数划分功能划分 分治设计技术双调归并网络凸包问题 平衡树设计技术求最大值前缀和 倍增设计技术表序问题 流水线设计技术卷积 并行分布式计算 并行算法常用设计技术 这里…

空调群控、智能报警,这些设备有点牛

空调绝对是夏季使用率高的电器之一&#xff0c;尤其是今年全国各地高温不断&#xff0c;说“命都是空调给的”都不为过...... 在家的时候&#xff0c;我们可以随手开关空调&#xff0c;非常方便&#xff0c;如果是学校教学楼、工厂宿舍、银行网点、办公大楼、机房等地的管理者…

原神服务端搭建架设Centos系统

原神服务端搭建架设Centos系统 我是艾西&#xff0c;今天为大家带来原神服务端centos系统的教程 Step1. 准备工具 这个端在Windows、Linux系统上都可以跑&#xff0c;本次教程基于Linux。 准备如下工具&#xff1a; 服务器1台 centos7 系统 最低配置32核32G 公网联机 2. 手…

Python采集<灵剑尊>全本内容,一次性看个爽~

前言 嗨喽&#xff0c;大家好呀~这里是爱看美女的茜茜呐 环境使用: Python 3.8 Pycharm 模块使用: requests >>> pip install requests 数据请求模块 parsel >>> pip install parsel 数据解析模块 使用知识点: python基础语法: print 输出函数 字符…

如何在繁重的工作中持续成长?

目录 一、认知&#xff1a;打破成长误区误区 1&#xff1a;个人成长和工作相互冲突误区 2&#xff1a;做自己没了解过的事情才是成长误区 3&#xff1a;学的东西越多&#xff0c;成长越快 二、实施&#xff1a;如何高效的利用时间2.1常见的时间管理法2.2 记录&#xff1a;提醒事…

三、easyUI中的accordion(分类)组件

1.accordion&#xff08;分类&#xff09;组件的概述 分类空间允许用户使用多面板&#xff0c;但在同一时间只会显示一个。每个面板都内建支持展开和折叠功能。点击一个面板的标题将会展开或折叠面板主体。面板内容可以通过指定的href属性使用ajax方式读取面板内容。用户可以定…

NISP含金量如何?NISP一级好考吗?

国家信息安全水平测试&#xff08;NationalInformationSecurityTestProgram&#xff0c;通称NISP&#xff09;&#xff0c;是通过中国信息安全测评中心执行塑造我国网络空间安全优秀人才的一个项目。 含金量很高值得考&#xff0c;NISP分为一级、二级、三级&#xff0c;其中一…

【JAVA程序设计】(C00136)基于SSM(非maven)的养老院综合服务管理系统

基于SSM&#xff08;非maven&#xff09;的养老院综合服务管理系统 项目简介项目获取开发环境项目技术运行截图 项目简介 本项目为基于SSM养老院综合服务系统&#xff0c;本项目分为二种角色:管理员、护工&#xff1b; 管理员角色包含以下功能&#xff1a; 管理员登录,个人资料…

java语法(二)线程并发、Juit单元测试、反射机制、注解、动态代理、XML解析、JVM

文章目录 线程并发多线程多线程的创建Thread常用API 线程同步与通信线程同步&#xff1a;单例模式的三种写法同步代码块同步方法Lock锁 线程通信 线程池获取线程池对象ThreadPoolExecutor线程池处理runnable任务线程池处理callable任务 Executors定时器Timer调度可重复执行任务…

Springboot +Flowable,会签、或签简单使用(一)

一.简介 **会签&#xff1a;**在一个流程中的某一个 Task 上&#xff0c;这个 Task 需要多个用户审批&#xff0c;当多个用户全部审批通过&#xff0c;或者多个用户中的某几个用户审批通过&#xff0c;就算通过。 例如&#xff1a;之前的请假流程&#xff0c;假设这个请假流程…

勇于创新,全力以赴,流辰信息快速开发平台助力企业提质增效!

在科技、社会、信息都快速发展的今天&#xff0c;每一家企业都希望能跟上时代的发展步伐&#xff0c;提质增效&#xff0c;获得长远发展。如今&#xff0c;办公自动化正在迅猛发展&#xff0c;数字化转型升级成为了各大企业的发展趋势和奋斗目标&#xff0c;流辰信息快速开发平…

【大数据模型】使用Claude浅试一下

汝之观览&#xff0c;吾之幸也&#xff01;本文主要聊聊Claude使用的流程&#xff0c;在最后对国内外做了一个简单问题的对比&#xff0c;希望国内的大数据模型更快的发展。 一、产品介绍 claude官网 Claude是一款由前OpenAI的研究员和工程师开发的新型聊天机器人&#xff0c;…

chatGPT嵌入浏览器搜索

正文 看效果&#xff0c;注意右侧&#xff1a; 更牛的是你还可以继续向他回答的进行下一步更细节的提问&#xff0c;互动交流问题&#xff0c;妥妥的一对一辅导啊。 安装此插件后&#xff0c;你还可以通过选中 文字 来解读&#xff0c;真是爱了爱了&#xff0c;只想说 "&a…

数据安全治理流程设计

在上一篇讲了 政务数据分级安全保护要求(明细) 只能说可以作为前期咨询工作的理论依据之一,但数据治理具体如何展开工作?这篇简单设计下,后续会有定期更新数据安全治理这方面的文章。 数据治理是大数据发展而来也是信息技术发展而来,在之前最多是数据安全防护,当然目前很…

专业的ADAS测试记录仪ETHOS

随着ADAS驾驶辅助系统技术的快速发展及日臻成熟&#xff0c;近年来ADAS在全球汽车市场已开始快速普及和商业化&#xff0c;而如何确保ADAS系统的可靠和安全俨然成为汽车领域的重要问题。因此&#xff0c;ADAS驾驶辅助系统的测试也成为了各大整车厂及零部件厂商关注的热点。 一 …

内网搭建Jenkins自动化远程部署项目到Windows服务器

一、下载Jenkins War Jenkins Packages Jenkins War Packages This is the WAR package repository of Jenkins for installation.You will need to explicitly install a supported Java runtime environment (JRE), e.g. Eclipse Temurin.Weekly Release Line Supported Ja…

Philosophy of life: Love and Time

背景介绍: 因为爱、幸福、快乐、悲伤、富有、悲伤等交织在一起组成了人这样一个特殊的感情个体&#xff0c;现实生活中很多人忽视了爱的珍贵&#xff0c;这个故事讲的是"富有"&#xff0c;"虚荣","悲伤","狂喜" 都不能理解爱的珍贵&am…

docker安装kafka(M2芯片)

背景&#xff1a;想通过kafka做一个需求&#xff0c;之前没有用过&#xff0c;将TXT文件中的数据加载到kafka中&#xff0c;再通过logstash将kafka中的数据加载到es中。 基本操作环境介绍 操作系统苹果M2kafka镜像版本wurstmeister/kafkazookeeper镜像版本zookeeper:latestka…