news 2026/6/9 22:31:21

算法题 买卖股票的最佳时机含手续费

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
算法题 买卖股票的最佳时机含手续费

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

问题描述

给定一个整数数组prices,其中prices[i]表示第i天的股票价格;整数fee代表了交易股票的手续费用。

你可以无限次地完成交易,但是你每笔交易都需要付手续费。如果你已经购买了一个股票,在卖出它之前你就不能再继续购买股票了。

返回获得利润的最大值。

注意:这里的一笔交易指买入股票然后卖出股票的整个过程。

示例

输入: prices = [1, 3, 2, 8, 4, 9], fee = 2 输出: 8 解释: 能够达到的最大利润: 在此处买入 prices[0] = 1 在此处卖出 prices[3] = 8 交易利润: 8 - 1 - 2 = 5 在此处买入 prices[4] = 4 在此处卖出 prices[5] = 9 交易利润: 9 - 4 - 2 = 3 总利润: 5 + 3 = 8 输入: prices = [1,3,7,5,10,3], fee = 3 输出: 6 解释: 最优策略是买入1,卖出10,利润: 10-1-3=6

算法思路

核心思想:

每一天只有两种状态:

  • 持有股票(hold):手上持有一股股票
  • 不持有股票(sold):手上没有股票

状态转移方程:

  • 持有状态(hold):

    • 要么之前就持有,今天继续持有:hold = hold
    • 要么今天买入:hold = sold - price[i]
    • 取最大值:hold = max(hold, sold - price[i])
  • 不持有状态(sold):

    • 要么之前就不持有,今天继续不持有:sold = sold
    • 要么今天卖出(需要支付手续费):sold = hold + price[i] - fee
    • 取最大值:sold = max(sold, hold + price[i] - fee)

初始化:

  • 第一天持有hold = -prices[0](买入股票)
  • 第一天不持有sold = 0(什么也不做)

结果:

  • 最后一天不持有股票的状态:sold
  • 因为持有股票肯定不如卖出后获得现金

代码实现

方法一:动态规划

classSolution{/** * 计算含手续费的股票交易最大利润 * * @param prices 股票价格数组 * @param fee 交易手续费 * @return 最大利润 */publicintmaxProfit(int[]prices,intfee){if(prices==null||prices.length<=1){return0;}// 初始化状态inthold=-prices[0];// 持有股票的最大利润intsold=0;// 不持有股票的最大利润// 从第二天开始遍历for(inti=1;i<prices.length;i++){// 保存前一天的hold状态,因为会被更新intprevHold=hold;// 更新持有状态:继续持有 或 买入股票hold=Math.max(hold,sold-prices[i]);// 更新不持有状态:继续不持有 或 卖出股票(支付手续费)sold=Math.max(sold,prevHold+prices[i]-fee);}// 最终状态是不持有股票returnsold;}}

方法二:优化

classSolution{/** * 直接使用变量更新,无需额外空间 */publicintmaxProfit(int[]prices,intfee){if(prices.length<=1){return0;}inthold=-prices[0];intsold=0;for(inti=1;i<prices.length;i++){// 这里需要先计算新的sold,再更新holdintnewHold=Math.max(hold,sold-prices[i]);intnewSold=Math.max(sold,hold+prices[i]-fee);hold=newHold;sold=newSold;}returnsold;}}

算法分析

  • 时间复杂度:O(n),n是价格数组的长度,只需要遍历一次
  • 空间复杂度:O(1),只使用常数个额外变量

算法过程

1:prices = [1,3,7,5,10,3], fee = 3

过程

  • 第0天:hold=-1, sold=0
  • 第1天:hold=-1, sold=max(0, -1+3-3)=0
  • 第2天:hold=-1, sold=max(0, -1+7-3)=3
  • 第3天:hold=max(-1, 3-5)=-1, sold=3
  • 第4天:hold=-1, sold=max(3, -1+10-3)=6
  • 第5天:hold=max(-1, 6-3)=3, sold=6

最终结果:6

测试用例

publicclassTestMaxProfit{publicstaticvoidmain(String[]args){Solutionsolution=newSolution();// 测试用例1:标准示例1int[]prices1={1,3,2,8,4,9};intfee1=2;System.out.println("Test 1: "+solution.maxProfit(prices1,fee1));// 8// 测试用例2:标准示例2int[]prices2={1,3,7,5,10,3};intfee2=3;System.out.println("Test 2: "+solution.maxProfit(prices2,fee2));// 6// 测试用例3:单天int[]prices3={1};intfee3=1;System.out.println("Test 3: "+solution.maxProfit(prices3,fee3));// 0// 测试用例4:两天,无利润int[]prices4={5,4};intfee4=1;System.out.println("Test 4: "+solution.maxProfit(prices4,fee4));// 0// 测试用例5:两天,有利润int[]prices5={1,5};intfee5=2;System.out.println("Test 5: "+solution.maxProfit(prices5,fee5));// 2 (5-1-2=2)// 测试用例6:手续费很高int[]prices6={1,5,10};intfee6=10;System.out.println("Test 6: "+solution.maxProfit(prices6,fee6));// 0// 测试用例7:递增序列int[]prices7={1,2,3,4,5};intfee7=1;System.out.println("Test 7: "+solution.maxProfit(prices7,fee7));// 3 (5-1-1=3)// 测试用例8:递减序列int[]prices8={5,4,3,2,1};intfee8=1;System.out.println("Test 8: "+solution.maxProfit(prices8,fee8));// 0}}

关键点

  1. 状态定义

    • hold表示持有股票时的最大利润(可能是负数)
    • sold表示不持有股票时的最大利润(始终≥0)
  2. 手续费

    • 手续费在卖出时扣除
  3. 初始化

    • 第一天持有股票的成本是-prices[0]
    • 第一天不持有股票的利润是0
  4. 最终状态

    • 最后一天应该不持有股票,因为持有股票不如卖出
    • 所以返回sold而不是max(hold, sold)

常见问题

  1. 为什么手续费只在卖出时扣除?

    • 规定每笔交易都需要手续费,一笔交易包括买入和卖出
    • 在动态规划中,可以在买入或卖出时扣除,效果相同
    • 选择在卖出时扣除,更符合实际交易场景
  2. 能否在同一天买入和卖出?

    • 这样会损失手续费,不会是最优解
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/9 8:49:22

我的错题冰雹数

nint(input()) max10 for j in range(2,n1):numjwhile num!1:if num%20:numnum//2else:num3*num1if num>max1:max1numif num<j:break print(max1)题目任意给定一个正整数 NN&#xff0c;如果是偶数&#xff0c;执行&#xff1a; N/2N/2&#xff1b;如果是奇数&#xff0c…

作者头像 李华
网站建设 2026/6/9 7:20:17

Linux 有名管道fifo进程间通信

函数原型/*** int mkfifo(const char *pathname, mode_t mode);* * brief 用于创建有名管道。该函数可以创建一个路径为pathname的FIFO专用文件&#xff0c;mode指定了FIFO的权限&#xff0c;FIFO的权限和它绑定的文件是一致的。FIFO和pipe唯一的区别在于创建方式的差异。一旦创…

作者头像 李华
网站建设 2026/6/9 10:10:36

TikTok直播录制全攻略:从入门到精通的完整解决方案

在内容创作蓬勃发展的今天&#xff0c;TikTok直播已成为创作者与粉丝深度互动的重要渠道。然而&#xff0c;直播内容的即时性往往让精彩瞬间转瞬即逝&#xff0c;让无数用户深感遗憾。现在&#xff0c;一款强大的开源录制工具横空出世&#xff0c;完美解决了这一痛点&#xff0…

作者头像 李华
网站建设 2026/6/8 22:43:31

SDXL VAE FP16修复版完全指南:从数值稳定性到高效推理

SDXL VAE FP16修复版完全指南&#xff1a;从数值稳定性到高效推理 【免费下载链接】sdxl-vae-fp16-fix 项目地址: https://ai.gitcode.com/hf_mirrors/madebyollin/sdxl-vae-fp16-fix SDXL-VAE-FP16-Fix是一个专门针对Stable Diffusion XL模型变分自编码器的FP16精度修…

作者头像 李华
网站建设 2026/6/10 16:43:14

44、Linux系统故障排查与常见用户问题解决

Linux系统故障排查与常见用户问题解决 1. 双系统启动问题及解决方法 在安装了可双启动Windows和Linux的系统后,有时会遇到在LILO提示符下没有启动Windows分区选项的情况。要解决这个问题,需要对Linux进行配置,具体方法是在 /etc/lilo.conf 文件中添加Windows部分,完成后…

作者头像 李华
网站建设 2026/6/9 7:22:09

45、Linux系统故障排查与维护全攻略

Linux系统故障排查与维护全攻略 在Linux系统的使用过程中,我们难免会遇到各种各样的问题,如打印故障、邮件问题、软件包安装问题、备份恢复错误、应用程序故障以及网络连接问题等。本文将详细介绍这些常见问题的排查方法和解决策略。 打印问题排查 行式打印机守护进程(lp…

作者头像 李华