Start learning free

LESSON 352 OF 410 · GREEDY

Best Time to Buy and Sell Stock III - JavaScript Solution with O(n*k) Time Complexity

Practice O(n) Read the full explanation

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

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

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

Example 1:

Input: prices = [2,4,1], k = 2
Output: 2
Explanation: Buy on day 1 (price = 2) and sell on day 2 (price = 4), profit = 4-2 = 2.

Example 2:

Input: prices = [3,2,6,5,0,3], k = 2
Output: 7
Explanation: Buy on day 2 (price = 2) and sell on day 3 (price = 6), profit = 6-2 = 4.
             Then buy on day 5 (price = 0) and sell on day 6 (price = 3), profit = 3-0 = 3.
STEP 1 OF 31
    Ctrl + Enter to check

    JAVASCRIPT · GREEDY

    Best Time To Buy Stock in JavaScript, explained

    Understanding the Problem

    The core challenge of this problem is to maximize the profit from at most k transactions. Each transaction consists of buying and then selling the stock. The constraint is that you cannot engage in multiple transactions simultaneously, meaning you must sell the stock before you can buy again.

    This problem is significant in financial markets where traders aim to maximize their returns by strategically buying and selling stocks. A common pitfall is to assume that more transactions always lead to higher profits, which is not necessarily true due to the constraints.

    Approach

    To solve this problem, we need to consider dynamic programming due to the overlapping subproblems and optimal substructure properties.

    Let's start with a naive solution:

    • For each day, try to buy and sell the stock and calculate the profit.
    • Repeat this process for up to k transactions.

    This approach is not optimal because it has a time complexity of O(n^k), which is infeasible for large inputs.

    Optimized Solution

    We can use dynamic programming to optimize this. The idea is to maintain two arrays:

    • dp[i][j]: Maximum profit up to day i with at most j transactions.
    • maxDiff: Maximum difference between the current price and the profit from the previous transaction.

    We update these arrays iteratively to build up the solution.

    Algorithm

    1. Initialize a 2D array dp with dimensions (n+1) x (k+1) to store the maximum profit.

    2. Iterate over the number of transactions from 1 to k.

    3. For each transaction, iterate over the days from 1 to n.

    4. Update the maxDiff and dp arrays based on the current price and previous profits.

    Code Implementation

    
    /**
     * @param {number[]} prices
     * @param {number} k
     * @return {number}
     */
    function maxProfit(prices, k) {
        const n = prices.length;
        if (n == 0) return 0;
    
        // If k is greater than n/2, it's equivalent to unlimited transactions
        if (k > n / 2) {
            let maxProfit = 0;
            for (let i = 1; i < n; i++) {
                if (prices[i] > prices[i - 1]) {
                    maxProfit += prices[i] - prices[i - 1];
                }
            }
            return maxProfit;
        }
    
        // Initialize dp array
        const dp = Array.from({ length: k + 1 }, () => Array(n).fill(0));
    
        for (let t = 1; t <= k; t++) {
            let maxDiff = -prices[0];
            for (let d = 1; d < n; d++) {
                dp[t][d] = Math.max(dp[t][d - 1], prices[d] + maxDiff);
                maxDiff = Math.max(maxDiff, dp[t - 1][d] - prices[d]);
            }
        }
    
        return dp[k][n - 1];
    }
    
    // Example usage:
    console.log(maxProfit([2, 4, 1], 2)); // Output: 2
    console.log(maxProfit([3, 2, 6, 5, 0, 3], 2)); // Output: 7
    

    Complexity Analysis

    The time complexity of this solution is O(n*k) because we have two nested loops iterating over the days and the number of transactions. The space complexity is also O(n*k) due to the 2D dp array.

    Edge Cases

    Consider the following edge cases:

    • Empty prices array: The output should be 0.
    • k is greater than n/2: The algorithm should handle this by treating it as unlimited transactions.
    • All prices are the same: The output should be 0 as no profit can be made.

    Testing

    To test the solution comprehensively, consider the following test cases:

    • Simple cases with small arrays.
    • Cases where k is larger than n/2.
    • Edge cases with empty arrays or arrays with identical prices.

    Use a testing framework like Jest or Mocha to automate the testing process.

    Thinking and Problem-Solving Tips

    When approaching such problems, consider the following tips:

    • Break down the problem into smaller subproblems.
    • Think about the constraints and how they affect the solution.
    • Use dynamic programming for problems with overlapping subproblems and optimal substructure.

    Practice solving similar problems to improve your problem-solving skills.

    Conclusion

    In this blog post, we discussed the problem of finding the maximum profit from at most k transactions. We explored a dynamic programming approach to solve the problem efficiently. Understanding and solving such problems is crucial for developing strong algorithmic skills.

    Additional Resources

    For further reading and practice, consider the following resources:

    Keep going with JavaScriptAbout 340 step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free