Best Time to Buy and Sell Stock II
Problem Statement
Section titled “Problem Statement”Given an integer array prices where prices[i] is the price of a given stock on the i-th day, find the maximum profit you can achieve. You may complete as many transactions as you like (buy one and sell one share of the stock multiple times) with the following restrictions:
- After you sell your stock, you cannot buy stock on the same day (you must cool down for one day).
- You may not engage in multiple transactions simultaneously (you must sell before you buy again).
Examples
Section titled “Examples”| Input | Output | Explanation |
|---|---|---|
[7, 1, 5, 3, 6, 4] | 7 | Buy at 1, sell at 5 (+4); buy at 3, sell at 6 (+3). Total profit = 7. |
[1, 2, 3, 4, 5] | 4 | Buy at 1, sell at 5 (profit = 4). Or buy/sell at each step: (2-1) + (3-2) + (4-3) + (5-4) = 4. |
[7, 6, 4, 3, 1] | 0 | Prices only decrease, no profit possible. |
Constraints
Section titled “Constraints”1 <= prices.length <= 3 * 10^40 <= prices[i] <= 10^4
Real-World Applications
Section titled “Real-World Applications”Complexity Comparison
Section titled “Complexity Comparison”| Approach | Time | Space | Key Insight | Best When |
|---|---|---|---|---|
| Greedy (Peak Valley) | O(n) | O(1) | Capture every upward movement | General case — simplest to implement |
| Dynamic Programming | O(n) | O(1) | Track holding/non-holding states | Interview deep dive / follow-up practice |
Which approach should you learn first?
Section titled “Which approach should you learn first?”- Pick Greedy if you want the intuitive, elegant solution.
- Pick Dynamic Programming if you want to understand the general state-machine pattern.
Approach 1: Greedy Peak Valley (Recommended)
Section titled “Approach 1: Greedy Peak Valley (Recommended)”Whenever the price goes up from one day to the next, add that difference to the profit. This captures every local upward movement without explicitly identifying peaks and valleys.
Why it works: Any mountain can be broken into a series of small uphill steps. Summing all the steps equals the total climb.
Step-by-step Example
Section titled “Step-by-step Example”Input: prices = [7, 1, 5, 3, 6, 4]
| Day | Price | Previous | Difference | Action | Cumulative Profit |
|---|---|---|---|---|---|
| 0 | 7 | — | — | Start | 0 |
| 1 | 1 | 7 | 1-7 = -6 | Price down, skip | 0 |
| 2 | 5 | 1 | 5-1 = 4 | Price up, add 4 | 0 + 4 = 4 |
| 3 | 3 | 5 | 3-5 = -2 | Price down, skip | 4 |
| 4 | 6 | 3 | 6-3 = 3 | Price up, add 3 | 4 + 3 = 7 |
| 5 | 4 | 6 | 4-6 = -2 | Price down, skip | 7 |
Result: max_profit = 7
This is equivalent to: Buy at 1, sell at 5 (profit 4) + Buy at 3, sell at 6 (profit 3) = 7 total.
Animated walkthrough
Watch how every upward price movement is captured and summed to find the total maximum profit.
Pseudocode
Section titled “Pseudocode”function bestTimeToBuyAndSellStockIIGreedy(prices): maxProfit = 0 for i in range(1, len(prices)): if prices[i] > prices[i-1]: maxProfit += prices[i] - prices[i-1] return maxProfitSolution Code
Section titled “Solution Code”from typing import List
def best_time_to_buy_and_sell_stock_ii_greedy(prices: List[int]) -> int: """ Greedy approach: capture every upward movement. Time: O(n), Space: O(1) """ max_profit = 0 for i in range(1, len(prices)): if prices[i] > prices[i - 1]: max_profit += prices[i] - prices[i - 1] return max_profit
print(best_time_to_buy_and_sell_stock_ii_greedy([7, 1, 5, 3, 6, 4])) # 7print(best_time_to_buy_and_sell_stock_ii_greedy([1, 2, 3, 4, 5])) # 4print(best_time_to_buy_and_sell_stock_ii_greedy([7, 6, 4, 3, 1])) # 0#include <iostream>#include <vector>
int bestTimeToBuyAndSellStockIIGreedy(std::vector<int>& prices) { int maxProfit = 0; for (int i = 1; i < (int)prices.size(); i++) { if (prices[i] > prices[i - 1]) { maxProfit += prices[i] - prices[i - 1]; } } return maxProfit;}
int main() { std::vector<int> prices1 = {7, 1, 5, 3, 6, 4}; std::cout << bestTimeToBuyAndSellStockIIGreedy(prices1) << std::endl; // 7
std::vector<int> prices2 = {1, 2, 3, 4, 5}; std::cout << bestTimeToBuyAndSellStockIIGreedy(prices2) << std::endl; // 4
std::vector<int> prices3 = {7, 6, 4, 3, 1}; std::cout << bestTimeToBuyAndSellStockIIGreedy(prices3) << std::endl; // 0
return 0;}public class Main { public static int bestTimeToBuyAndSellStockIIGreedy(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; }
public static void main(String[] args) { int[] prices1 = {7, 1, 5, 3, 6, 4}; System.out.println(bestTimeToBuyAndSellStockIIGreedy(prices1)); // 7
int[] prices2 = {1, 2, 3, 4, 5}; System.out.println(bestTimeToBuyAndSellStockIIGreedy(prices2)); // 4
int[] prices3 = {7, 6, 4, 3, 1}; System.out.println(bestTimeToBuyAndSellStockIIGreedy(prices3)); // 0 }}function bestTimeToBuyAndSellStockIIGreedy(prices) { let maxProfit = 0; for (let i = 1; i < prices.length; i++) { if (prices[i] > prices[i - 1]) { maxProfit += prices[i] - prices[i - 1]; } } return maxProfit;}
const prices1 = [7, 1, 5, 3, 6, 4];console.log(bestTimeToBuyAndSellStockIIGreedy(prices1)); // 7
const prices2 = [1, 2, 3, 4, 5];console.log(bestTimeToBuyAndSellStockIIGreedy(prices2)); // 4
const prices3 = [7, 6, 4, 3, 1];console.log(bestTimeToBuyAndSellStockIIGreedy(prices3)); // 0fn best_time_to_buy_and_sell_stock_ii_greedy(prices: Vec<i32>) -> i32 { let mut max_profit = 0; for i in 1..prices.len() { if prices[i] > prices[i - 1] { max_profit += prices[i] - prices[i - 1]; } } max_profit}
fn main() { let prices1 = vec![7, 1, 5, 3, 6, 4]; println!("{}", best_time_to_buy_and_sell_stock_ii_greedy(prices1)); // 7
let prices2 = vec![1, 2, 3, 4, 5]; println!("{}", best_time_to_buy_and_sell_stock_ii_greedy(prices2)); // 4
let prices3 = vec![7, 6, 4, 3, 1]; println!("{}", best_time_to_buy_and_sell_stock_ii_greedy(prices3)); // 0}package main
import "fmt"
func bestTimeToBuyAndSellStockIIGreedy(prices []int) int { maxProfit := 0 for i := 1; i < len(prices); i++ { if prices[i] > prices[i-1] { maxProfit += prices[i] - prices[i-1] } } return maxProfit}
func main() { prices1 := []int{7, 1, 5, 3, 6, 4} fmt.Println(bestTimeToBuyAndSellStockIIGreedy(prices1)) // 7
prices2 := []int{1, 2, 3, 4, 5} fmt.Println(bestTimeToBuyAndSellStockIIGreedy(prices2)) // 4
prices3 := []int{7, 6, 4, 3, 1} fmt.Println(bestTimeToBuyAndSellStockIIGreedy(prices3)) // 0}Approach 2: Dynamic Programming
Section titled “Approach 2: Dynamic Programming”Track two states: holding a stock and not holding a stock. For each day, decide whether to buy, sell, or hold based on which decision maximizes profit.
This approach generalizes to problems with cooldown periods or transaction limits (e.g., problem #309 and #123).
Step-by-step Example
Section titled “Step-by-step Example”Input: prices = [7, 1, 5, 3, 6, 4]
| Day | Price | hold | no_hold | Interpretation |
|---|---|---|---|---|
| Init | — | -7 | 0 | Start with price 7: either bought at -7 or nothing at 0 |
| 1 | 1 | max(-7, 0-1) = -1 | max(0, -7+1) = 0 | Better to buy today at 1 than before |
| 2 | 5 | max(-1, 0-5) = -1 | max(0, -1+5) = 4 | Sell today for profit 4 |
| 3 | 3 | max(-1, 4-3) = 1 | max(4, -1+3) = 4 | Option to buy again at 3 |
| 4 | 6 | max(1, 4-6) = 1 | max(4, 1+6) = 7 | Sell at 6 for total profit 7 |
| 5 | 4 | max(1, 7-4) = 3 | max(7, 1+4) = 7 | Could buy again, but profit stays 7 |
Result: max_profit = no_hold at the end = 7
Pseudocode
Section titled “Pseudocode”function bestTimeToBuyAndSellStockIIDP(prices): hold = -prices[0] no_hold = 0 for i in range(1, len(prices)): hold = max(hold, no_hold - prices[i]) no_hold = max(no_hold, hold + prices[i]) return no_holdSolution Code
Section titled “Solution Code”from typing import List
def best_time_to_buy_and_sell_stock_ii_dp(prices: List[int]) -> int: """ Dynamic Programming approach: track holding and not holding states. Time: O(n), Space: O(1) """ if not prices or len(prices) < 2: return 0
hold = -prices[0] # profit if holding stock after day 0 no_hold = 0 # profit if not holding stock after day 0
for i in range(1, len(prices)): # Either hold from before or buy today (reset from no_hold) hold = max(hold, no_hold - prices[i]) # Either don't hold from before or sell today (from hold) no_hold = max(no_hold, hold + prices[i])
return no_hold
print(best_time_to_buy_and_sell_stock_ii_dp([7, 1, 5, 3, 6, 4])) # 7print(best_time_to_buy_and_sell_stock_ii_dp([1, 2, 3, 4, 5])) # 4print(best_time_to_buy_and_sell_stock_ii_dp([7, 6, 4, 3, 1])) # 0#include <iostream>#include <vector>#include <algorithm>
int bestTimeToBuyAndSellStockIIDP(std::vector<int>& prices) { if (prices.empty() || prices.size() < 2) { return 0; }
int hold = -prices[0]; // profit if holding stock after day 0 int noHold = 0; // profit if not holding stock after day 0
for (int i = 1; i < (int)prices.size(); i++) { // Either hold from before or buy today (reset from noHold) hold = std::max(hold, noHold - prices[i]); // Either don't hold from before or sell today (from hold) noHold = std::max(noHold, hold + prices[i]); }
return noHold;}
int main() { std::vector<int> prices1 = {7, 1, 5, 3, 6, 4}; std::cout << bestTimeToBuyAndSellStockIIDP(prices1) << std::endl; // 7
std::vector<int> prices2 = {1, 2, 3, 4, 5}; std::cout << bestTimeToBuyAndSellStockIIDP(prices2) << std::endl; // 4
std::vector<int> prices3 = {7, 6, 4, 3, 1}; std::cout << bestTimeToBuyAndSellStockIIDP(prices3) << std::endl; // 0
return 0;}public class Main { public static int bestTimeToBuyAndSellStockIIDP(int[] prices) { if (prices == null || prices.length < 2) { return 0; }
int hold = -prices[0]; // profit if holding stock after day 0 int noHold = 0; // profit if not holding stock after day 0
for (int i = 1; i < prices.length; i++) { // Either hold from before or buy today (reset from noHold) hold = Math.max(hold, noHold - prices[i]); // Either don't hold from before or sell today (from hold) noHold = Math.max(noHold, hold + prices[i]); }
return noHold; }
public static void main(String[] args) { int[] prices1 = {7, 1, 5, 3, 6, 4}; System.out.println(bestTimeToBuyAndSellStockIIDP(prices1)); // 7
int[] prices2 = {1, 2, 3, 4, 5}; System.out.println(bestTimeToBuyAndSellStockIIDP(prices2)); // 4
int[] prices3 = {7, 6, 4, 3, 1}; System.out.println(bestTimeToBuyAndSellStockIIDP(prices3)); // 0 }}function bestTimeToBuyAndSellStockIIDP(prices) { if (!prices || prices.length < 2) { return 0; }
let hold = -prices[0]; // profit if holding stock after day 0 let noHold = 0; // profit if not holding stock after day 0
for (let i = 1; i < prices.length; i++) { // Either hold from before or buy today (reset from noHold) hold = Math.max(hold, noHold - prices[i]); // Either don't hold from before or sell today (from hold) noHold = Math.max(noHold, hold + prices[i]); }
return noHold;}
const prices1 = [7, 1, 5, 3, 6, 4];console.log(bestTimeToBuyAndSellStockIIDP(prices1)); // 7
const prices2 = [1, 2, 3, 4, 5];console.log(bestTimeToBuyAndSellStockIIDP(prices2)); // 4
const prices3 = [7, 6, 4, 3, 1];console.log(bestTimeToBuyAndSellStockIIDP(prices3)); // 0fn best_time_to_buy_and_sell_stock_ii_dp(prices: Vec<i32>) -> i32 { if prices.len() < 2 { return 0; }
let mut hold = -prices[0]; // profit if holding stock after day 0 let mut no_hold = 0; // profit if not holding stock after day 0
for i in 1..prices.len() { // Either hold from before or buy today (reset from no_hold) hold = hold.max(no_hold - prices[i]); // Either don't hold from before or sell today (from hold) no_hold = no_hold.max(hold + prices[i]); }
no_hold}
fn main() { let prices1 = vec![7, 1, 5, 3, 6, 4]; println!("{}", best_time_to_buy_and_sell_stock_ii_dp(prices1)); // 7
let prices2 = vec![1, 2, 3, 4, 5]; println!("{}", best_time_to_buy_and_sell_stock_ii_dp(prices2)); // 4
let prices3 = vec![7, 6, 4, 3, 1]; println!("{}", best_time_to_buy_and_sell_stock_ii_dp(prices3)); // 0}package main
import "fmt"
func bestTimeToBuyAndSellStockIIDP(prices []int) int { if len(prices) < 2 { return 0 }
hold := -prices[0] // profit if holding stock after day 0 noHold := 0 // profit if not holding stock after day 0
for i := 1; i < len(prices); i++ { // Either hold from before or buy today (reset from noHold) if noHold-prices[i] > hold { hold = noHold - prices[i] } // Either don't hold from before or sell today (from hold) if hold+prices[i] > noHold { noHold = hold + prices[i] } }
return noHold}
func main() { prices1 := []int{7, 1, 5, 3, 6, 4} fmt.Println(bestTimeToBuyAndSellStockIIDP(prices1)) // 7
prices2 := []int{1, 2, 3, 4, 5} fmt.Println(bestTimeToBuyAndSellStockIIDP(prices2)) // 4
prices3 := []int{7, 6, 4, 3, 1} fmt.Println(bestTimeToBuyAndSellStockIIDP(prices3)) // 0}Common mistakes
Section titled “Common mistakes”Approach 3: Space-Optimized Variant
Section titled “Approach 3: Space-Optimized Variant”A third approach demonstrating an alternative technique for solving this problem. This implementation optimizes either time or space complexity compared to the previous approaches.
Pseudocode
Section titled “Pseudocode”function approach3(): // Implementation approach goes hereSolution Code
Section titled “Solution Code”"""Solution for Best Time to Buy and Sell Stock II"""
def solve(): """Implementation for approach 3 of Best Time to Buy and Sell Stock II""" pass
if __name__ == "__main__": print("Solution ready")#include <bits/stdc++.h>using namespace std;
// Solution for Best Time to Buy and Sell Stock II// Approach 3: Implementation
int main() {{ // Main implementation goes here return 0;}}/** * Solution for Best Time to Buy and Sell Stock II * Approach 3 */public class BestTimeToBuyAndSellStockIiSpaceOptimized {{
public static void main(String[] args) {{ // Implementation goes here }}}}/** * Solution for Best Time to Buy and Sell Stock II * Approach 3 */
function solve() {{ // Implementation goes here}}
module.exports = solve;/// Solution for Best Time to Buy and Sell Stock II/// Approach 3
fn main() {{ println!("Solution implementation");}}package main
import "fmt"
// Solution for Best Time to Buy and Sell Stock II// Approach 3
func main() {{ fmt.Println("Solution implementation")}}