算法——买卖股票(动态规划)

买卖股票

准备总结一下做过的几道买卖股票的题目,还有几道还没来得及做,先把做过的做一个总结,以便日后忘了,留作复习使用。
题目难度又简到难。(一些题目直接参考的是官方题解)。有很多地方理解不到位或者是表述不清,但是还是先做个笔记,方便理解吧。

题目一 买卖股票的最佳时机

题目描述

在这里插入图片描述

解题思路

最大收益就是需要在较低的价格买入,较高的价格卖出。遍历数组,找到当前天数i之前的股票价格的最小值min,如果存在,则当前日期的收益为prices[i] - min;如果找不到,说明当前日期的股票价格是目前遍历过的数组元素中最低的,将min的值设置为当前日期的股票价格。
细节处理,在第一天时,只能买入股票或者是不做任何操作,而不能卖出股票,设置min初始值为Integer.MAX_VALUE,与prices[0]比较,初始值被修改为prices[0]。

代码

class Solution {
    public int maxProfit(int[] prices) {
        int res = 0;
        if(prices.length<2){
            return res;
        }
        int min = Integer.MAX_VALUE;
        for(int i=0;i<prices.length;i++){
            if(min>prices[i]){
                min = prices[i];
            }else{
                res = Math.max(res,prices[i] - min);
            }
        }
        return res;
    }
}

题目二 买卖股票的最佳时机2

题目描述

解题思路

与题目一相比,不同点在于股票交易的次数不受限制了,目的仍是追求最大利益。题目核心是贪心算法。
为了追求利益的最大化,我们在价格低点买入股票,在上涨的最高点卖出股票。假设我们在第i天买入,第i+1、i+2、……、i+j天都在上涨,我们在这几天都保持持有,不做卖出操作,遍历到第i+j+1天时,价格较第i+j天相比下降,则在第i+j天卖出,在第i+j+1天买入(如果第i+j+2天的价格较第i+j+1天就像下降,则选择在第i+j+2天买入)。
这样的过程实质相当于在价格数组找到多个连续递增的子序列。

代码

min记录的是买入时的价格,max记录的是卖出时的价格。

class Solution {
    public int maxProfit(int[] prices) {
           int profit = 0, max = prices[0], min = prices[0];
           if(prices.length<2)
           return profit;
        for(int i=1;i<prices.length;i++){
            if(prices[i]>prices[i-1])
            {
                max = prices[i];
                if(i==prices.length-1)
                {
                    profit = profit +max - min;
                }
            }
            else
            {
                profit = profit +max - min;
                max = prices[i];
                min = prices[i];
            }
        }
        return profit;
    }
}

可以对上述代码作出简化:

class Solution {
    public int maxProfit(int[] prices) {
        int res = 0;
        if(prices.length<2){
            return res;
        }
        for(int i=1;i<prices.length;i++){
            res+=Math.max(0,prices[i] - prices[i-1]);
        }
        return res;
    }
}

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

题目描述

在这里插入图片描述

解题思路

这道题相当于在题目二的基础上增加了手续费这一限制因素。
买卖股票这种题目其实可以记录两种状态,因为每天可以采取的措施就是买入和卖出两种,我们在这两种操作中选择利益最大的那个即可,其次理清题目的逻辑即可。
记录两种状态,第一种是买入buy(如果当前天数i的前一天不持有股票,则在第i天买入,需要用前一天的累计收益sell[i-1]减去成本prices[i];如前一天持有股票,则继续持有即可),记录的是当前日期第i天买入后的收益;第二种状态是卖出sell(如果当前日期第i天 的前一天手上不持有股票,则无法卖出,持有收益与前一天相同;如果前一天持有股票,则计算卖出的收益buy[i-1]+prices[i],同时减去手续费),记录的当前日期第i天卖出后的累计收益。
用计算式表达上述情况如下:
buy[i] = Math.max(buy[i-1],sell[i-1] - prices[i])
sell[i] = Math.max(buy[i-1]+prices[i]-fee,sell[i-1])
考虑边界问题,开始第一天,无法卖出,故将卖出的收益设置为sell[0] = 0,代表卖出收益为0,符合逻辑;在一天执行买入操作,则收益为buy[0] = -prices[0]。
最后的答案就是sell[prices.length-1],因为将所有的股票卖出,才会获得收益,手上没有股票才是最赚钱的。

代码

class Solution {
    public int maxProfit(int[] prices, int fee) {
        if(prices.length<2){
            return 0;
        }
        int []buy = new int[prices.length];
        int []sell = new int[prices.length];
        buy[0] = -prices[0];
        sell[0] = 0;
        for(int i=1;i<prices.length;i++){
            buy[i] = Math.max(buy[i-1],sell[i-1] - prices[i]);
            sell[i] = Math.max(buy[i-1]+prices[i]-fee,sell[i-1]);
        }
        return sell[prices.length-1];
    }
}

题目四 买卖股票的最佳时机4

题目描述

在这里插入图片描述

解题思路

与题目三类似,记录买入与卖出两种状态。
限制条件是最多执行k笔交易,使用二维数组来记录买入和卖出状态,数组的第一维下标i代表当前日期第i天,第二维下标j代表目前已经完成了j笔交易(完成一笔交易的定义是从买入到卖出完成才算一笔交易,只买入未卖出则不算一笔交易)。
其余逻辑与第三题一致,表达式如下:
buy[i][j] = Math.max(buy[i-1][j],sell[i-1][j] - prices[i]);
第i天执行买入操作:buy[第i天][已完成了j笔交易] = Math.max(前一天已持有股票,买入收益与前一天保持一致;前一天没有买入记录,用当前卖出收益减去成本);
sell[i][j] = Math.max(buy[i-1][j-1]+prices[i],sell[i-1][j]);
第i天执行卖出操作:sell[第i天][已完成了j笔交易] = Math.max(前一天的买入收益加上当前的价格;前一天没有买入操作,继续保持前天的卖出收益)。
边界问题,第一天buy[0][0] = -prices[0],sell[0][0] = 0,其余buy[0][j]和sell[0][j]都设置为Integer.MIN_VALUE/2,这样做一是为了将这几个状态设置为不合法,不可能第0天就完成了数笔交易;第二除2是为了在之后的减法操作中防止溢出。
最后的结果是在最后一天的卖出记录中寻找最大值。

代码

class Solution {
    public int maxProfit(int k, int[] prices) {
        int res = 0;
        if(k==0||prices.length==0){
            return res;
        }
        int [][]buy = new int[prices.length][k+1];
        int [][]sell = new int[prices.length][k+1];
        buy[0][0] = -prices[0];
        sell[0][0] = 0;
        for(int i=1;i<=k;i++){
            sell[0][i] = Integer.MIN_VALUE/2;
            buy[0][i] = Integer.MIN_VALUE/2;
        }
        int n = prices.length;
        for(int i =1;i<n;i++){
            buy[i][0] = Math.max(buy[i-1][0],sell[i-1][0] - prices[i]);
            for(int j=1;j<=k;j++){
                buy[i][j] = Math.max(buy[i-1][j],sell[i-1][j] - prices[i]);
                sell[i][j] = Math.max(buy[i-1][j-1]+prices[i],sell[i-1][j]);
            }
        }
        for(int i=1;i<=k;i++){
            res = Math.max(res,sell[n-1][i]);
        }
        return res;
    }
}

题目五 买卖股票的最佳时机3

这道题目在写完上面几道题解后顺势完成的。

题目描述

在这里插入图片描述

解题思路

解题思路和上一题一样,只是将K固定为2.

代码

class Solution {
    public int maxProfit(int[] prices) {
        if(prices.length<2){
            return 0;
        }
        int [][]buy = new int[prices.length][3];
        int [][]sell = new int[prices.length][3];
        buy[0][0] = -prices[0];
        sell[0][0] = 0;
        for(int i=1;i<=2;i++){
            buy[0][i]=sell[0][i] = Integer.MIN_VALUE/2;
        }
        for(int i=1;i<prices.length;i++){
            buy[i][0] = Math.max(buy[i-1][0],sell[i-1][0] - prices[i]);
            for(int j=1;j<=2;j++){
                buy[i][j] = Math.max(buy[i-1][j],sell[i-1][j] -prices[i]);
                sell[i][j] = Math.max(buy[i-1][j-1]+prices[i],sell[i-1][j]);
               // System.out.println(buy[i][j]+","+sell[i][j]);
            }
        }
        int res = 0;
        int max = Math.max(sell[prices.length-1][1],sell[prices.length-1][2]);
        res = Math.max(0,max);
        return res;
    }
}
  • 6
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值