Best Time to Buy and Sell Stock(股票的价值系列 整理总结)

本文详细解析了四种不同约束条件下的股票买卖策略算法实现,包括一次买卖、无限次买卖、两次买卖及考虑交易费的情况,并提供了Java代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  • 股票的最大价值I (leetcode 121)
    • 题目:假设把某股票的价格按照时间先后顺序存储再数组中,请问买卖该股票一次可能获得的最大利润是多少?例如,一只股票在某些时间节点的价格为{9,11,8,5,7,12,16,14}。如果5买入,16迈出,则利润为11.
    • 思路:
      • 定义一个函数diff(i) 表示为卖出价格为数组下标为i的元素时获得的最大利润。显然只要找到前i-1个数中最小的元素,就能算出当前的最大利润。
    • 代码:
      class Solution {
          public int maxProfit(int[] prices) {
              if(prices==null||prices.length<2){
                  return 0;
              }
              int min = prices[0];
              int curMaxPro = 0;
              for(int i=1;i<prices.length;i++){
                  if(curMaxPro<prices[i]-min){
                      curMaxPro = prices[i]-min;
                  }
                  if(prices[i]<min){
                      min = prices[i];
                  }
              }
              return curMaxPro;
          }
      }​
  • 股票的最大值II (leetcode 122)
    • 题目:假设有一个数组,它的第i个元素是一个给定的股票在第i天的价格。设计一个算法来找到最大的利润。你可以完成尽可能多的交易(多次买卖股票)。然而,你不能同时参与多个交易(你必须在再次购买前出售股票)。
    • 思路:
      • 本题由于是可以操作任意次数,只为获得最大收益,而且对于一个上升子序列,比如:[5, 1, 2, 3, 4]中的1, 2, 3, 4序列来说,对于两种操作方案:1 在1买入,4卖出 2 在1买入,2卖出同时买入,3卖出同时买入,4卖出 这两种操作下,收益是一样的。所以可以从头到尾扫描prices,如果price[i] – price[i-1]大于零则计入最后的收益中。即贪心法
      • https://www.jianshu.com/p/34bbb0594bd9
    • 代码:
      class Solution {
          public int maxProfit(int[] prices) {
              if(prices==null||prices.length<2){
                  return 0;
              }
              int curMaxProf = 0;
              for(int i=0;i<prices.length-1;i++){
                  if(prices[i+1]>prices[i]){
                      curMaxProf += prices[i+1]-prices[i];
                  }
              }
              return curMaxProf;
          }
      }
  • 股票的最大值III(leetcode 123)
    • 题目:有一个数组arr,其中第i 个元素是第i天给定股票的价格。设计算法以找到最大利润。最多可以完成两笔交易
    • 思路:
      • 还是动态规划的思想。因为最多时两笔交易。也就是说有两种情况可能达到最大利润。一种是完成一笔交易达到最大利润,还有一种是完成二笔交易达到最大利润。对于第一种情况:直接基于(股票的最大价值题)。那么对于第二种情况:我们可以把数组差成两部分的思想来做。什么意思呢》比如第i天为分界点,那么我们把第0到第i天内进行第一笔交易,第i+1到第n-1天进行第二笔交易。(1<=i<=n-2)。基于上叙思想,我们可以写个状态转移方程。
      • 第0到第i天内进行第一笔交易:pre[i] 表示前i天内,进行一次交易能达到的最大价值。转移点怎么写呢,可以类比0-1背包的思想。前i天内,在第i天卖出还是不在第i天卖出这个状态点写一个状态转移方程,不在第i天卖出:相当于pre[i-1];在第i天卖出:arr[i]-preMin(preMin指的前i-1天股票的最小值)
      • 因此状态转移方程为:pre[i] = max(pre[i-1],arr[i]-preMin);
      • 同理;第i+1到第n-1天进行第二笔交易:back[i]表示第i到n-1天内,进行一次交易能达到的最大价值。转移点怎么写呢,同样类比0-1背包的思想。第i到n-1天内,在第i天买入还是不在第i天买入这个状态点写一个状态转移方程,不在第i天买入:相当于back[i+1];在第i天买入:backMax-arr[i](backMax指的第i+1天到第n-1天股票的最大值)
      • 因此状态转移方程为:back[i] = max(back[i-1],backMax-arr[i]);
      • 那么得出两个状态数组,我们可以利用 max(pre[i]+back[i+1])得到两笔交易的最大价值 (0=<i<len-1)pre[len-1] 为进行一笔交易的最大值
    • 代码:(java 时间O(N) 空间O(N))
      class Solution {
          public int maxProfit(int[] prices) {
              if(prices==null||prices.length<=0) return 0;
              int len = prices.length;
              int pre[] = new int[len];//表示前i天内,进行一次交易能达到的最大价值
              int preMin = prices[0];
              int curMax = 0;
              for(int i=1;i<len;i++){
                  if(curMax<prices[i]-preMin){
                      curMax = prices[i]-preMin;
                  }
                  pre[i] = Math.max(pre[i-1],curMax); //curMax指前i天 进行一次交易且第i天卖出所达到的最大值
                  preMin = Math.min(preMin,prices[i]);
              }
              int back[] = new int[len]; //表示第i天到第n-1天内,进行依次交易能达到的最大价值
              int bacMax = prices[len-1];
              curMax = 0;
              for(int j=len-2;j>=0;j--){
                  if(curMax<bacMax-prices[j]){
                      curMax = bacMax-prices[j];
                  }
                  back[j] = Math.max(back[j+1],curMax);
                  bacMax = Math.max(bacMax,prices[j]);
              }
              int res = 0;
              for(int i=0;i<len;i++){
                  if(i!=len-1){
                      res = Math.max(res,pre[i]+back[i+1]);
                  }else{
                      res = Math.max(res,pre[i]);
                  }
              }
              return res;
          }
      }
  • Best Time to Buy and Sell Stock with Transaction Fee (leetcode 714)
    • 题目:一个整数数组prices,其中prices[i]表示一天给定股票的价格;fee表示交易费用的非负整数。根据需要完成尽可能多的交易,但是需要为每笔交易支付交易费用。一次不能购买超过1股的股票(即必须在再次购买之前卖出股票。)
    • 思路:
      • 动态规划
        • 动态转移点:手上有没有股票 进行DP
        • 对于第i天的最大收益,应分成两种情况,一是该天结束后手里没有stock,可能是保持前一天的状态也可能是今天卖出了,此时令收益为cash;二是该天结束后手中有一个stock,可能是保持前一天的状态,也可能是今天买入了,用hold表示。由于第i天的情况只和i-1天有关,所以用两个变量cash和hold就可以,不需要用数组
        • 代码:
          class Solution {
              public int maxProfit(int[] prices, int fee) {
                  if(prices==null||prices.length<=1) return 0;
                  int buy = -prices[0];
                  int cash = 0;
                  for(int i=1;i<prices.length;i++){
                      cash = Math.max(cash,buy+prices[i]-fee);
                      buy = Math.max(buy,cash-prices[i]);
                  }
                  return cash;
              }
          }
      • 贪心算法:
        • 贪心选择的关键是找到一个最大后是不是能够卖掉stock,重新开始寻找买入机会。比如序列1 3 2 8,如果发现2小于3就完成交易买1卖3,此时由于fee=2,(3-1-fee)+(8-2-fee)<(8-1-fee),所以说明卖早了,令max是当前最大price,当(max-price[i]>=fee)时可以在max处卖出,且不会存在卖早的情况,再从i开始重新寻找买入机会。
        • 代码:
          class Solution {
              public int maxProfit(int[] prices, int fee) {
                  if(prices==null||prices.length<=1) return 0;
                  int profit=0;
                  int curProfit=0;
                  int minP=prices[0];
                  int maxP=prices[0];
                  int i;
                  for(i=1;i<prices.length;i++){
                      minP=Math.min(minP,prices[i]);
                      maxP=Math.max(maxP,prices[i]);
                      curProfit=Math.max(curProfit,prices[i]-minP-fee);
                      if((maxP-prices[i])>=fee){
                          profit+=curProfit;
                          curProfit=0;
                          minP=prices[i];
                          maxP=prices[i];
                      }
                  }
                  return profit+curProfit;
              }
          }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值