Below is the implementation of the stock trader with cooldown:
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238/** * Brute Force Recursive Approach * Time Complexity: O(2^n) - Exponential due to exploring all possible buy/sell combinations * Space Complexity: O(n) - Due to recursion stack * * This approach explores all possible decisions at each step using recursion. * * @param {number[]} prices - Array of stock prices * @return {number} - Maximum profit */function maxProfitBruteForce(prices) { // Helper function to recursively find the maximum profit function dfs(i, holding) { // Base case: If we've gone through all prices, no more profit can be made if (i >= prices.length) { return 0; } if (holding) { // Option 1: Sell the stock and skip the next day (cooldown) const sellProfit = prices[i] + dfs(i + 2, false); // Option 2: Continue holding the stock const holdProfit = dfs(i + 1, true); // Return the maximum profit between selling and holding return Math.max(sellProfit, holdProfit); } else { // Option 1: Buy a stock const buyProfit = -prices[i] + dfs(i + 1, true); // Option 2: Do nothing const waitProfit = dfs(i + 1, false); // Return the maximum profit between buying and waiting return Math.max(buyProfit, waitProfit); } } // Start the recursion from day 0 with no stock return dfs(0, false);} /** * Memoized Recursive Approach * Time Complexity: O(n) - Each state is computed only once * Space Complexity: O(n) - For memoization and recursion stack * * This approach optimizes the brute force solution by caching results of subproblems. * * @param {number[]} prices - Array of stock prices * @return {number} - Maximum profit */function maxProfitMemoized(prices) { // Create a memo object to store results of subproblems const memo = {}; // Helper function to recursively find the maximum profit with memoization function dfs(i, holding) { // Base case: If we've gone through all prices, no more profit can be made if (i >= prices.length) { return 0; } // Create a unique key for the current state const key = `${i},${holding}`; // If we've already computed this state, return the cached result if (key in memo) { return memo[key]; } let profit; if (holding) { // Option 1: Sell the stock and skip the next day (cooldown) const sellProfit = prices[i] + dfs(i + 2, false); // Option 2: Continue holding the stock const holdProfit = dfs(i + 1, true); profit = Math.max(sellProfit, holdProfit); } else { // Option 1: Buy a stock const buyProfit = -prices[i] + dfs(i + 1, true); // Option 2: Do nothing const waitProfit = dfs(i + 1, false); profit = Math.max(buyProfit, waitProfit); } // Cache the result before returning memo[key] = profit; return profit; } // Start the recursion from day 0 with no stock return dfs(0, false);} /** * State Machine Approach * Time Complexity: O(n) - We iterate through the prices array once * Space Complexity: O(1) - We use only a constant amount of extra space * * This approach models the problem as a state machine with three states: * 1. hold: We are holding a stock * 2. sold: We just sold a stock * 3. rest: We are not holding a stock and not in cooldown * * @param {number[]} prices - Array of stock prices * @return {number} - Maximum profit */function maxProfit(prices) { // Edge case: If there are no prices or only one price, no profit can be made if (prices.length <= 1) { return 0; } // Initialize state variables let hold = -prices[0]; // Maximum profit with a stock (initially we buy the first stock) let sold = 0; // Maximum profit after selling (initially we haven't sold anything) let rest = 0; // Maximum profit at rest (initially we haven't done anything) // Iterate through the prices starting from the second day for (let i = 1; i < prices.length; i++) { // We need to save the current hold value before updating it // because we'll need the old value to calculate the new sold value const prevHold = hold; // Update hold: either continue holding or buy a new stock after resting hold = Math.max(hold, rest - prices[i]); // Update sold: sell the stock we were holding sold = prevHold + prices[i]; // Update rest: either continue resting or come from sold state (after cooldown) rest = Math.max(rest, sold); } // The maximum profit will be the maximum of sold and rest states // (we don't want to end up holding a stock) return Math.max(sold, rest);} /** * Dynamic Programming Approach * Time Complexity: O(n) - We iterate through the prices array once * Space Complexity: O(n) - We use three arrays of size n * * This approach uses dynamic programming to track the maximum profit * for each state at each day. * * @param {number[]} prices - Array of stock prices * @return {number} - Maximum profit */function maxProfitDP(prices) { // Edge case: If there are no prices or only one price, no profit can be made if (prices.length <= 1) { return 0; } const n = prices.length; // Create arrays to track the maximum profit for each state at each day const buy = new Array(n).fill(0); // Maximum profit if we have a stock at the end of day i const sell = new Array(n).fill(0); // Maximum profit if we sell a stock at day i const cooldown = new Array(n).fill(0); // Maximum profit if we are in cooldown at day i // Initialize the first day: we can only buy on the first day buy[0] = -prices[0]; // Fill the DP arrays for each day for (let i = 1; i < n; i++) { // Buy: either continue from previous buy state or buy after cooldown buy[i] = Math.max(buy[i - 1], cooldown[i - 1] - prices[i]); // Sell: either continue from previous sell state or sell the stock we bought sell[i] = Math.max(sell[i - 1], buy[i - 1] + prices[i]); // Cooldown: either continue from previous cooldown state or enter cooldown after selling cooldown[i] = Math.max(cooldown[i - 1], sell[i - 1]); } // The maximum profit will be the maximum of sell and cooldown states at the last day return Math.max(sell[n - 1], cooldown[n - 1]);} /** * Optimized Dynamic Programming Approach * Time Complexity: O(n) - We iterate through the prices array once * Space Complexity: O(1) - We use only a constant amount of extra space * * This approach optimizes the space usage of the DP approach by using * variables instead of arrays. * * @param {number[]} prices - Array of stock prices * @return {number} - Maximum profit */function maxProfitOptimized(prices) { // Edge case: If there are no prices or only one price, no profit can be made if (prices.length <= 1) { return 0; } // Initialize state variables for the first day let buy = -prices[0]; let sell = 0; let cooldown = 0; // Iterate through the prices starting from the second day for (let i = 1; i < prices.length; i++) { // We need to save the current values before updating them // to avoid using updated values in the same iteration const prevBuy = buy; const prevSell = sell; // Update buy: either continue from previous buy state or buy after cooldown buy = Math.max(buy, cooldown - prices[i]); // Update sell: either continue from previous sell state or sell the stock we bought sell = Math.max(sell, prevBuy + prices[i]); // Update cooldown: either continue from previous cooldown state or enter cooldown after selling cooldown = Math.max(cooldown, prevSell); } // The maximum profit will be the maximum of sell and cooldown states return Math.max(sell, cooldown);} // Test casesconsole.log("State Machine Approach:");console.log(maxProfit([1, 2, 3, 0, 2])); // 3console.log(maxProfit([1])); // 0 console.log("\nDP Approach:");console.log(maxProfitDP([1, 2, 3, 0, 2])); // 3console.log(maxProfitDP([1])); // 0 console.log("\nOptimized DP Approach:");console.log(maxProfitOptimized([1, 2, 3, 0, 2])); // 3console.log(maxProfitOptimized([1])); // 0 // Note: The brute force and memoized approaches are not tested with large inputs// as they would be too slow or cause stack overflow for large inputs
Let's break down the implementation:
Implement the stock trader with cooldown solution in different programming languages.
Below is the implementation of the stock trader with cooldown in different programming languages. Select a language tab to view the corresponding code.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238/** * Brute Force Recursive Approach * Time Complexity: O(2^n) - Exponential due to exploring all possible buy/sell combinations * Space Complexity: O(n) - Due to recursion stack * * This approach explores all possible decisions at each step using recursion. * * @param {number[]} prices - Array of stock prices * @return {number} - Maximum profit */function maxProfitBruteForce(prices) { // Helper function to recursively find the maximum profit function dfs(i, holding) { // Base case: If we've gone through all prices, no more profit can be made if (i >= prices.length) { return 0; } if (holding) { // Option 1: Sell the stock and skip the next day (cooldown) const sellProfit = prices[i] + dfs(i + 2, false); // Option 2: Continue holding the stock const holdProfit = dfs(i + 1, true); // Return the maximum profit between selling and holding return Math.max(sellProfit, holdProfit); } else { // Option 1: Buy a stock const buyProfit = -prices[i] + dfs(i + 1, true); // Option 2: Do nothing const waitProfit = dfs(i + 1, false); // Return the maximum profit between buying and waiting return Math.max(buyProfit, waitProfit); } } // Start the recursion from day 0 with no stock return dfs(0, false);} /** * Memoized Recursive Approach * Time Complexity: O(n) - Each state is computed only once * Space Complexity: O(n) - For memoization and recursion stack * * This approach optimizes the brute force solution by caching results of subproblems. * * @param {number[]} prices - Array of stock prices * @return {number} - Maximum profit */function maxProfitMemoized(prices) { // Create a memo object to store results of subproblems const memo = {}; // Helper function to recursively find the maximum profit with memoization function dfs(i, holding) { // Base case: If we've gone through all prices, no more profit can be made if (i >= prices.length) { return 0; } // Create a unique key for the current state const key = `${i},${holding}`; // If we've already computed this state, return the cached result if (key in memo) { return memo[key]; } let profit; if (holding) { // Option 1: Sell the stock and skip the next day (cooldown) const sellProfit = prices[i] + dfs(i + 2, false); // Option 2: Continue holding the stock const holdProfit = dfs(i + 1, true); profit = Math.max(sellProfit, holdProfit); } else { // Option 1: Buy a stock const buyProfit = -prices[i] + dfs(i + 1, true); // Option 2: Do nothing const waitProfit = dfs(i + 1, false); profit = Math.max(buyProfit, waitProfit); } // Cache the result before returning memo[key] = profit; return profit; } // Start the recursion from day 0 with no stock return dfs(0, false);} /** * State Machine Approach * Time Complexity: O(n) - We iterate through the prices array once * Space Complexity: O(1) - We use only a constant amount of extra space * * This approach models the problem as a state machine with three states: * 1. hold: We are holding a stock * 2. sold: We just sold a stock * 3. rest: We are not holding a stock and not in cooldown * * @param {number[]} prices - Array of stock prices * @return {number} - Maximum profit */function maxProfit(prices) { // Edge case: If there are no prices or only one price, no profit can be made if (prices.length <= 1) { return 0; } // Initialize state variables let hold = -prices[0]; // Maximum profit with a stock (initially we buy the first stock) let sold = 0; // Maximum profit after selling (initially we haven't sold anything) let rest = 0; // Maximum profit at rest (initially we haven't done anything) // Iterate through the prices starting from the second day for (let i = 1; i < prices.length; i++) { // We need to save the current hold value before updating it // because we'll need the old value to calculate the new sold value const prevHold = hold; // Update hold: either continue holding or buy a new stock after resting hold = Math.max(hold, rest - prices[i]); // Update sold: sell the stock we were holding sold = prevHold + prices[i]; // Update rest: either continue resting or come from sold state (after cooldown) rest = Math.max(rest, sold); } // The maximum profit will be the maximum of sold and rest states // (we don't want to end up holding a stock) return Math.max(sold, rest);} /** * Dynamic Programming Approach * Time Complexity: O(n) - We iterate through the prices array once * Space Complexity: O(n) - We use three arrays of size n * * This approach uses dynamic programming to track the maximum profit * for each state at each day. * * @param {number[]} prices - Array of stock prices * @return {number} - Maximum profit */function maxProfitDP(prices) { // Edge case: If there are no prices or only one price, no profit can be made if (prices.length <= 1) { return 0; } const n = prices.length; // Create arrays to track the maximum profit for each state at each day const buy = new Array(n).fill(0); // Maximum profit if we have a stock at the end of day i const sell = new Array(n).fill(0); // Maximum profit if we sell a stock at day i const cooldown = new Array(n).fill(0); // Maximum profit if we are in cooldown at day i // Initialize the first day: we can only buy on the first day buy[0] = -prices[0]; // Fill the DP arrays for each day for (let i = 1; i < n; i++) { // Buy: either continue from previous buy state or buy after cooldown buy[i] = Math.max(buy[i - 1], cooldown[i - 1] - prices[i]); // Sell: either continue from previous sell state or sell the stock we bought sell[i] = Math.max(sell[i - 1], buy[i - 1] + prices[i]); // Cooldown: either continue from previous cooldown state or enter cooldown after selling cooldown[i] = Math.max(cooldown[i - 1], sell[i - 1]); } // The maximum profit will be the maximum of sell and cooldown states at the last day return Math.max(sell[n - 1], cooldown[n - 1]);} /** * Optimized Dynamic Programming Approach * Time Complexity: O(n) - We iterate through the prices array once * Space Complexity: O(1) - We use only a constant amount of extra space * * This approach optimizes the space usage of the DP approach by using * variables instead of arrays. * * @param {number[]} prices - Array of stock prices * @return {number} - Maximum profit */function maxProfitOptimized(prices) { // Edge case: If there are no prices or only one price, no profit can be made if (prices.length <= 1) { return 0; } // Initialize state variables for the first day let buy = -prices[0]; let sell = 0; let cooldown = 0; // Iterate through the prices starting from the second day for (let i = 1; i < prices.length; i++) { // We need to save the current values before updating them // to avoid using updated values in the same iteration const prevBuy = buy; const prevSell = sell; // Update buy: either continue from previous buy state or buy after cooldown buy = Math.max(buy, cooldown - prices[i]); // Update sell: either continue from previous sell state or sell the stock we bought sell = Math.max(sell, prevBuy + prices[i]); // Update cooldown: either continue from previous cooldown state or enter cooldown after selling cooldown = Math.max(cooldown, prevSell); } // The maximum profit will be the maximum of sell and cooldown states return Math.max(sell, cooldown);} // Test casesconsole.log("State Machine Approach:");console.log(maxProfit([1, 2, 3, 0, 2])); // 3console.log(maxProfit([1])); // 0 console.log("\nDP Approach:");console.log(maxProfitDP([1, 2, 3, 0, 2])); // 3console.log(maxProfitDP([1])); // 0 console.log("\nOptimized DP Approach:");console.log(maxProfitOptimized([1, 2, 3, 0, 2])); // 3console.log(maxProfitOptimized([1])); // 0 // Note: The brute force and memoized approaches are not tested with large inputs// as they would be too slow or cause stack overflow for large inputs
First, understand that we need to find the maximum profit from buying and selling stocks with a cooldown period of one day after selling. This means after selling a stock, we must wait one day before buying again.
Start with a recursive approach that explores all possible decisions at each step: buy, sell, or do nothing. For each day, we need to consider our current state (whether we're holding a stock or not) and make the best decision.
Notice that the recursive approach has overlapping subproblems. We can use memoization to avoid recalculating the same states multiple times by storing the results in a cache.
For the iterative approaches, define state variables to track the maximum profit in different states: holding a stock, just sold a stock, and resting (not holding and not in cooldown).
For each day, update the state variables based on the possible actions: buy, sell, or do nothing. The key insight is to understand how these states transition into each other.
Ensure that after selling a stock, we cannot buy on the next day (cooldown period). This is handled differently in each approach: in the recursive approach, we skip a day after selling; in the DP approach, we use separate arrays for different states.
Observe that we only need information from at most two days ahead. We can optimize the space usage by using variables instead of arrays, reducing the space complexity from O(n) to O(1).
Return the maximum profit, which will be the maximum of the sold and rest states (we don't want to end up holding a stock).
Modify the code to implement an alternative approach and test it with the same examples.
Implement a function that solves the stock trader with cooldown problem using a different approach than shown above.
Handle the case where the input array is empty (return 0 since no transactions can be made).
Handle the case where the input array has only one element (return 0 since we need at least two days to make a profit).
Handle the case where prices are strictly decreasing (return 0 since we can't make any profit).
Ensure that after selling a stock, we cannot buy on the next day (cooldown period).
Handle multiple buy-sell transactions to maximize profit, respecting the cooldown period.
Below is the implementation of the stock trader with cooldown:
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238/** * Brute Force Recursive Approach * Time Complexity: O(2^n) - Exponential due to exploring all possible buy/sell combinations * Space Complexity: O(n) - Due to recursion stack * * This approach explores all possible decisions at each step using recursion. * * @param {number[]} prices - Array of stock prices * @return {number} - Maximum profit */function maxProfitBruteForce(prices) { // Helper function to recursively find the maximum profit function dfs(i, holding) { // Base case: If we've gone through all prices, no more profit can be made if (i >= prices.length) { return 0; } if (holding) { // Option 1: Sell the stock and skip the next day (cooldown) const sellProfit = prices[i] + dfs(i + 2, false); // Option 2: Continue holding the stock const holdProfit = dfs(i + 1, true); // Return the maximum profit between selling and holding return Math.max(sellProfit, holdProfit); } else { // Option 1: Buy a stock const buyProfit = -prices[i] + dfs(i + 1, true); // Option 2: Do nothing const waitProfit = dfs(i + 1, false); // Return the maximum profit between buying and waiting return Math.max(buyProfit, waitProfit); } } // Start the recursion from day 0 with no stock return dfs(0, false);} /** * Memoized Recursive Approach * Time Complexity: O(n) - Each state is computed only once * Space Complexity: O(n) - For memoization and recursion stack * * This approach optimizes the brute force solution by caching results of subproblems. * * @param {number[]} prices - Array of stock prices * @return {number} - Maximum profit */function maxProfitMemoized(prices) { // Create a memo object to store results of subproblems const memo = {}; // Helper function to recursively find the maximum profit with memoization function dfs(i, holding) { // Base case: If we've gone through all prices, no more profit can be made if (i >= prices.length) { return 0; } // Create a unique key for the current state const key = `${i},${holding}`; // If we've already computed this state, return the cached result if (key in memo) { return memo[key]; } let profit; if (holding) { // Option 1: Sell the stock and skip the next day (cooldown) const sellProfit = prices[i] + dfs(i + 2, false); // Option 2: Continue holding the stock const holdProfit = dfs(i + 1, true); profit = Math.max(sellProfit, holdProfit); } else { // Option 1: Buy a stock const buyProfit = -prices[i] + dfs(i + 1, true); // Option 2: Do nothing const waitProfit = dfs(i + 1, false); profit = Math.max(buyProfit, waitProfit); } // Cache the result before returning memo[key] = profit; return profit; } // Start the recursion from day 0 with no stock return dfs(0, false);} /** * State Machine Approach * Time Complexity: O(n) - We iterate through the prices array once * Space Complexity: O(1) - We use only a constant amount of extra space * * This approach models the problem as a state machine with three states: * 1. hold: We are holding a stock * 2. sold: We just sold a stock * 3. rest: We are not holding a stock and not in cooldown * * @param {number[]} prices - Array of stock prices * @return {number} - Maximum profit */function maxProfit(prices) { // Edge case: If there are no prices or only one price, no profit can be made if (prices.length <= 1) { return 0; } // Initialize state variables let hold = -prices[0]; // Maximum profit with a stock (initially we buy the first stock) let sold = 0; // Maximum profit after selling (initially we haven't sold anything) let rest = 0; // Maximum profit at rest (initially we haven't done anything) // Iterate through the prices starting from the second day for (let i = 1; i < prices.length; i++) { // We need to save the current hold value before updating it // because we'll need the old value to calculate the new sold value const prevHold = hold; // Update hold: either continue holding or buy a new stock after resting hold = Math.max(hold, rest - prices[i]); // Update sold: sell the stock we were holding sold = prevHold + prices[i]; // Update rest: either continue resting or come from sold state (after cooldown) rest = Math.max(rest, sold); } // The maximum profit will be the maximum of sold and rest states // (we don't want to end up holding a stock) return Math.max(sold, rest);} /** * Dynamic Programming Approach * Time Complexity: O(n) - We iterate through the prices array once * Space Complexity: O(n) - We use three arrays of size n * * This approach uses dynamic programming to track the maximum profit * for each state at each day. * * @param {number[]} prices - Array of stock prices * @return {number} - Maximum profit */function maxProfitDP(prices) { // Edge case: If there are no prices or only one price, no profit can be made if (prices.length <= 1) { return 0; } const n = prices.length; // Create arrays to track the maximum profit for each state at each day const buy = new Array(n).fill(0); // Maximum profit if we have a stock at the end of day i const sell = new Array(n).fill(0); // Maximum profit if we sell a stock at day i const cooldown = new Array(n).fill(0); // Maximum profit if we are in cooldown at day i // Initialize the first day: we can only buy on the first day buy[0] = -prices[0]; // Fill the DP arrays for each day for (let i = 1; i < n; i++) { // Buy: either continue from previous buy state or buy after cooldown buy[i] = Math.max(buy[i - 1], cooldown[i - 1] - prices[i]); // Sell: either continue from previous sell state or sell the stock we bought sell[i] = Math.max(sell[i - 1], buy[i - 1] + prices[i]); // Cooldown: either continue from previous cooldown state or enter cooldown after selling cooldown[i] = Math.max(cooldown[i - 1], sell[i - 1]); } // The maximum profit will be the maximum of sell and cooldown states at the last day return Math.max(sell[n - 1], cooldown[n - 1]);} /** * Optimized Dynamic Programming Approach * Time Complexity: O(n) - We iterate through the prices array once * Space Complexity: O(1) - We use only a constant amount of extra space * * This approach optimizes the space usage of the DP approach by using * variables instead of arrays. * * @param {number[]} prices - Array of stock prices * @return {number} - Maximum profit */function maxProfitOptimized(prices) { // Edge case: If there are no prices or only one price, no profit can be made if (prices.length <= 1) { return 0; } // Initialize state variables for the first day let buy = -prices[0]; let sell = 0; let cooldown = 0; // Iterate through the prices starting from the second day for (let i = 1; i < prices.length; i++) { // We need to save the current values before updating them // to avoid using updated values in the same iteration const prevBuy = buy; const prevSell = sell; // Update buy: either continue from previous buy state or buy after cooldown buy = Math.max(buy, cooldown - prices[i]); // Update sell: either continue from previous sell state or sell the stock we bought sell = Math.max(sell, prevBuy + prices[i]); // Update cooldown: either continue from previous cooldown state or enter cooldown after selling cooldown = Math.max(cooldown, prevSell); } // The maximum profit will be the maximum of sell and cooldown states return Math.max(sell, cooldown);} // Test casesconsole.log("State Machine Approach:");console.log(maxProfit([1, 2, 3, 0, 2])); // 3console.log(maxProfit([1])); // 0 console.log("\nDP Approach:");console.log(maxProfitDP([1, 2, 3, 0, 2])); // 3console.log(maxProfitDP([1])); // 0 console.log("\nOptimized DP Approach:");console.log(maxProfitOptimized([1, 2, 3, 0, 2])); // 3console.log(maxProfitOptimized([1])); // 0 // Note: The brute force and memoized approaches are not tested with large inputs// as they would be too slow or cause stack overflow for large inputs
Let's break down the implementation:
Implement the stock trader with cooldown solution in different programming languages.
Below is the implementation of the stock trader with cooldown in different programming languages. Select a language tab to view the corresponding code.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238/** * Brute Force Recursive Approach * Time Complexity: O(2^n) - Exponential due to exploring all possible buy/sell combinations * Space Complexity: O(n) - Due to recursion stack * * This approach explores all possible decisions at each step using recursion. * * @param {number[]} prices - Array of stock prices * @return {number} - Maximum profit */function maxProfitBruteForce(prices) { // Helper function to recursively find the maximum profit function dfs(i, holding) { // Base case: If we've gone through all prices, no more profit can be made if (i >= prices.length) { return 0; } if (holding) { // Option 1: Sell the stock and skip the next day (cooldown) const sellProfit = prices[i] + dfs(i + 2, false); // Option 2: Continue holding the stock const holdProfit = dfs(i + 1, true); // Return the maximum profit between selling and holding return Math.max(sellProfit, holdProfit); } else { // Option 1: Buy a stock const buyProfit = -prices[i] + dfs(i + 1, true); // Option 2: Do nothing const waitProfit = dfs(i + 1, false); // Return the maximum profit between buying and waiting return Math.max(buyProfit, waitProfit); } } // Start the recursion from day 0 with no stock return dfs(0, false);} /** * Memoized Recursive Approach * Time Complexity: O(n) - Each state is computed only once * Space Complexity: O(n) - For memoization and recursion stack * * This approach optimizes the brute force solution by caching results of subproblems. * * @param {number[]} prices - Array of stock prices * @return {number} - Maximum profit */function maxProfitMemoized(prices) { // Create a memo object to store results of subproblems const memo = {}; // Helper function to recursively find the maximum profit with memoization function dfs(i, holding) { // Base case: If we've gone through all prices, no more profit can be made if (i >= prices.length) { return 0; } // Create a unique key for the current state const key = `${i},${holding}`; // If we've already computed this state, return the cached result if (key in memo) { return memo[key]; } let profit; if (holding) { // Option 1: Sell the stock and skip the next day (cooldown) const sellProfit = prices[i] + dfs(i + 2, false); // Option 2: Continue holding the stock const holdProfit = dfs(i + 1, true); profit = Math.max(sellProfit, holdProfit); } else { // Option 1: Buy a stock const buyProfit = -prices[i] + dfs(i + 1, true); // Option 2: Do nothing const waitProfit = dfs(i + 1, false); profit = Math.max(buyProfit, waitProfit); } // Cache the result before returning memo[key] = profit; return profit; } // Start the recursion from day 0 with no stock return dfs(0, false);} /** * State Machine Approach * Time Complexity: O(n) - We iterate through the prices array once * Space Complexity: O(1) - We use only a constant amount of extra space * * This approach models the problem as a state machine with three states: * 1. hold: We are holding a stock * 2. sold: We just sold a stock * 3. rest: We are not holding a stock and not in cooldown * * @param {number[]} prices - Array of stock prices * @return {number} - Maximum profit */function maxProfit(prices) { // Edge case: If there are no prices or only one price, no profit can be made if (prices.length <= 1) { return 0; } // Initialize state variables let hold = -prices[0]; // Maximum profit with a stock (initially we buy the first stock) let sold = 0; // Maximum profit after selling (initially we haven't sold anything) let rest = 0; // Maximum profit at rest (initially we haven't done anything) // Iterate through the prices starting from the second day for (let i = 1; i < prices.length; i++) { // We need to save the current hold value before updating it // because we'll need the old value to calculate the new sold value const prevHold = hold; // Update hold: either continue holding or buy a new stock after resting hold = Math.max(hold, rest - prices[i]); // Update sold: sell the stock we were holding sold = prevHold + prices[i]; // Update rest: either continue resting or come from sold state (after cooldown) rest = Math.max(rest, sold); } // The maximum profit will be the maximum of sold and rest states // (we don't want to end up holding a stock) return Math.max(sold, rest);} /** * Dynamic Programming Approach * Time Complexity: O(n) - We iterate through the prices array once * Space Complexity: O(n) - We use three arrays of size n * * This approach uses dynamic programming to track the maximum profit * for each state at each day. * * @param {number[]} prices - Array of stock prices * @return {number} - Maximum profit */function maxProfitDP(prices) { // Edge case: If there are no prices or only one price, no profit can be made if (prices.length <= 1) { return 0; } const n = prices.length; // Create arrays to track the maximum profit for each state at each day const buy = new Array(n).fill(0); // Maximum profit if we have a stock at the end of day i const sell = new Array(n).fill(0); // Maximum profit if we sell a stock at day i const cooldown = new Array(n).fill(0); // Maximum profit if we are in cooldown at day i // Initialize the first day: we can only buy on the first day buy[0] = -prices[0]; // Fill the DP arrays for each day for (let i = 1; i < n; i++) { // Buy: either continue from previous buy state or buy after cooldown buy[i] = Math.max(buy[i - 1], cooldown[i - 1] - prices[i]); // Sell: either continue from previous sell state or sell the stock we bought sell[i] = Math.max(sell[i - 1], buy[i - 1] + prices[i]); // Cooldown: either continue from previous cooldown state or enter cooldown after selling cooldown[i] = Math.max(cooldown[i - 1], sell[i - 1]); } // The maximum profit will be the maximum of sell and cooldown states at the last day return Math.max(sell[n - 1], cooldown[n - 1]);} /** * Optimized Dynamic Programming Approach * Time Complexity: O(n) - We iterate through the prices array once * Space Complexity: O(1) - We use only a constant amount of extra space * * This approach optimizes the space usage of the DP approach by using * variables instead of arrays. * * @param {number[]} prices - Array of stock prices * @return {number} - Maximum profit */function maxProfitOptimized(prices) { // Edge case: If there are no prices or only one price, no profit can be made if (prices.length <= 1) { return 0; } // Initialize state variables for the first day let buy = -prices[0]; let sell = 0; let cooldown = 0; // Iterate through the prices starting from the second day for (let i = 1; i < prices.length; i++) { // We need to save the current values before updating them // to avoid using updated values in the same iteration const prevBuy = buy; const prevSell = sell; // Update buy: either continue from previous buy state or buy after cooldown buy = Math.max(buy, cooldown - prices[i]); // Update sell: either continue from previous sell state or sell the stock we bought sell = Math.max(sell, prevBuy + prices[i]); // Update cooldown: either continue from previous cooldown state or enter cooldown after selling cooldown = Math.max(cooldown, prevSell); } // The maximum profit will be the maximum of sell and cooldown states return Math.max(sell, cooldown);} // Test casesconsole.log("State Machine Approach:");console.log(maxProfit([1, 2, 3, 0, 2])); // 3console.log(maxProfit([1])); // 0 console.log("\nDP Approach:");console.log(maxProfitDP([1, 2, 3, 0, 2])); // 3console.log(maxProfitDP([1])); // 0 console.log("\nOptimized DP Approach:");console.log(maxProfitOptimized([1, 2, 3, 0, 2])); // 3console.log(maxProfitOptimized([1])); // 0 // Note: The brute force and memoized approaches are not tested with large inputs// as they would be too slow or cause stack overflow for large inputs
First, understand that we need to find the maximum profit from buying and selling stocks with a cooldown period of one day after selling. This means after selling a stock, we must wait one day before buying again.
Start with a recursive approach that explores all possible decisions at each step: buy, sell, or do nothing. For each day, we need to consider our current state (whether we're holding a stock or not) and make the best decision.
Notice that the recursive approach has overlapping subproblems. We can use memoization to avoid recalculating the same states multiple times by storing the results in a cache.
For the iterative approaches, define state variables to track the maximum profit in different states: holding a stock, just sold a stock, and resting (not holding and not in cooldown).
For each day, update the state variables based on the possible actions: buy, sell, or do nothing. The key insight is to understand how these states transition into each other.
Ensure that after selling a stock, we cannot buy on the next day (cooldown period). This is handled differently in each approach: in the recursive approach, we skip a day after selling; in the DP approach, we use separate arrays for different states.
Observe that we only need information from at most two days ahead. We can optimize the space usage by using variables instead of arrays, reducing the space complexity from O(n) to O(1).
Return the maximum profit, which will be the maximum of the sold and rest states (we don't want to end up holding a stock).
Modify the code to implement an alternative approach and test it with the same examples.
Implement a function that solves the stock trader with cooldown problem using a different approach than shown above.
Handle the case where the input array is empty (return 0 since no transactions can be made).
Handle the case where the input array has only one element (return 0 since we need at least two days to make a profit).
Handle the case where prices are strictly decreasing (return 0 since we can't make any profit).
Ensure that after selling a stock, we cannot buy on the next day (cooldown period).
Handle multiple buy-sell transactions to maximize profit, respecting the cooldown period.