Since the profit is defined by the current price and the minimum price before today. So we can have one pointer holds the minimum price so far, and another pointer holds the max price so far, with the lowerest peak from the left and highest peak from the right, we can get the maximum profit.
1 2 3 4 5 6 7 8 9 10 11 12
funcmaxProfit(prices []int)int { profit := 0 for min, i := 100001, 0; i < len(prices); i++ { if min > prices[i] { min = prices[i] } if prices[i] - min > profit { profit = prices[i] - min } } return profit }
b. Dynamic programming
Let’s denote dp[i] as the profit we have so far, it can be two cases:
dp[i][0] We have stock so the profit we have for the first day if we buy stock is dp[0][0] = -prices[0]
dp[i][1] We don’t have stock
so the state transition function will be :
dp[i][0] = max(dp[i - 1][0], -prices[i]) the maximum value if we bought the stock in the previous day of we buy the stock on day i dp[i][1] = max(dp[i - 1][0] + prices[i], dp[i-1][1]) the maximum value if we bought the stock before and sell it today, or we don’t have stock before and won’t buy today
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
funcmaxProfit(prices []int)int { n := len(prices) dp := make([][2]int, n) dp[0][0] = -prices[0] dp[0][1] = 0 max := func(a, b int)int { if a > b { return a } return b } for i := 1; i < n; i++ { dp[i][0] = max(dp[i - 1][0], -prices[i]) dp[i][1] = max(dp[i - 1][0] + prices[i], dp[i - 1][1]) } return dp[n - 1][1] }
To gian the maximum amount of profit, we just need to accumulate the position profits if we buy it on the previous day and sell it on the next day.
1 2 3 4 5 6 7 8 9 10
funcmaxProfit(prices []int)int { result := 0 for i := 1; i < len(prices); i++ { profit := prices[i] - prices[i - 1] if profit > 0 { result += profit } } return result }
b. Peak & Valley solution
A naive approach is find the local lowest price (valley price) and sell it at the next local highest price (peak price). Then we accumulate all of those local profits.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
funcmaxProfit(prices []int)int { result := 0 peak := prices[0] valley := prices[0] length := len(prices) for i := 0; i < length - 1; { for i < length - 1 && prices[i] >= prices[i + 1] { i++ } valley = prices[i] for i < length - 1 && prices[i] <= prices[i + 1] { i++ } peak = prices[i] result += peak - valley } return result }
funcmaxProfit(prices []int)int { length := len(prices) dp := make([][2]int, length) // dp[i][0] on day i we are holding stock // dp[i][1] on day i we don't have stock dp[0] = [2]int{-prices[0], 0} max := func(a, b int)int { if a > b { return a } return b } for i := 1; i < length; i++ { // stock we got from day i - 1, or stock we are going to buy on day i dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] - prices[i]) // we don't have stock from day i - 1, or we are going to sell stock we got from day i - 1 dp[i][1] = max(dp[i - 1][1], dp[i - 1][0] + prices[i]) } return max(dp[length - 1][0], dp[length - 1][1]) }
dp[i][0] we are holding the stock, so dp[i][0] = max(dp[i-1][0], dp[i-1][2] - prices[i])
dp[i][1] we are selling the stock, so dp[i][1] = dp[i - 1][0] + prices[i]
dp[i][2] we are in the cooldown period, so dp[i][2] = max(dp[i-1][2], dp[i-1][1])
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
funcmaxProfit(prices []int)int { n := len(prices) dp := make([][3]int, n) max := func(a, b int)int { if a > b { return a } return b } dp[0] = [3]int{-prices[0], 0, 0} for i := 1; i < n; i++ { dp[i][0] = max(dp[i-1][0], dp[i-1][2] - prices[i]) dp[i][1] = dp[i-1][0] + prices[i] dp[i][2] = max(dp[i-1][2], dp[i-1][1]) } return max(dp[n-1][1], dp[n-1][2]) }
We can also reduce the space complexity to O(n)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
funcmaxProfit(prices []int)int { n := len(prices) max := func(a, b int)int { if a > b { return a } return b } hold := -prices[0] cooldown := 0 sold := 0 for i := 1; i < n; i++ { previousSold := sold sold = hold + prices[i] hold = max(hold, cooldown - prices[i]) cooldown = max(cooldown, previousSold) } return max(cooldown, sold) }