Problem Tracker

🔢 DSA Trackers & Hubs · Hope AI — ML & DS Course

✅ DSA Problem Tracker — Optimal Solutions

How to use: Study the approach and visual diagram first. Try to code it yourself. Then compare with the optimal solution. Check off when you can solve it from memory.

🔢 Arrays

Best Time to Buy and Sell Stock — LeetCode #121

Difficulty: Easy | Optimal: Greedy, One Pass | Time: O(n) · Space: O(1)

Approach: Track the minimum price seen so far. For each price, compute profit = price - min_price. Update max profit.

Prices: [7, 1, 5, 3, 6, 4]
         7  1  5  3  6  4
min_so_far: 7  1  1  1  1  1
max_profit: 0  0  4  4  5  5
                      ↑
                 Answer = 5 (buy at 1, sell at 6)
def max_profit(prices):
    min_price = float('inf')
    max_profit = 0
    for price in prices:
        min_price = min(min_price, price)
        max_profit = max(max_profit, price - min_price)
    return max_profit
  • Solved ✓

Product of Array Except Self — LeetCode #238

Difficulty: Medium | Optimal: Prefix × Suffix | Time: O(n) · Space: O(1)

Approach: For each index, result = product of all elements to its left × product of all elements to its right.

Input:  [1, 2, 3, 4]
Prefix: [1, 1, 2, 6]   (product of all elements to the LEFT)
Suffix: [24,12, 4, 1]  (product of all elements to the RIGHT)
Result: [24,12, 8, 6]  = prefix[i] × suffix[i]
def product_except_self(nums):
    n = len(nums)
    result = [1] * n
    # Left pass: result[i] = product of all nums[0..i-1]
    prefix = 1
    for i in range(n):
        result[i] = prefix
        prefix *= nums[i]
    # Right pass: multiply by product of all nums[i+1..n-1]
    suffix = 1
    for i in range(n - 1, -1, -1):
        result[i] *= suffix
        suffix *= nums[i]
    return result
  • Solved ✓

Majority Element — LeetCode #169

Difficulty: Easy | Optimal: Boyer-Moore Voting | Time: O(n) · Space: O(1)

Approach: Majority element appears > n/2 times. Cancel out non-majority with majority: net count stays positive.

nums = [2,2,1,1,1,2,2]
candidate=2, count=1
         2   ↑   1 count: 1→2
             1   ↓   count: 2→1
                 1   ↓   count: 1→0
                     1   new candidate=1, count=1
                         2   ↓   count=0
                             2   new candidate=2, count=1
Result: candidate = 2 ✓
def majority_element(nums):
    candidate, count = None, 0
    for num in nums:
        if count == 0:
            candidate = num
        count += 1 if num == candidate else -1
    return candidate
  • Solved ✓

Rotate Array — LeetCode #189

Difficulty: Medium | Optimal: Triple Reverse | Time: O(n) · Space: O(1)

Approach: Reverse entire array → reverse first k → reverse rest.

nums=[1,2,3,4,5,6,7], k=3
Step 1 reverse all:  [7,6,5,4,3,2,1]
Step 2 reverse [0,k): [5,6,7,4,3,2,1]
Step 3 reverse [k,n): [5,6,7,1,2,3,4]  ← Answer!
def rotate(nums, k):
    n = len(nums)
    k %= n
    def reverse(lo, hi):
        while lo < hi:
            nums[lo], nums[hi] = nums[hi], nums[lo]
            lo += 1; hi -= 1
    reverse(0, n-1)
    reverse(0, k-1)
    reverse(k, n-1)
  • Solved ✓

Subarray Sum Equals K — LeetCode #560

Difficulty: Medium | Optimal: Prefix Sum + HashMap | Time: O(n) · Space: O(n)

Approach: For each index j, count number of indices i where prefix[j] - prefix[i] = k, i.e., prefix[i] = prefix[j] - k.

nums=[1,1,1], k=2
prefix sums: 0→1→2→3
At prefix=2: need prefix=0 (seen 1 time) → count+=1
At prefix=3: need prefix=1 (seen 1 time) → count+=1
Answer: 2 subarrays ([1,1] starting at index 0, starting at index 1)
def subarray_sum(nums, k):
    prefix_freq = {0: 1}  # prefix_sum: count
    prefix_sum = count = 0
    for num in nums:
        prefix_sum += num
        count += prefix_freq.get(prefix_sum - k, 0)
        prefix_freq[prefix_sum] = prefix_freq.get(prefix_sum, 0) + 1
    return count
  • Solved ✓

🔡 Strings

Valid Palindrome — LeetCode #125

Difficulty: Easy | Optimal: Two Pointers | Time: O(n) · Space: O(1)

"A man, a plan, a canal: Panama"
 ↑                             ↑  → a==a, move inward
   ↑                         ↑    → m==m, move inward
     ↑                     ↑      → ... continue
→ All match → True ✓
def is_palindrome(s):
    left, right = 0, len(s) - 1
    while left < right:
        while left < right and not s[left].isalnum():
            left += 1
        while left < right and not s[right].isalnum():
            right -= 1
        if s[left].lower() != s[right].lower():
            return False
        left += 1; right -= 1
    return True
  • Solved ✓

Longest Substring Without Repeating Characters — LeetCode #3

Difficulty: Medium | Optimal: Sliding Window + HashMap | Time: O(n) · Space: O(k)

"abcabcbb"
[a b c] size=3 ← max
a[b c a] - 'a' seen at 0, move left to 1
  [b c a b] - 'b' seen at 1, move left to 2
    [c a b c] - 'c' seen at 2, move left to 3
      [a b c b] - 'b' seen at 4, move left to 5
        [c b b] - 'b' seen at 5, move to 6
          [c b] done. Max = 3
def length_of_longest_substring(s):
    char_idx = {}
    left = res = 0
    for right, c in enumerate(s):
        if c in char_idx and char_idx[c] >= left:
            left = char_idx[c] + 1
        char_idx[c] = right
        res = max(res, right - left + 1)
    return res
  • Solved ✓

Minimum Window Substring — LeetCode #76

Difficulty: Hard | Optimal: Sliding Window + Counter | Time: O(n+m) · Space: O(m)

s="ADOBECODEBANC", t="ABC"
Expand right until window contains all of t:
  ADOBEC → contains A,B,C → try shrink
  DOBECODEBA → shrink from left: OBECODEBA → BECODEBA → ECODEBA → CODEBA → ODEBANC → DEBANC → EBANC → BANC ← minimum!
Answer: "BANC"
from collections import Counter
def min_window(s, t):
    need = Counter(t)
    missing = len(t)
    left = start = end = 0
    for right, c in enumerate(s, 1):
        if need[c] > 0:
            missing -= 1
        need[c] -= 1
        if missing == 0:
            while need[s[left]] < 0:
                need[s[left]] += 1
                left += 1
            if end == 0 or right - left < end - start:
                start, end = left, right
            need[s[left]] += 1
            missing += 1
            left += 1
    return s[start:end]
  • Solved ✓

#️⃣ Hashing

Two Sum — LeetCode #1

Difficulty: Easy | Optimal: HashMap | Time: O(n) · Space: O(n)

nums=[2,7,11,15], target=9
i=0: num=2, need=7 → not in map, add {2:0}
i=1: num=7, need=2 → IN MAP at index 0! → return [0,1]
def two_sum(nums, target):
    seen = {}
    for i, num in enumerate(nums):
        complement = target - num
        if complement in seen:
            return [seen[complement], i]
        seen[num] = i
  • Solved ✓

Longest Consecutive Sequence — LeetCode #128

Difficulty: Medium | Optimal: HashSet | Time: O(n) · Space: O(n)

nums=[100,4,200,1,3,2]
set={100,4,200,1,3,2}
Only start sequence if n-1 NOT in set:
  1: 1-1=0 not in set → start! 1→2→3→4 = length 4
  100: 99 not in set → 100→101 not in set = length 1
  200: 199 not in set → 200 = length 1
Answer: 4
def longest_consecutive(nums):
    num_set = set(nums)
    best = 0
    for n in num_set:
        if n - 1 not in num_set:  # sequence start
            length = 1
            while n + length in num_set:
                length += 1
            best = max(best, length)
    return best
  • Solved ✓

Search in Rotated Sorted Array — LeetCode #33

Difficulty: Medium | Optimal: Modified Binary Search | Time: O(log n) · Space: O(1)

[4,5,6,7,0,1,2], target=0
lo=0, hi=6, mid=3 → arr[3]=7
  Left half [4,5,6,7] is sorted (arr[lo]=4 ≤ arr[mid]=7)
  Is target=0 in [4,7]? No → search right: lo=4
lo=4, hi=6, mid=5 → arr[5]=1
  Left half [0,1] is sorted (arr[lo]=0 ≤ arr[mid]=1)
  Is target=0 in [0,1]? Yes → search left: hi=4
lo=4, hi=4, mid=4 → arr[4]=0 == target ✓
def search(nums, target):
    lo, hi = 0, len(nums) - 1
    while lo <= hi:
        mid = (lo + hi) // 2
        if nums[mid] == target:
            return mid
        if nums[lo] <= nums[mid]:  # left half sorted
            if nums[lo] <= target < nums[mid]:
                hi = mid - 1
            else:
                lo = mid + 1
        else:  # right half sorted
            if nums[mid] < target <= nums[hi]:
                lo = mid + 1
            else:
                hi = mid - 1
    return -1
  • Solved ✓

📚 Stack

Valid Parentheses — LeetCode #20

Difficulty: Easy | Optimal: Stack | Time: O(n) · Space: O(n)

Input: "({[]})"
( → push
{ → push        stack: [(, {]
[ → push        stack: [(, {, []
] → pop [, match? [ matches ] ✓  stack: [(, {]
} → pop {, match? { matches } ✓  stack: [(]
) → pop (, match? ( matches ) ✓  stack: []
Empty stack → True ✓
def is_valid(s):
    stack = []
    mapping = {')': '(', '}': '{', ']': '['}
    for c in s:
        if c in mapping:
            if not stack or stack[-1] != mapping[c]:
                return False
            stack.pop()
        else:
            stack.append(c)
    return not stack
  • Solved ✓

Daily Temperatures — LeetCode #739

Difficulty: Medium | Optimal: Monotonic Stack | Time: O(n) · Space: O(n)

Temperatures: [73,74,75,71,69,72,76,73]
Monotonic Decreasing Stack stores indices:

i=0 T=73: stack=[] → push 0.   stack:[0]
i=1 T=74: 74>T[0]=73 → pop 0, ans[0]=1-0=1. push 1. stack:[1]
i=2 T=75: 75>T[1]=74 → pop 1, ans[1]=2-1=1. push 2. stack:[2]
i=3 T=71: 71<T[2]=75 → push 3. stack:[2,3]
i=4 T=69: push 4.     stack:[2,3,4]
i=5 T=72: 72>T[4]=69 → pop 4, ans[4]=5-4=1
          72>T[3]=71 → pop 3, ans[3]=5-3=2
          72<T[2]=75 → push 5. stack:[2,5]
i=6 T=76: 76>T[5]=72 → pop 5, ans[5]=6-5=1
          76>T[2]=75 → pop 2, ans[2]=6-2=4. push 6.
i=7 T=73: push 7
Result: [1,1,4,2,1,1,0,0]
def daily_temperatures(temps):
    ans = [0] * len(temps)
    stack = []  # indices, decreasing temps
    for i, t in enumerate(temps):
        while stack and temps[stack[-1]] < t:
            j = stack.pop()
            ans[j] = i - j
        stack.append(i)
    return ans
  • Solved ✓

🔗 Linked List

Reverse Linked List — LeetCode #206

Difficulty: Easy | Optimal: Iterative (O(1) space) | Time: O(n) · Space: O(1)

1 → 2 → 3 → 4 → 5 → None
↑
prev=None, curr=1

Step 1: next=2, curr.next=None, prev=1, curr=2
None ← 1   2 → 3 → 4 → 5

Step 2: next=3, curr.next=1, prev=2, curr=3
None ← 1 ← 2   3 → 4 → 5

...until curr=None
None ← 1 ← 2 ← 3 ← 4 ← 5
                          ↑ prev = new head
def reverse_list(head):
    prev, curr = None, head
    while curr:
        next_node = curr.next
        curr.next = prev
        prev = curr
        curr = next_node
    return prev
  • Solved ✓

Linked List Cycle — LeetCode #141

Difficulty: Easy | Optimal: Floyd's Tortoise & Hare | Time: O(n) · Space: O(1)

1 → 2 → 3 → 4 → 5
            ↑       |
            └───────┘
slow: 1→2→3→4→5→3→4→5
fast: 1→3→5→4→3→5→4→3
slow meets fast at some node → CYCLE detected!

No cycle: fast reaches None → return False
def has_cycle(head):
    slow = fast = head
    while fast and fast.next:
        slow = slow.next
        fast = fast.next.next
        if slow is fast:
            return True
    return False
  • Solved ✓

Merge Two Sorted Lists — LeetCode #21

Difficulty: Easy | Optimal: Iterative with dummy node | Time: O(m+n) · Space: O(1)

L1: 1 → 2 → 4
L2: 1 → 3 → 4

dummy → ?
  1(L1) vs 1(L2): tie → take L1: dummy→1(L1)→?
  2(L1) vs 1(L2): 1<2 → take L2: →1(L2)→?
  2(L1) vs 3(L2): 2<3 → take L1: →2(L1)→?
  4(L1) vs 3(L2): 3<4 → take L2: →3(L2)→?
  L1 remains → append all: →4(L1)→4(L2)
Result: 1→1→2→3→4→4
def merge_two_lists(l1, l2):
    dummy = curr = ListNode(0)
    while l1 and l2:
        if l1.val <= l2.val:
            curr.next = l1; l1 = l1.next
        else:
            curr.next = l2; l2 = l2.next
        curr = curr.next
    curr.next = l1 or l2
    return dummy.next
  • Solved ✓

🌀 Recursion

Generate Parentheses — LeetCode #22

Difficulty: Medium | Optimal: Backtracking | Time: O(4ⁿ/√n) · Space: O(n)

n=2, build valid combinations:
                    ""
              /          \
           "("            (can't start with ')')
         /     \
       "(("    "()"
        |      /   \
      "(()"  "()("  (close if open==0? no)
        |      |
     "(())"  "()()"
Both valid! Answer: ["(())", "()()"]

Rule: Add '(' if open < n, add ')' if close < open
def generate_parenthesis(n):
    result = []
    def backtrack(s, open_count, close_count):
        if len(s) == 2 * n:
            result.append(s)
            return
        if open_count < n:
            backtrack(s + '(', open_count + 1, close_count)
        if close_count < open_count:
            backtrack(s + ')', open_count, close_count + 1)
    backtrack('', 0, 0)
    return result
  • Solved ✓

🔄 Backtracking

Permutations — LeetCode #46

Difficulty: Medium | Optimal: Backtracking | Time: O(n·n!) · Space: O(n)

nums=[1,2,3]
Decision Tree:
         []
    /    |    \
  [1]   [2]   [3]
  / \   / \   / \
[1,2][1,3][2,1][2,3][3,1][3,2]
  |    |    |    |    |    |
[1,2,3][1,3,2][2,1,3][2,3,1][3,1,2][3,2,1]

At each step: choose unused number, add to path, recurse, remove (backtrack)
def permute(nums):
    result = []
    def backtrack(path, remaining):
        if not remaining:
            result.append(path[:])
            return
        for i in range(len(remaining)):
            path.append(remaining[i])
            backtrack(path, remaining[:i] + remaining[i+1:])
            path.pop()
    backtrack([], nums)
    return result
  • Solved ✓

🗃️ Heap

K Closest Points to Origin — LeetCode #973

Difficulty: Medium | Optimal: Max-Heap of size K | Time: O(n log k) · Space: O(k)

points=[[1,3],[-2,2]], k=1
Distances: √(1+9)=√10, √(4+4)=√8
√8 < √10 → closest is [-2,2]

Max-heap approach (keep k smallest):
Push (-dist, x, y) so we can pop the farthest
When heap size > k, pop the largest distance
import heapq
def k_closest(points, k):
    # Max-heap: negate distance to use min-heap as max-heap
    heap = []
    for x, y in points:
        dist = -(x*x + y*y)
        heapq.heappush(heap, (dist, x, y))
        if len(heap) > k:
            heapq.heappop(heap)  # remove farthest
    return [[x, y] for _, x, y in heap]
  • Solved ✓

🌲 Trees

Maximum Depth of Binary Tree — LeetCode #104

Difficulty: Easy | Optimal: DFS Recursion | Time: O(n) · Space: O(h)

        3
       / \
      9   20
         /  \
        15   7

DFS:
depth(3) = 1 + max(depth(9), depth(20))
         = 1 + max(1, 1 + max(depth(15), depth(7)))
         = 1 + max(1, 1 + max(1, 1))
         = 1 + max(1, 2) = 3
def max_depth(root):
    if not root:
        return 0
    return 1 + max(max_depth(root.left), max_depth(root.right))
  • Solved ✓

Invert Binary Tree — LeetCode #226

Difficulty: Easy | Optimal: DFS Recursion | Time: O(n) · Space: O(h)

Before:        After:
    4              4
   / \            / \
  2   7    →    7   2
 / \ / \       / \ / \
1  3 6  9     9  6 3  1
def invert_tree(root):
    if not root:
        return None
    root.left, root.right = invert_tree(root.right), invert_tree(root.left)
    return root
  • Solved ✓

Lowest Common Ancestor — LeetCode #236

Difficulty: Medium | Optimal: Recursive DFS | Time: O(n) · Space: O(h)

Find LCA of 5 and 1 in tree:
        3
       / \
      5   1
     / \ / \
    6  2 0  8

DFS: if node is p or q → return node
     if both subtrees return non-None → current node is LCA
     else return whichever subtree returned non-None
def lowest_common_ancestor(root, p, q):
    if not root or root is p or root is q:
        return root
    left = lowest_common_ancestor(root.left, p, q)
    right = lowest_common_ancestor(root.right, p, q)
    if left and right:
        return root  # p and q in different subtrees
    return left or right
  • Solved ✓

🕸️ Graphs

Number of Islands — LeetCode #200

Difficulty: Medium | Optimal: DFS/BFS | Time: O(m×n) · Space: O(m×n)

Grid:
1 1 0 0 0
1 1 0 0 0
0 0 1 0 0
0 0 0 1 1

DFS from each unvisited '1':
  Start at (0,0) → flood fill → marks all connected 1s as visited
  Count = 1
  Start at (2,2) → Count = 2
  Start at (3,3) → Count = 3
Answer: 3 islands
def num_islands(grid):
    if not grid: return 0
    rows, cols = len(grid), len(grid[0])
    count = 0

    def dfs(r, c):
        if r < 0 or r >= rows or c < 0 or c >= cols or grid[r][c] != '1':
            return
        grid[r][c] = '0'  # mark visited
        dfs(r+1,c); dfs(r-1,c); dfs(r,c+1); dfs(r,c-1)

    for r in range(rows):
        for c in range(cols):
            if grid[r][c] == '1':
                dfs(r, c)
                count += 1
    return count
  • Solved ✓

Course Schedule — LeetCode #207

Difficulty: Medium | Optimal: Topological Sort (BFS/Kahn's) | Time: O(V+E) · Space: O(V+E)

numCourses=4, prerequisites=[[1,0],[2,0],[3,1],[3,2]]
Graph: 0→1, 0→2, 1→3, 2→3
In-degrees: [0:0, 1:1, 2:1, 3:2]
Queue: [0] (in-degree 0)
  Process 0: reduce in-degree of 1,2 → [1:0, 2:0]
  Queue: [1,2]
  Process 1: reduce 3 → [3:1]. Process 2: reduce 3 → [3:0]
  Queue: [3]. Process 3.
Completed 4 courses == numCourses → True ✓
from collections import deque, defaultdict
def can_finish(numCourses, prerequisites):
    in_degree = [0] * numCourses
    graph = defaultdict(list)
    for a, b in prerequisites:
        graph[b].append(a)
        in_degree[a] += 1

    queue = deque(c for c in range(numCourses) if in_degree[c] == 0)
    completed = 0
    while queue:
        course = queue.popleft()
        completed += 1
        for next_course in graph[course]:
            in_degree[next_course] -= 1
            if in_degree[next_course] == 0:
                queue.append(next_course)
    return completed == numCourses
  • Solved ✓

🧮 Dynamic Programming

Climbing Stairs — LeetCode #70

Difficulty: Easy | Optimal: 1D DP / Fibonacci | Time: O(n) · Space: O(1)

n=5 stairs, can take 1 or 2 steps
ways(1)=1, ways(2)=2
ways(n) = ways(n-1) + ways(n-2)  ← Fibonacci!

n:    1  2  3  4  5
ways: 1  2  3  5  8
def climb_stairs(n):
    if n <= 2: return n
    prev2, prev1 = 1, 2
    for _ in range(3, n + 1):
        prev2, prev1 = prev1, prev2 + prev1
    return prev1
  • Solved ✓

Coin Change — LeetCode #322

Difficulty: Medium | Optimal: Bottom-Up DP | Time: O(amount × n) · Space: O(amount)

coins=[1,2,5], amount=11
dp[i] = min coins to make amount i

dp = [0, ∞, ∞, ∞, ∞, ∞, ∞, ∞, ∞, ∞, ∞, ∞]
      0   1   2   3   4   5   6   7   8   9  10  11

After processing coin=1: dp[i]=i
After processing coin=2: dp[2]=1, dp[3]=2...
After processing coin=5: dp[5]=1, dp[6]=2, dp[10]=2, dp[11]=3

Answer: dp[11] = 3 (use 5+5+1)
def coin_change(coins, amount):
    dp = [float('inf')] * (amount + 1)
    dp[0] = 0
    for i in range(1, amount + 1):
        for coin in coins:
            if coin <= i:
                dp[i] = min(dp[i], dp[i - coin] + 1)
    return dp[amount] if dp[amount] != float('inf') else -1
  • Solved ✓

House Robber — LeetCode #198

Difficulty: Medium | Optimal: 1D DP | Time: O(n) · Space: O(1)

nums=[2,7,9,3,1]
rob(0)=2, rob(1)=max(2,7)=7
rob(2)=max(7, 2+9)=11
rob(3)=max(11, 7+3)=11
rob(4)=max(11, 11+1)=12
Answer: 12 (rob houses 0,2,4: 2+9+1=12)
def rob(nums):
    prev2 = prev1 = 0
    for num in nums:
        prev2, prev1 = prev1, max(prev1, prev2 + num)
    return prev1
  • Solved ✓

Longest Increasing Subsequence — LeetCode #300

Difficulty: Medium | Optimal: DP + Binary Search | Time: O(n log n) · Space: O(n)

nums=[10,9,2,5,3,7,101,18]
tails[] = []  (tails[i] = smallest tail of IS of length i+1)

10 → [10]
9  → [9]   (replace 10 with 9, smaller tail)
2  → [2]
5  → [2,5]
3  → [2,3] (replace 5 with 3)
7  → [2,3,7]
101→ [2,3,7,101]
18 → [2,3,7,18] (replace 101)
Length = 4 ✓
import bisect
def length_of_lis(nums):
    tails = []
    for num in nums:
        pos = bisect.bisect_left(tails, num)
        if pos == len(tails):
            tails.append(num)
        else:
            tails[pos] = num
    return len(tails)
  • Solved ✓

🔤 Tries

Implement Trie — LeetCode #208

Difficulty: Medium | Optimal: TrieNode with dict | Time: O(L) per op · Space: O(n·L)

Trie after inserting "app", "apple", "apt":
root
└── a
    └── p
        ├── p [end]
        │   └── l
        │       └── e [end]
        └── t [end]

search("app") → traverse a→p→p → is_end=True ✓
startsWith("ap") → traverse a→p → exists ✓
search("ap") → traverse a→p → is_end=False ✗
class TrieNode:
    def __init__(self):
        self.children = {}
        self.is_end = False

class Trie:
    def __init__(self):
        self.root = TrieNode()

    def insert(self, word):
        node = self.root
        for c in word:
            if c not in node.children:
                node.children[c] = TrieNode()
            node = node.children[c]
        node.is_end = True

    def search(self, word):
        node = self.root
        for c in word:
            if c not in node.children:
                return False
            node = node.children[c]
        return node.is_end

    def starts_with(self, prefix):
        node = self.root
        for c in prefix:
            if c not in node.children:
                return False
            node = node.children[c]
        return True
  • Solved ✓

🔢 Bit Manipulation

Single Number — LeetCode #136

Difficulty: Easy | Optimal: XOR | Time: O(n) · Space: O(1)

XOR properties:
  a ^ a = 0  (same number cancels)
  a ^ 0 = a  (XOR with 0 = itself)
  Commutative & Associative

[4,1,2,1,2]
4 ^ 1 = 5
5 ^ 2 = 7
7 ^ 1 = 6
6 ^ 2 = 4  ← The single number!
def single_number(nums):
    result = 0
    for num in nums:
        result ^= num
    return result
  • Solved ✓

⚙️ Design

LRU Cache — LeetCode #146

Difficulty: Medium | Optimal: OrderedDict | Time: O(1) per op · Space: O(capacity)

LRU Cache capacity=3:
put(1,1): {1:1}
put(2,2): {1:1, 2:2}
put(3,3): {1:1, 2:2, 3:3}
get(1):   {2:2, 3:3, 1:1}  ← 1 moved to end (most recent)
put(4,4): evict LRU (2) → {3:3, 1:1, 4:4}

Key insight: Most recently used = rightmost in OrderedDict
             Least recently used = leftmost → evict
from collections import OrderedDict
class LRUCache:
    def __init__(self, capacity):
        self.cache = OrderedDict()
        self.capacity = capacity

    def get(self, key):
        if key not in self.cache:
            return -1
        self.cache.move_to_end(key)  # mark as recently used
        return self.cache[key]

    def put(self, key, value):
        if key in self.cache:
            self.cache.move_to_end(key)
        self.cache[key] = value
        if len(self.cache) > self.capacity:
            self.cache.popitem(last=False)  # evict LRU
  • Solved ✓

🔢 Math

Count Primes — LeetCode #204

Difficulty: Medium | Optimal: Sieve of Eratosthenes | Time: O(n log log n) · Space: O(n)

Sieve for n=10:
is_prime = [T,T,T,T,T,T,T,T,T,T]  (indices 0-9)
Start: mark 0,1 as not prime
i=2 (prime): mark 4,6,8 as not prime
i=3 (prime): mark 6,9 as not prime
i=4 (not prime): skip
Result: primes = [2,3,5,7] → count = 4
def count_primes(n):
    if n < 2: return 0
    is_prime = [True] * n
    is_prime[0] = is_prime[1] = False
    for i in range(2, int(n**0.5) + 1):
        if is_prime[i]:
            for j in range(i*i, n, i):
                is_prime[j] = False
    return sum(is_prime)
  • Solved ✓

💡 Study Tips:
1. Read the problem → identify the pattern (sorting? two pointers? DP?)
2. State the approach out loud before coding
3. Write the time/space complexity before coding
4. Test with the visual diagram example
5. Code the solution
6. Check edge cases: empty input, single element, duplicates, negative numbers