From 011c3ee6d491aebaf3efe529dc596fd6c36b6a1a Mon Sep 17 00:00:00 2001 From: Kai Chappell Date: Mon, 28 Apr 2025 21:47:07 +0100 Subject: [PATCH] easy questions --- .../best-time-to-buy-and-sell-stock.yaml | 89 +++++++++++++++ backend/data/questions/climbing-stairs.yaml | 102 ++++++++++++++++++ 2 files changed, 191 insertions(+) create mode 100644 backend/data/questions/best-time-to-buy-and-sell-stock.yaml create mode 100644 backend/data/questions/climbing-stairs.yaml diff --git a/backend/data/questions/best-time-to-buy-and-sell-stock.yaml b/backend/data/questions/best-time-to-buy-and-sell-stock.yaml new file mode 100644 index 0000000..85c6e70 --- /dev/null +++ b/backend/data/questions/best-time-to-buy-and-sell-stock.yaml @@ -0,0 +1,89 @@ +title: Best Time to Buy and Sell Stock +slug: best-time-to-buy-and-sell-stock +difficulty: easy +leetcode_id: 121 +leetcode_url: https://leetcode.com/problems/best-time-to-buy-and-sell-stock/ +categories: + - arrays + - dynamic-programming +patterns: + - greedy + +description: | + You are given an array `prices` where `prices[i]` is the price of a given stock on the ith day. + + You want to maximize your profit by choosing a single day to buy one stock and choosing a + different day in the future to sell that stock. + + Return the maximum profit you can achieve from this transaction. If you cannot achieve any + profit, return 0. + +constraints: | + - 1 <= prices.length <= 10^5 + - 0 <= prices[i] <= 10^4 + +examples: + - input: "prices = [7,1,5,3,6,4]" + output: "5" + explanation: "Buy on day 2 (price = 1) and sell on day 5 (price = 6), profit = 6-1 = 5." + - input: "prices = [7,6,4,3,1]" + output: "0" + explanation: "No profitable transaction possible." + +explanation: + approach: | + 1. Track the minimum price seen so far + 2. For each day, calculate the profit if we sold today + 3. Update maximum profit if current profit is higher + 4. Update minimum price if current price is lower + + intuition: | + To maximize profit, we want to buy at the lowest price and sell at the highest price after + that. Rather than comparing all pairs (O(n²)), we track the minimum price seen so far. + + For each day, we ask: "If I sold today, what's the maximum profit?" This is simply + today's price minus the minimum price we've seen before today. + + common_pitfalls: + - title: Selling before buying + description: | + Ensure you only consider selling on days after the minimum price was observed. + Tracking minimum as you iterate handles this automatically. + wrong_approach: "Using global min and max without considering order" + correct_approach: "Track running minimum, calculate profit from that point" + + - title: Initializing min_price to 0 + description: | + Initialize min_price to the first element or infinity, not 0, since prices are positive + and you need to track actual minimum. + + key_takeaways: + - Single pass through array is sufficient + - Track running minimum for optimal buy point + - Greedy approach works when you can only make one transaction + - This is a foundation for more complex stock problems + + time_complexity: "O(n)" + space_complexity: "O(1)" + complexity_explanation: | + Time: Single pass through the prices array. + Space: Only two variables needed (min_price, max_profit). + +solutions: + - approach_name: Single Pass (Optimal) + is_optimal: true + code: | + def max_profit(prices: list[int]) -> int: + min_price = float('inf') + max_profit = 0 + + for price in prices: + if price < min_price: + min_price = price + elif price - min_price > max_profit: + max_profit = price - min_price + + return max_profit + explanation: | + Track minimum price seen so far and maximum profit achievable. + Update both as we iterate through prices. diff --git a/backend/data/questions/climbing-stairs.yaml b/backend/data/questions/climbing-stairs.yaml new file mode 100644 index 0000000..1056ba9 --- /dev/null +++ b/backend/data/questions/climbing-stairs.yaml @@ -0,0 +1,102 @@ +title: Climbing Stairs +slug: climbing-stairs +difficulty: easy +leetcode_id: 70 +leetcode_url: https://leetcode.com/problems/climbing-stairs/ +categories: + - dynamic-programming + - math +patterns: + - dynamic-programming + +description: | + You are climbing a staircase. It takes n steps to reach the top. + + Each time you can either climb 1 or 2 steps. In how many distinct ways can you climb to the top? + +constraints: | + - 1 <= n <= 45 + +examples: + - input: "n = 2" + output: "2" + explanation: "Two ways: (1+1) or (2)" + - input: "n = 3" + output: "3" + explanation: "Three ways: (1+1+1), (1+2), (2+1)" + +explanation: + approach: | + 1. Recognize this follows the Fibonacci pattern + 2. ways(n) = ways(n-1) + ways(n-2) + 3. From step n-1, we can take 1 step to reach n + 4. From step n-2, we can take 2 steps to reach n + 5. Base cases: ways(1) = 1, ways(2) = 2 + + intuition: | + At any step, you either got there by taking 1 step from the previous position + or 2 steps from two positions back. This gives us the recurrence relation. + + This is essentially the Fibonacci sequence! The number of ways to reach step n + equals the sum of ways to reach steps n-1 and n-2. + + common_pitfalls: + - title: Using recursion without memoization + description: | + Naive recursion recalculates the same subproblems repeatedly, leading to + exponential time complexity. Either use memoization or iterative DP. + wrong_approach: "return climb(n-1) + climb(n-2) without caching" + correct_approach: "Use bottom-up DP or memoize recursive calls" + + - title: Off-by-one in base cases + description: | + Carefully define base cases. There's 1 way to stay at ground (step 0), + 1 way to reach step 1, and 2 ways to reach step 2. + + key_takeaways: + - Many counting problems follow Fibonacci-like patterns + - Convert recursion to iteration for O(1) space + - Bottom-up DP avoids stack overflow for large inputs + - Recognize overlapping subproblems as a DP signal + + time_complexity: "O(n)" + space_complexity: "O(1)" + complexity_explanation: | + Time: We compute n states, each in O(1). + Space: Only track two previous values (space-optimized DP). + +solutions: + - approach_name: Space-Optimized DP (Optimal) + is_optimal: true + code: | + def climb_stairs(n: int) -> int: + if n <= 2: + return n + + prev1, prev2 = 2, 1 + for i in range(3, n + 1): + current = prev1 + prev2 + prev2 = prev1 + prev1 = current + + return prev1 + explanation: | + Track only the two previous values since that's all we need. + Equivalent to computing the nth Fibonacci number. + + - approach_name: Recursive with Memoization + is_optimal: false + code: | + from functools import lru_cache + + def climb_stairs(n: int) -> int: + @lru_cache(maxsize=None) + def dp(step: int) -> int: + if step <= 2: + return step + return dp(step - 1) + dp(step - 2) + + return dp(n) + explanation: | + Top-down approach with memoization. + Uses O(n) space for the cache and recursion stack.