122. Best Time to Buy and Sell Stock II
Problem Description
You have an array prices
where prices[i]
represents the stock price on day i
.
You can perform multiple transactions (buy and sell) with these rules:
- You can buy and/or sell the stock on any day
- You can only hold at most one share at a time (must sell before buying again)
- You can buy and sell on the same day
Your goal is to find the maximum total profit you can achieve by making as many transactions as you want.
For example, if prices are [1, 3, 2, 5]
:
- Buy on day 0 at price 1, sell on day 1 at price 3: profit = 2
- Buy on day 2 at price 2, sell on day 3 at price 5: profit = 3
- Total maximum profit = 2 + 3 = 5
The solution uses a greedy approach: whenever the price increases from one day to the next, we capture that profit by buying on the previous day and selling on the current day. The code sum(max(0, b - a) for a, b in pairwise(prices))
calculates the sum of all positive price differences between consecutive days, which gives us the maximum possible profit.
Intuition
The key insight is that since we can make as many transactions as we want, we should capture every possible profit opportunity.
Think about it this way: if you could see the future stock prices, when would you trade? You'd want to buy right before every price increase and sell right before every price decrease.
Looking at a price graph, imagine it as a series of peaks and valleys. The maximum profit comes from buying at every valley and selling at every peak. But here's the clever part - this is equivalent to capturing all the upward movements in the price.
For example, if prices go from 1 → 3 → 2 → 5:
- From day 0 to 1: price increases by 2 (we profit)
- From day 1 to 2: price decreases by 1 (we don't trade)
- From day 2 to 3: price increases by 3 (we profit)
Notice that we only care about positive differences between consecutive days. Any time prices[i+1] > prices[i]
, we can profit by buying on day i
and selling on day i+1
.
This leads to the elegant solution: sum all positive differences between consecutive days. Even if there's a multi-day upward trend like 1 → 2 → 3 → 4, capturing each daily gain (1→2, 2→3, 3→4) gives the same total profit as holding from 1→4. This is why sum(max(0, b - a) for a, b in pairwise(prices))
works - it adds up all the profitable daily movements while ignoring the losses.
Solution Approach
The solution implements a greedy algorithm that captures profit from every price increase between consecutive days.
Implementation Details:
-
Iterate through consecutive price pairs: Using
pairwise(prices)
, we get pairs of consecutive prices(a, b)
wherea
is the price on dayi
andb
is the price on dayi+1
. -
Calculate daily profit: For each pair, we compute
b - a
:- If
b - a > 0
: The price increased, so we make a profit by buying at pricea
and selling at priceb
- If
b - a ≤ 0
: The price decreased or stayed the same, so we don't trade (profit = 0)
- If
-
Use
max(0, b - a)
: This ensures we only count positive differences (profits) and ignore negative differences (losses). -
Sum all profits: The
sum()
function adds up all the individual daily profits to get the maximum total profit.
Why this works:
The greedy approach is optimal because:
- We're not limited in the number of transactions
- We can't hold multiple stocks simultaneously
- Any profitable sequence can be decomposed into individual daily gains
For example, if prices are [1, 2, 3, 4]
:
- Instead of one transaction (buy at 1, sell at 4) with profit = 3
- We can do three transactions: (1→2) + (2→3) + (3→4) = 1 + 1 + 1 = 3
- Both give the same total profit!
The time complexity is O(n)
where n
is the length of the prices array, and space complexity is O(1)
as we only track the running sum.
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through the solution with prices = [7, 1, 5, 3, 6, 4]
.
Step 1: Identify consecutive price pairs
Using pairwise(prices)
, we get:
- Pair 1: (7, 1) → day 0 to day 1
- Pair 2: (1, 5) → day 1 to day 2
- Pair 3: (5, 3) → day 2 to day 3
- Pair 4: (3, 6) → day 3 to day 4
- Pair 5: (6, 4) → day 4 to day 5
Step 2: Calculate profit for each pair
For each pair (a, b), we compute max(0, b - a)
:
- Pair 1: max(0, 1 - 7) = max(0, -6) = 0 (price dropped, don't trade)
- Pair 2: max(0, 5 - 1) = max(0, 4) = 4 (price rose, buy at 1, sell at 5)
- Pair 3: max(0, 3 - 5) = max(0, -2) = 0 (price dropped, don't trade)
- Pair 4: max(0, 6 - 3) = max(0, 3) = 3 (price rose, buy at 3, sell at 6)
- Pair 5: max(0, 4 - 6) = max(0, -2) = 0 (price dropped, don't trade)
Step 3: Sum all profits Total profit = 0 + 4 + 0 + 3 + 0 = 7
Verification: Our algorithm effectively executed these transactions:
- Buy on day 1 at price 1, sell on day 2 at price 5: profit = 4
- Buy on day 3 at price 3, sell on day 4 at price 6: profit = 3
- Total profit = 4 + 3 = 7
This greedy approach captured every upward price movement while avoiding all downward movements, achieving the maximum possible profit.
Solution Implementation
1class Solution:
2 def maxProfit(self, prices: List[int]) -> int:
3 """
4 Calculate maximum profit from multiple stock transactions.
5 Buy and sell whenever there's a price increase from one day to the next.
6
7 Args:
8 prices: List of stock prices where prices[i] is the price on day i
9
10 Returns:
11 Maximum profit achievable from multiple buy-sell transactions
12 """
13 # Initialize total profit
14 total_profit = 0
15
16 # Iterate through consecutive price pairs
17 for i in range(1, len(prices)):
18 # Calculate profit from buying on day i-1 and selling on day i
19 daily_profit = prices[i] - prices[i-1]
20
21 # Only add positive profits (buy low, sell high on consecutive days)
22 if daily_profit > 0:
23 total_profit += daily_profit
24
25 return total_profit
26
1class Solution {
2 /**
3 * Calculate the maximum profit from stock trading with unlimited transactions.
4 * Strategy: Buy and sell whenever there's a profit opportunity (greedy approach).
5 *
6 * @param prices Array of stock prices where prices[i] is the price on day i
7 * @return Maximum profit achievable
8 */
9 public int maxProfit(int[] prices) {
10 int totalProfit = 0;
11
12 // Iterate through each day starting from day 1
13 for (int day = 1; day < prices.length; day++) {
14 // Calculate profit if we bought yesterday and sold today
15 int dailyProfit = prices[day] - prices[day - 1];
16
17 // Only add positive profits (equivalent to buying before price increases)
18 // If profit is negative, we simply don't trade (add 0)
19 totalProfit += Math.max(0, dailyProfit);
20 }
21
22 return totalProfit;
23 }
24}
25
1class Solution {
2public:
3 int maxProfit(vector<int>& prices) {
4 // Initialize total profit to 0
5 int totalProfit = 0;
6
7 // Iterate through the prices array starting from index 1
8 for (int i = 1; i < prices.size(); ++i) {
9 // If today's price is higher than yesterday's price,
10 // we can make a profit by buying yesterday and selling today
11 // Add the profit to our total (if negative, add 0 instead)
12 totalProfit += max(0, prices[i] - prices[i - 1]);
13 }
14
15 // Return the maximum total profit achievable
16 return totalProfit;
17 }
18};
19
1/**
2 * Calculates the maximum profit from buying and selling stocks multiple times.
3 * Uses a greedy approach: buy and sell whenever there's a profit opportunity.
4 *
5 * @param prices - Array of stock prices where prices[i] is the price on day i
6 * @returns The maximum profit that can be achieved
7 */
8function maxProfit(prices: number[]): number {
9 // Initialize total profit accumulator
10 let totalProfit = 0;
11
12 // Iterate through prices starting from day 1
13 for (let i = 1; i < prices.length; i++) {
14 // If today's price is higher than yesterday's, capture the profit
15 // Otherwise, add 0 (no transaction)
16 const dailyProfit = prices[i] - prices[i - 1];
17 totalProfit += Math.max(0, dailyProfit);
18 }
19
20 return totalProfit;
21}
22
Time and Space Complexity
The time complexity is O(n)
, where n
is the length of the prices
array. This is because the pairwise
function iterates through the array once to create consecutive pairs, and the sum
function with the generator expression processes each pair exactly once. The total number of pairs generated is n-1
, resulting in linear time complexity.
The space complexity is O(1)
. While pairwise
creates an iterator that generates pairs on-the-fly, it doesn't store all pairs in memory simultaneously. The generator expression max(0, b - a) for a, b in pairwise(prices)
also computes values one at a time without storing them. Only a constant amount of extra space is used for variables like the running sum and the current pair being processed.
Common Pitfalls
1. Misunderstanding the Greedy Strategy as "Cheating"
Pitfall: Many people initially think the greedy approach is incorrect because it seems like we're "looking into the future" by buying and selling on consecutive days whenever there's a profit. They might think: "How can I know tomorrow's price when deciding to buy today?"
Why it's not a problem: The algorithm doesn't actually require future knowledge. We're analyzing historical data to find the maximum profit that could have been achieved. The consecutive day trading is mathematically equivalent to holding through multiple profitable days.
Example:
- Prices:
[1, 2, 3, 4]
- Greedy: Buy at 1, sell at 2 (+1), buy at 2, sell at 3 (+1), buy at 3, sell at 4 (+1) = 3 profit
- Alternative: Buy at 1, sell at 4 = 3 profit
- Both strategies yield the same result!
2. Trying to Track Buy/Sell States Explicitly
Pitfall: Overcomplicating the solution by maintaining boolean flags or state variables to track whether we currently own stock:
# Overcomplicated approach
holding_stock = False
buy_price = 0
total_profit = 0
for i in range(len(prices)):
if not holding_stock and i < len(prices) - 1 and prices[i] < prices[i + 1]:
buy_price = prices[i]
holding_stock = True
elif holding_stock and (i == len(prices) - 1 or prices[i] > prices[i + 1]):
total_profit += prices[i] - buy_price
holding_stock = False
Solution: The greedy approach eliminates the need for state tracking. Simply sum all positive differences between consecutive days.
3. Edge Case: Single Element or Empty Array
Pitfall: Not handling arrays with fewer than 2 elements properly, which could cause index out of bounds errors.
Solution: The provided solution naturally handles this since range(1, len(prices))
produces an empty range when len(prices) < 2
, resulting in a total profit of 0 (which is correct).
4. Integer Overflow in Other Languages
Pitfall: In languages with fixed-size integers (like Java or C++), summing many large profits could cause overflow.
Solution: In Python, integers have arbitrary precision so this isn't an issue. In other languages, use appropriate data types (e.g., long long
in C++ or long
in Java) or check for overflow conditions.
5. Misunderstanding When to Buy and Sell
Pitfall: Thinking you need to find explicit "valleys" (local minima) and "peaks" (local maxima) for buy/sell points:
# Unnecessarily complex peak-valley approach
i = 0
valley = peak = prices[0]
max_profit = 0
while i < len(prices) - 1:
while i < len(prices) - 1 and prices[i] >= prices[i + 1]:
i += 1
valley = prices[i]
while i < len(prices) - 1 and prices[i] <= prices[i + 1]:
i += 1
peak = prices[i]
max_profit += peak - valley
Solution: The greedy approach is simpler and equivalent - just capture every upward price movement, regardless of whether it's part of a larger trend or not.
Which data structure is used to implement priority queue?
Recommended Readings
Greedy Introduction div class responsive iframe iframe src https www youtube com embed WTslqPbj7I title YouTube video player frameborder 0 allow accelerometer autoplay clipboard write encrypted media gyroscope picture in picture web share allowfullscreen iframe div When do we use greedy Greedy algorithms tend to solve optimization problems Typically they will ask you to calculate the max min of some value Commonly you may see this phrased in the problem as max min longest shortest largest smallest etc These keywords can be identified by just scanning
What is Dynamic Programming Prerequisite DFS problems dfs_intro Backtracking problems backtracking Memoization problems memoization_intro Pruning problems backtracking_pruning Dynamic programming is an algorithmic optimization technique that breaks down a complicated problem into smaller overlapping sub problems in a recursive manner and uses solutions to the sub problems to construct a solution
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Want a Structured Path to Master System Design Too? Don’t Miss This!