LeetCode(121-123 & 309):买卖股票的最佳时机 I II III + 冷冻期 Best Time to Buy and Sell Stock I II III (Java)

234 篇文章 1 订阅
177 篇文章 0 订阅

2019.7.21 #程序员笔试必备# LeetCode 从零单刷个人笔记整理(持续更新)

github:https://github.com/ChopinXBP/LeetCode-Babel

这道股票交易题属于零维动态规划题。的三个level,level1只允许一次交易,level2可以允许无限多次交易,level3只允许两次交易,level4是可以允许无限多次交易,但是每一笔卖出之后有一天的买入冷冻期。交易的目的是为了获得最大利润。

对于level1

因为只涉及一次有先后顺序的交易,那么只需要从前往后找出每个i对应的最小买入价格;从后往前找出每个i对应的最大卖出价格。两者一减就可以出结果了。

这个过程其实可以靠动态规划来优化,用两个变量分别记录当前最小买入价格和当前最大利润,一次遍历不断更新。

对于level2

对于多次的交易,需要首先论证一个公理:若单次最佳买卖点之间存在局部波峰和波谷,那么直接进行单次买卖获得的总利润总是小于包含波峰波谷的多次买卖所获得的利润。

有了这条公理就设计算法就很简单了,那就是在所有的波峰和波谷处进行交易,最大利润即为每次波谷-波峰交易的总和。

同样地,过程也可以依靠动态规划来优化,动态更新变量最大利润maxprofit,只要在当前价格比上一价格高时,就更新利润。例如对于[1,2,4,3],maxprofit在第二天更新为0+(2-1)=1,在第三天更新为1+(4-2)=3,第四天不更新。由此遍历一次数组之后能够获得最大利润值。

对于level3

level3只允许完成两次交易,那么就没法利用level2的做法来进行加和了。但总体还是可以凭借动态规划的思想求解。

对于任意一天考虑四个变量:

fstBuy: 在该天第一次买入股票可获得的最大收益
fstSell: 在该天第一次卖出股票可获得的最大收益
secBuy: 在该天第二次买入股票可获得的最大收益
secSell: 在该天第二次卖出股票可获得的最大收益

分别对四个变量进行相应的更新, 最后secSell就是最大收益值(secSell >= fstSell)

每一天的价格为 p
fstBuy(初始为min):第一次不买:fstBuy;第一次买入:-p
fstSell(初始为0): 第一次不卖:fstSell;第一次卖出:fstBuy + p
secBuy(初始为min): 第二次不买:secBuy;第二次买入:fstSell - p
secSell(初始为0): 第二次不卖:secSell;第二次卖出:secBuy + p

对于level4

和level2类似,可以在其基础上进行修改。

建立动态规划数组dp,dp[i][0]代表股票第i天休市时的状态是不持有所得的最大利润,dp[i][1]代表股票第i天休市时的状态是持有所得的最大利润。

第i天休市时的状态是不持有有两种可能:第i-1天也不持有,第i天保持;第i-1天持有,第i天卖出。状态转移方程为:

dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][1] + prices[i - 1]);

第i天休市时的状态是持有有两种可能:第i-1天也持有,第i天保持;第i-2天不持有,第i-1天冷冻,第i天买入。

dp[i][1] = Math.max(dp[i - 1][1], dp[i - 2][0] - prices[i - 1]);

最终的最大利润为dp[prices.length][0]。

对于股票问题的动态转移方程推导可以见:一个通用方法团灭6道股票问题


传送门:买卖股票的最佳时机

Say you have an array for which the ith element is the price of a given stock on day i.

If you were only permitted to complete at most one transaction (i.e., buy one and sell one share of the stock), design an algorithm to find the maximum profit.

Note that you cannot sell a stock before you buy one.

给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。

如果你最多只允许完成一笔交易(即买入和卖出一支股票),设计一个算法来计算你所能获取的最大利润。

注意你不能在买入股票前卖出股票。

示例 1:
输入: [7,1,5,3,6,4]
输出: 5
解释: 在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5。注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格。
     
示例 2:
输入: [7,6,4,3,1]
输出: 0
解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。


/**
 *
 * Say you have an array for which the ith element is the price of a given stock on day i.
 * If you were only permitted to complete at most one transaction (i.e., buy one and sell one share of the stock), design an algorithm to find the maximum profit.
 * Note that you cannot sell a stock before you buy one.
 * 给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。
 * 如果你最多只允许完成一笔交易(即买入和卖出一支股票),设计一个算法来计算你所能获取的最大利润。
 * 注意你不能在买入股票前卖出股票。
 *
 */

public class BestTimetoBuyAndSellStock {
    //两个数组,一个数组记录每个i对应的最小买入价格;一个数组记录每个i对应的最大卖出价格
    public int maxProfit(int[] prices) {
        int length = prices.length;
        if(length < 2)
            return 0;
        int[] buyprice = new int[length];
        int[] sellprice = new int[length];
        int buy = prices[0];
        int sell = prices[length - 1];
        for(int i = 0; i < length; i++){
            if(prices[i] < buy){
                buyprice[i] = prices[i];
                buy = prices[i];
            }else{
                buyprice[i] = buy;
            }
            if(prices[length - 1 - i] > sell){
                sellprice[length - 1 - i] = prices[length - 1 - i];
                sell = prices[length - 1 - i];
            }else{
                sellprice[length - 1 - i] = sell;
            }
        }
        int profit = 0;
        for(int i = 0; i < length; i++){
            if(sellprice[i] - buyprice[i] > profit){
                profit = sellprice[i] - buyprice[i];
            }
        }
        return profit;
    }

    //记录最小买入价格与最大利润,动态更新
    public int maxProfit2(int prices[]) {
        int minprice = Integer.MAX_VALUE;
        int maxprofit = 0;
        for (int i = 0; i < prices.length; i++) {
            if (prices[i] < minprice)
                minprice = prices[i];
            else if (prices[i] - minprice > maxprofit)
                maxprofit = prices[i] - minprice;
        }
        return maxprofit;
    }
}




传送门:买卖股票的最佳时机 II

Say you have an array for which the ith element is the price of a given stock on day i.

Design an algorithm to find the maximum profit. You may complete as many transactions as you like (i.e., buy one and sell one share of the stock multiple times).

Note: You may not engage in multiple transactions at the same time (i.e., you must sell the stock before you buy again).

给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。

设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。

注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

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

示例 2:
输入: [1,2,3,4,5]
输出: 4
解释: 在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。
     注意你不能在第 1 天和第 2 天接连购买股票,之后再将它们卖出。
     因为这样属于同时参与了多笔交易,你必须在再次购买前出售掉之前的股票。
    
示例 3:
输入: [7,6,4,3,1]
输出: 0
解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。


/**
 *
 * Say you have an array for which the ith element is the price of a given stock on day i.
 * Design an algorithm to find the maximum profit. You may complete as many transactions as you like (i.e., buy one and sell one share of the stock multiple times).
 * Note: You may not engage in multiple transactions at the same time (i.e., you must sell the stock before you buy again).
 * 给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。
 * 设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。
 * 注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
 *
 */

public class BestTimetoBuyAndSellStockII {
    //峰谷法:
    //若单次最佳买卖点之间存在局部波峰和波谷,那么直接进行单次买卖获得的总利润总是小于包含波峰波谷的多次买卖所获得的利润
    //因此对所有波峰波谷记录并进行多次买卖
    public int maxProfit(int[] prices) {
        if(prices.length < 2)
            return 0;
        int buyprice = prices[0];
        int sellprice = prices[0];
        int profit = 0;

        int idx  = 0;
        while(idx < prices.length - 1){
            while(idx < prices.length - 1 && prices[idx] >= prices[idx + 1])
                idx++;
            buyprice = prices[idx];
            while(idx < prices.length - 1 && prices[idx] <= prices[idx + 1])
                idx++;
            sellprice = prices[idx];
            profit += sellprice - buyprice;
        }
        return profit;
    }

    //记录所有波峰波谷或者连续提升进行加和
    public int maxProfit1(int[] prices) {
        int maxprofit = 0;
        for (int i = 1; i < prices.length; i++) {
            if (prices[i] > prices[i - 1])
                maxprofit += prices[i] - prices[i - 1];
        }
        return maxprofit;
    }
}



传送门:买卖股票的最佳时机 III

Say you have an array for which the ith element is the price of a given stock on day i.

Design an algorithm to find the maximum profit. You may complete at most two transactions.

Note: You may not engage in multiple transactions at the same time (i.e., you must sell the stock before you buy again).

给定一个数组,它的第 i 个元素是一支给定的股票在第 i 天的价格。

设计一个算法来计算你所能获取的最大利润。你最多可以完成 两笔 交易。

注意: 你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

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

示例 2:
输入: [1,2,3,4,5]
输出: 4
解释: 在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。   
     注意你不能在第 1 天和第 2 天接连购买股票,之后再将它们卖出。   
     因为这样属于同时参与了多笔交易,你必须在再次购买前出售掉之前的股票。
     
示例 3:
输入: [7,6,4,3,1] 
输出: 0 
解释: 在这个情况下, 没有交易完成, 所以最大利润为 0。


/**
 *
 * Say you have an array for which the ith element is the price of a given stock on day i.
 * Design an algorithm to find the maximum profit. You may complete at most two transactions.
 * Note: You may not engage in multiple transactions at the same time (i.e., you must sell the stock before you buy again).
 * 给定一个数组,它的第 i 个元素是一支给定的股票在第 i 天的价格。
 * 设计一个算法来计算你所能获取的最大利润。你最多可以完成 两笔 交易。
 * 注意: 你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
 *
 */

public class BestTimetoBuyAndSellStockIII {
    public static void main(String[] args){
        int[] prices = {1,2,4,2,5,7,2,4,9,0};
        System.out.println(maxProfit(prices));
    }
    /**
     对于任意一天考虑四个变量:
     fstBuy: 在该天第一次买入股票可获得的最大收益
     fstSell: 在该天第一次卖出股票可获得的最大收益
     secBuy: 在该天第二次买入股票可获得的最大收益
     secSell: 在该天第二次卖出股票可获得的最大收益
     分别对四个变量进行相应的更新, 最后secSell就是最大收益值(secSell >= fstSell)
     **/
    public static int maxProfit(int[] prices) {
        int fstBuy = Integer.MIN_VALUE, fstSell = 0;
        int secBuy = Integer.MIN_VALUE, secSell = 0;
        for(int p : prices) {
            fstBuy = Math.max(fstBuy, -p);
            fstSell = Math.max(fstSell, fstBuy + p);
            secBuy = Math.max(secBuy, fstSell - p);
            secSell = Math.max(secSell, secBuy + p);
        }
        return secSell;
    }
}




传送门:最佳买卖股票时机含冷冻期

Say you have an array for which the ith element is the price of a given stock on day i.

Design an algorithm to find the maximum profit. You may complete as many transactions as you like (ie, buy one and sell one share of the stock multiple times) with the following restrictions:

You may not engage in multiple transactions at the same time (ie, you must sell the stock before you buy again).
After you sell your stock, you cannot buy stock on next day. (ie, cooldown 1 day)

给定一个整数数组,其中第 i 个元素代表了第 i 天的股票价格 。​

设计一个算法计算出最大利润。在满足以下约束条件下,你可以尽可能地完成更多的交易(多次买卖一支股票):

你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
卖出股票后,你无法在第二天买入股票 (即冷冻期为 1 天)。

示例:
输入: [1,2,3,0,2]
输出: 3 
解释: 对应的交易状态为: [买入, 卖出, 冷冻期, 买入, 卖出]


/**
 *
 * Say you have an array for which the ith element is the price of a given stock on day i.
 * Design an algorithm to find the maximum profit. You may complete as many transactions as you like
 * (ie, buy one and sell one share of the stock multiple times) with the following restrictions:
 * You may not engage in multiple transactions at the same time (ie, you must sell the stock before you buy again).
 * After you sell your stock, you cannot buy stock on next day. (ie, cooldown 1 day)
 * 给定一个整数数组,其中第 i 个元素代表了第 i 天的股票价格 。​
 * 设计一个算法计算出最大利润。在满足以下约束条件下,你可以尽可能地完成更多的交易(多次买卖一支股票):
 * 你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
 * 卖出股票后,你无法在第二天买入股票 (即冷冻期为 1 天)。
 *
 */

public class BestTimeToBuyAndSellStockWithCooldown {
    //动态规划
    public int maxProfit(int[] prices) {
        if(prices.length == 0){
            return 0;
        }
        //dp[i][0]代表股票第i天休市时的状态是不持有,dp[i][1]代表股票第i天休市时的状态是持有
        int[][] dp = new int[prices.length + 1][2];
        dp[0][1] = Integer.MIN_VALUE;
        dp[1][1] = -prices[0];
        for(int i = 2; i <= prices.length; i++){
            //第i天休市时的状态是不持有有两种可能:第i-1天也不持有,第i天保持;第i-1天持有,第i天卖出。
            dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][1] + prices[i - 1]);
            //第i天休市时的状态是持有有两种可能:第i-1天也持有,第i天保持;第i-2天不持有,第i-1天冷冻,第i天买入。
            dp[i][1] = Math.max(dp[i - 1][1], dp[i - 2][0] - prices[i - 1]);
        }
        return dp[prices.length][0];
    }

    //压缩至一维
    public int maxProfit2(int[] prices) {
        int preSellValue = 0;
        int buyValue = Integer.MIN_VALUE;
        int sellValue = 0;
        for(int i = 0; i < prices.length; i++){
            int temp = sellValue;
            sellValue = Math.max(sellValue, buyValue + prices[i]);
            buyValue = Math.max(buyValue, preSellValue - prices[i]);
            preSellValue = temp;
        }
        return sellValue;
    }
}




#Coding一小时,Copying一秒钟。留个言点个赞呗,谢谢你#

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值