💼

Coding Interview Cheat Sheet

Don't memorize 200 problems - learn the patterns behind them. Solutions are in Python, with Big-O. Examples use classic, public problems (Two Sum, Binary Search, etc.).

🎤 New: watch a full mock interview, narrated
A complete 45-min session - OOP design → DP algorithm → behavioral - both sides of the conversation, with coaching notes.
Read the walkthrough →
🆓 Free vs ⭐ Premium: the 🚀 Start Here track is 100% free - full solutions + the code-along terminal, no account needed. Every other section gives a free taste (one Easy, Medium & Hard); reveal any single locked solution for 10 ⭐, or get Learn Premium to unlock everything and follow along anywhere. See Premium →
Classic ProblemsClassic Problems
Arrays & MatrixArrays & Matrix
StringsStrings
Searching & SortingSearching & Sorting
Sliding WindowSliding Window
Linked ListsLinked Lists
Stacks & QueuesStacks & Queues
Trees & BSTTrees & BST
GraphsGraphs
HeapsHeaps
Recursion & BacktrackingRecursion & Backtracking
Dynamic ProgrammingDynamic Programming
GreedyGreedy
Data Structures & DesignData Structures & Design
JavaJava
Real-world & On-the-jobReal-world & On-the-job
Behavioral & PrepBehavioral & Prep
Mixed & HardestMixed & Hardest
Top 150Top 150

🚀 Start here - free beginner track

Brand new, or just looking around? These 8 classics are completely free - full solutions, run them right in the terminal, no account or credits needed. Work top to bottom, then dip into the rest of the page for depth. (With Learn Premium, every section below works exactly like this one.)

FizzBuzz EASY

Print 1-100, but "Fizz" for multiples of 3, "Buzz" for 5, "FizzBuzz" for both. The classic warm-up - check the most specific case (15) first.

for n in range(1, 101):
    if n % 15 == 0:   print("FizzBuzz")
    elif n % 3 == 0:  print("Fizz")
    elif n % 5 == 0:  print("Buzz")
    else:             print(n)

Reverse a String EASY

Slicing does it in one line; the two-pointer version is what an interviewer wants to see you reason about.

def reverse(s):
    return s[::-1]                 # Pythonic one-liner

def reverse_two_pointer(chars):    # in-place, O(n)
    lo, hi = 0, len(chars) - 1
    while lo < hi:
        chars[lo], chars[hi] = chars[hi], chars[lo]
        lo += 1; hi -= 1
    return chars

Check a Palindrome EASY

A word that reads the same backwards. Compare the string to its reverse.

def is_palindrome(s):
    s = s.lower()
    return s == s[::-1]            # "Racecar" -> True

Find the Largest Number EASY

One pass, tracking the biggest seen so far - the pattern behind countless harder problems.

def largest(nums):
    biggest = nums[0]
    for n in nums[1:]:
        if n > biggest:
            biggest = n
    return biggest                # (built-in shortcut: max(nums))

Count the Vowels EASY

Loop the characters and tally the ones in "aeiou" - a gentle intro to string scanning.

def count_vowels(s):
    return sum(1 for c in s.lower() if c in "aeiou")

Two Sum EASY

The single most common interview opener: return the indices of the two numbers that add to target. A hash map turns O(n²) into O(n).

def two_sum(nums, target):
    seen = {}                          # value -> index
    for i, n in enumerate(nums):
        if target - n in seen:
            return [seen[target - n], i]
        seen[n] = i
    return None                        # no pair found

Fibonacci EASY

Each number is the sum of the two before it. The iterative version is O(n) and uses no recursion stack.

def fib(n):
    a, b = 0, 1
    for _ in range(n):
        a, b = b, a + b
    return a                           # fib(10) -> 55

Binary Search EASY

On a sorted array, halve the search space each step - O(log n). Knowing this cold is non-negotiable.

def binary_search(arr, target):
    lo, hi = 0, len(arr) - 1
    while lo <= hi:
        mid = (lo + hi) // 2
        if arr[mid] == target: return mid
        if arr[mid] < target:  lo = mid + 1
        else:                  hi = mid - 1
    return -1                          # not found
Finished all 8? Here's where to go next - there's a lot more free. Every topic below also gives you free sample problems (marked FREE in the left sidebar; locked ones show 🔒). In the sidebar, tap 🆓 Show free only to hide everything locked and see just your free path, then work down in this order:
Arrays → Strings → Searching & Sorting → Sliding Window → Linked Lists → Stacks & Queues → Trees & BST → Graphs → Recursion → Dynamic Programming. When you're ready for the full set, reveal any locked solution for 10 ⭐ or unlock everything with Premium.

Big-O - how fast is your code?

Big-O describes how runtime grows as input size n grows. Lower is better.

Big-ONameExample
O(1)constantlook up a dict/array index
O(log n)logarithmicbinary search
O(n)linearone loop over the data
O(n log n)log-lineargood sorting (merge/quick)
O(n²)quadraticnested loops
O(2ⁿ)exponentialnaive recursion (avoid!)

📊 Complexity reference - operations by data structure

The cost of common operations on each structure. O(1) on hash maps/sets is relative to n - hashing a string key still costs O(m) for a length-m string.

Array / dynamic list (n = len)

OperationBig-O
Add / remove at the endO(1) amortized
Add / remove at arbitrary indexO(n)
Access / modify at indexO(1)
Check if element existsO(n)
Two pointers / sliding windowO(n·k) (k = work per step)
Build a prefix sumO(n)
Subarray sum from a prefix sumO(1)

String - immutable (n = len)

OperationBig-O
Add / remove a characterO(n)
Access at indexO(1)
Concatenate two stringsO(n + m)
Create a substringO(m)
Build via "".join(list)O(n)

Linked list (n = nodes)

OperationBig-O
Add / remove with pointer at spotO(1) (doubly linked)
Add / remove at arbitrary positionO(n)
Access at arbitrary positionO(n)
Reverse between i and jO(j − i)
Detect a cycle (fast/slow)O(n)

Hash map / set (n = size)

OperationBig-O
Add / remove / look up a keyO(1)
Check if a value existsO(n)
Iterate over keys / valuesO(n)

Stack & Queue (n = size)

OperationBig-O
Push / pop / peek (stack)O(1)
Enqueue / dequeue / peek (queue)O(1)
Check if element existsO(n)

Trees, Heaps, Binary search

OperationBig-O
Binary tree DFS / BFSO(n·k) (k = work per node)
BST add / remove / searchO(log n) avg, O(n) worst (unbalanced)
Heap add / remove-minO(log n)
Heap find-minO(1)
Heap check if existsO(n)
Binary searchO(log n)

Misc - sorting, graphs, DP

OperationBig-O
SortingO(n log n)
Graph DFS / BFS (time)O(n·k + e) (n nodes, e edges)
Graph DFS / BFS (space)O(n), or O(n + e) to store the graph
DP (time)O(n·k) (n states, k work/state)
DP (space)O(n) (n states)

Input size → expected Big-O (read the constraints as hints)

Interviewers rarely state constraints out loud - but it never hurts to ask the expected input size. The size strongly hints at the intended complexity:
Constraint on nLikely target complexityThink…
n ≤ 10O(n!) / O(n²·n!)backtracking, brute-force recursion
10 < n ≤ 20O(2ⁿ)subsets/subsequences (take / don't take)
20 < n ≤ 100O(n³)brute force with nested loops
100 < n ≤ 1,000O(n²)nested loops, often optimal here
1,000 < n < 100,000O(n log n) or O(n)sort, heap, hash map, two pointers, monotonic stack, binary search
100,000 < n < 1,000,000O(n)almost certainly a hash map
n > 1,000,000 (or 10⁹+)O(log n) / O(1)binary search, math tricks, clever hashing
Rule of thumb: if the problem must look at every element (e.g. find the max of an unsorted array), you can't beat O(n). Otherwise you usually can't beat O(log n). An O(n) solution can hide a constant factor of ~40 (e.g. looping the 26 letters → O(26n)). Don't be confidently wrong about optimality - "I think this is optimal, but it may be improvable" is the safe phrasing.

🎤 The 7 stages of a coding interview (45-60 min)

A condensed game plan. Remote interview? Keep this in front of you. The meta-skill that ties it all together: think out loud the entire time - it lets the interviewer hint you toward the solution.

1. Introductions

2. Problem statement

3. Brainstorming DS & A

4. Implementation

5. Testing & debugging

6. Explanations & follow-ups

7. Outro

The patterns (this is the real cheat code)

⭐ Code templates (memorize these skeletons)

These are reusable fill-in-the-blank skeletons for every common pattern. Learn the shape once, then for each problem just plug in the specific logic (the CONDITION / # do logic parts). This is the single highest-leverage thing to memorize for interviews.

Two pointers - one input, opposite ends

def fn(arr):
    left = ans = 0
    right = len(arr) - 1
    while left < right:
        # do logic with left and right
        if CONDITION:
            left += 1
        else:
            right -= 1
    return ans

Two pointers - two inputs, exhaust both

def fn(arr1, arr2):
    i = j = ans = 0
    while i < len(arr1) and j < len(arr2):
        # do logic
        if CONDITION:
            i += 1
        else:
            j += 1
    while i < len(arr1):
        # do logic
        i += 1
    while j < len(arr2):
        # do logic
        j += 1
    return ans

Sliding window

def fn(arr):
    left = ans = curr = 0
    for right in range(len(arr)):
        # add arr[right] to curr
        while WINDOW_CONDITION_BROKEN:
            # remove arr[left] from curr
            left += 1
        # update ans
    return ans

Build a prefix sum

class Solution:
    def fn(self, arr):
        prefix = [arr[0]]
        for i in range(1, len(arr)):
            prefix.append(prefix[-1] + arr[i])
        return prefix

Efficient string building

Collect chars in a list, then "".join() - building a string with += in a loop is O(n²) in Python. (In JS, benchmarks show += is actually faster than .join().)

class Solution:
    def fn(self, arr):                # arr is a list of characters
        ans = []
        for c in arr:
            ans.append(c)
        return "".join(ans)

Linked list - fast & slow pointer

class Solution:
    def fn(self, head):
        slow = head
        fast = head
        ans = 0
        while fast and fast.next:
            # do logic
            slow = slow.next
            fast = fast.next.next
        return ans

Reversing a linked list

class Solution:
    def fn(self, head):
        curr = head
        prev = None
        while curr:
            next_node = curr.next
            curr.next = prev
            prev = curr
            curr = next_node
        return prev

Count subarrays that fit an exact criteria

Prefix-count with a hash map (e.g. "subarrays summing to k").

from collections import defaultdict

class Solution:
    def fn(self, arr, k):
        counts = defaultdict(int)
        counts[0] = 1
        ans = curr = 0
        for num in arr:
            # update curr (running value)
            ans += counts[curr - k]
            counts[curr] += 1
        return ans

Monotonic increasing stack

Same idea maintains a monotonic queue. For monotonic decreasing, just flip > to <.

class Solution:
    def fn(self, arr):
        stack = []
        ans = 0
        for num in arr:
            while stack and stack[-1] > num:
                # do logic
                stack.pop()
            stack.append(num)
        return ans

Binary tree - DFS (recursive)

class Solution:
    def dfs(self, root):
        if not root:
            return
        ans = 0
        # do logic
        self.dfs(root.left)
        self.dfs(root.right)
        return ans

Binary tree - DFS (iterative)

class Solution:
    def dfs(self, root):
        stack = [root]
        ans = 0
        while stack:
            node = stack.pop()
            # do logic
            if node.left:
                stack.append(node.left)
            if node.right:
                stack.append(node.right)
        return ans

Binary tree - BFS (level order)

from collections import deque

class Solution:
    def fn(self, root):
        queue = deque([root])
        ans = 0
        while queue:
            current_length = len(queue)
            # do logic for the current level
            for _ in range(current_length):
                node = queue.popleft()
                # do logic
                if node.left:
                    queue.append(node.left)
                if node.right:
                    queue.append(node.right)
        return ans

Graph - DFS (recursive)

Assume nodes 0..n-1 and an adjacency-list graph. Convert other inputs to this form first.

def fn(graph):
    def dfs(self, node):
        ans = 0
        # do logic
        for neighbor in graph[node]:
            if neighbor not in seen:
                seen.add(neighbor)
                ans += dfs(neighbor)
        return ans
    seen = {START_NODE}
    return dfs(START_NODE)

Graph - DFS (iterative)

def fn(graph):
    stack = [START_NODE]
    seen = {START_NODE}
    ans = 0
    while stack:
        node = stack.pop()
        # do logic
        for neighbor in graph[node]:
            if neighbor not in seen:
                seen.add(neighbor)
                stack.append(neighbor)
    return ans

Graph - BFS

from collections import deque
def fn(graph):
    queue = deque([START_NODE])
    seen = {START_NODE}
    ans = 0
    while queue:
        node = queue.popleft()
        # do logic
        for neighbor in graph[node]:
            if neighbor not in seen:
                seen.add(neighbor)
                queue.append(neighbor)
    return ans

Top k elements with a heap

import heapq
def fn(arr, k):
    heap = []
    for num in arr:
        # push according to the problem's criteria
        heapq.heappush(heap, (CRITERIA, num))
        if len(heap) > k:
            heapq.heappop(heap)
    return [num for num in heap]

Binary search

class Solution:
    def fn(self, arr, target):
        left = 0
        right = len(arr) - 1
        while left <= right:
            mid = (left + right) // 2
            if arr[mid] == target:
                # do something
                return mid
            if arr[mid] > target:
                right = mid - 1
            else:
                left = mid + 1
        # left is the insertion point
        return left

Binary search - left-most insertion point (duplicates)

class Solution:
    def fn(self, arr, target):
        left = 0
        right = len(arr)
        while left < right:
            mid = (left + right) // 2
            if arr[mid] >= target:
                right = mid
            else:
                left = mid + 1
        return left

Binary search - right-most insertion point (duplicates)

class Solution:
    def fn(self, arr, target):
        left = 0
        right = len(arr)
        while left < right:
            mid = (left + right) // 2
            if arr[mid] > target:
                right = mid
            else:
                left = mid + 1
        return left

Binary search - greedy "search the answer" (minimum)

def fn(arr):
    def check(self, x):
        # returns True/False depending on the problem
        return BOOLEAN
    left = MINIMUM_POSSIBLE_ANSWER
    right = MAXIMUM_POSSIBLE_ANSWER
    while left <= right:
        mid = (left + right) // 2
        if check(mid):
            right = mid - 1
        else:
            left = mid + 1
    return left

Binary search - greedy "search the answer" (maximum)

def fn(arr):
    def check(self, x):
        return BOOLEAN
    left = MINIMUM_POSSIBLE_ANSWER
    right = MAXIMUM_POSSIBLE_ANSWER
    while left <= right:
        mid = (left + right) // 2
        if check(mid):
            left = mid + 1
        else:
            right = mid - 1
    return right

Backtracking

def backtrack(curr, OTHER_ARGUMENTS):
    if BASE_CASE:
        # modify the answer
        return
    ans = 0
    for ITERATE_OVER_INPUT:
        # modify the current state
        ans += backtrack(curr, OTHER_ARGUMENTS)
        # undo the modification of the current state
    return ans

Dynamic programming - top-down memoization

def fn(arr):
    def dp(self, STATE):
        if BASE_CASE:
            return 0
        if STATE in memo:
            return memo[STATE]
        ans = RECURRENCE_RELATION(STATE)
        memo[STATE] = ans
        return ans
    memo = {}
    return dp(STATE_FOR_WHOLE_INPUT)
Top-down → bottom-up: (1) make a dp array sized by your state variables (so dp(4,6) becomes dp[4][6]); (2) set the same base cases (often just init to 0); (3) write for-loops over the state variables, iterating from the base cases toward the answer state; (4) copy the recurrence in, turning every dp(...) call into dp[...] array access; (5) return dp[...] instead of dp(...).

Build a trie

class TrieNode:                 # a class is only needed if you store data per node
    def __init__(self):
        self.data = None        # store data at nodes if you wish
        self.children = {}
def fn(words):
    root = TrieNode()
    for word in words:
        curr = root
        for c in word:
            if c not in curr.children:
                curr.children[c] = TrieNode()
            curr = curr.children[c]
        # curr now holds a full word - give it an attribute if you want
    return root

Dijkstra's algorithm

from math import inf
from heapq import heappop, heappush

class Solution:
    def fn(self, graph, source, n):
        distances = [inf] * n
        distances[source] = 0
        heap = [(0, source)]
        while heap:
            curr_dist, node = heappop(heap)
            if curr_dist > distances[node]:
                continue
            for nei, weight in graph[node]:
                dist = curr_dist + weight
                if dist < distances[nei]:
                    distances[nei] = dist
                    heappush(heap, (dist, nei))
        return distances

Classic Problems

Two Number Sum EASY

Problem: given an array and a target, return two numbers that add up to the target.

Pattern: hash set - for each number, check if target − number was already seen. O(n) time.

def two_number_sum(nums, target):
    seen = set()
    for n in nums:
        complement = target - n
        if complement in seen:
            return [complement, n]
        seen.add(n)
    return []
# two_number_sum([3, 5, -4, 8, 11, 1, -1, 6], 10) -> [11, -1]

Validate Subsequence EASY

Problem: is the second array a subsequence of the first (same order, not necessarily contiguous)?

Pattern: two pointers - walk the main array, advance the second pointer on each match. O(n).

def is_valid_subsequence(array, sequence):
    i = 0
    for value in array:
        if i == len(sequence):
            break
        if sequence[i] == value:
            i += 1
    return i == len(sequence)

Find Three Largest Numbers EASY

Problem: return the 3 largest numbers in sorted order - without fully sorting.

Pattern: a single pass keeping the top 3. O(n).

def three_largest(nums):
    top = [None, None, None]
    for n in nums:
        if top[2] is None or n > top[2]:
            top = [top[1], top[2], n]
        elif top[1] is None or n > top[1]:
            top = [top[1], n, top[2]]
        elif top[0] is None or n > top[0]:
            top = [n, top[1], top[2]]
    return top

Tournament Winner EASY

class Solution:
    def tournament_winner(self, competitions, results):
        scores = {"": 0}; best = ""
        for i, (home, away) in enumerate(competitions):
            winner = home if results[i] == 1 else away
            scores[winner] = scores.get(winner, 0) + 3
            if scores[winner] > scores[best]: best = winner
        return best

Transpose Matrix EASY

class Solution:
    def transpose(self, matrix):
        return [[matrix[r][c] for r in range(len(matrix))]
                for c in range(len(matrix[0]))]

Product Sum (nested lists) EASY

class Solution:
    def product_sum(self, arr, depth=1):
        total = 0
        for el in arr:
            if isinstance(el, list):
                total += self.product_sum(el, depth + 1)
            else:
                total += el
        return total * depth

Minimum Waiting Time EASY

Order shortest jobs first so everyone waits the least. Greedy. O(n log n).

class Solution:
    def min_waiting_time(self, queries):
        queries.sort()
        total = 0
        for i, duration in enumerate(queries):
            total += duration * (len(queries) - i - 1)
        return total

Common Characters EASY

class Solution:
    def common_characters(self, strings):
        result = set(strings[0])
        for s in strings[1:]:
            result &= set(s)
        return list(result)

Semordnilap EASY

Pairs where one word is the other reversed (e.g. "diaper"/"repaid"). O(n·len).

class Solution:
    def semordnilap(self, words):
        seen = set(words); pairs = []
        for w in words:
            rev = w[::-1]
            if rev in seen and rev != w:
                pairs.append([w, rev]); seen.discard(w); seen.discard(rev)
        return pairs

Branch Sums (tree) EASY

class Solution:
    def branch_sums(self, root):
        sums = []
        def helper(node, running):
            if not node: return
            running += node.value
            if not node.left and not node.right:
                sums.append(running); return
            helper(node.left, running); helper(node.right, running)
        helper(root, 0)
        return sums

Node Depths (tree) EASY

class Solution:
    def node_depths(self, root, depth=0):
        if not root: return 0
        return depth + self.node_depths(root.left, depth + 1) + self.node_depths(root.right, depth + 1)

3Sum MEDIUM

Problem: Return all unique triples in the array that sum to zero.
def three_sum_classic(nums):
    nums.sort()
    result = []
    for i in range(len(nums) - 2):
        if i > 0 and nums[i] == nums[i-1]:
            continue
        lo, hi = i + 1, len(nums) - 1
        while lo < hi:
            total = nums[i] + nums[lo] + nums[hi]
            if total < 0:
                lo += 1
            elif total > 0:
                hi -= 1
            else:
                result.append([nums[i], nums[lo], nums[hi]])
                while lo < hi and nums[lo] == nums[lo+1]:
                    lo += 1
                while lo < hi and nums[hi] == nums[hi-1]:
                    hi -= 1
                lo += 1
                hi -= 1
    return result

Container With Most Water MEDIUM

Problem: Given heights of vertical lines, return the maximum area of water a container formed by two lines can hold.
def max_area_classic(height):
    lo, hi = 0, len(height) - 1
    best = 0
    while lo < hi:
        best = max(best, min(height[lo], height[hi]) * (hi - lo))
        if height[lo] < height[hi]:
            lo += 1
        else:
            hi -= 1
    return best

Longest Consecutive Sequence MEDIUM

Problem: Return the length of the longest run of consecutive integers present in the array, in O(n).
def longest_consecutive_classic(nums):
    num_set = set(nums)
    best = 0
    for n in num_set:
        if n - 1 not in num_set:
            length = 1
            while n + length in num_set:
                length += 1
            best = max(best, length)
    return best

Subarray Sum Equals K MEDIUM

Problem: Return the number of contiguous subarrays whose elements sum to exactly k, using prefix sums.
def subarray_sum_classic(nums, k):
    from collections import defaultdict
    prefix = defaultdict(int)
    prefix[0] = 1
    total = 0
    count = 0
    for n in nums:
        total += n
        count += prefix[total - k]
        prefix[total] += 1
    return count

Find All Duplicates in an Array MEDIUM

Problem: Every value is in the range 1..n and appears once or twice. Return all values that appear twice, in O(n) time and O(1) extra space.
def find_duplicates(nums):
    result = []
    for n in nums:
        idx = abs(n) - 1
        if nums[idx] < 0:
            result.append(abs(n))
        else:
            nums[idx] = -nums[idx]
    return result

Majority Element MEDIUM

Problem: Return the element that appears more than n/2 times, using the Boyer-Moore voting algorithm.
def majority_element_classic(nums):
    count = 0
    candidate = None
    for n in nums:
        if count == 0:
            candidate = n
        count += 1 if n == candidate else -1
    return candidate

Valid Sudoku MEDIUM

Problem: Return True if a 9x9 Sudoku board (with '.' for empty) is valid: no row, column, or 3x3 box has a repeated digit.
def is_valid_sudoku_classic(board):
    seen = set()
    for r in range(9):
        for c in range(9):
            val = board[r][c]
            if val == '.':
                continue
            keys = [('row', r, val), ('col', c, val), ('box', r // 3, c // 3, val)]
            for key in keys:
                if key in seen:
                    return False
                seen.add(key)
    return True

Game of Life MEDIUM

Problem: Compute the next state of Conway's Game of Life board in place (encode transitions with intermediate values), then return it.
def game_of_life_classic(board):
    rows, cols = len(board), len(board[0])
    for r in range(rows):
        for c in range(cols):
            live = 0
            for dr in (-1, 0, 1):
                for dc in (-1, 0, 1):
                    if dr == 0 and dc == 0:
                        continue
                    nr, nc = r + dr, c + dc
                    if 0 <= nr < rows and 0 <= nc < cols and board[nr][nc] in (1, 2):
                        live += 1
            if board[r][c] == 1 and (live < 2 or live > 3):
                board[r][c] = 2
            elif board[r][c] == 0 and live == 3:
                board[r][c] = 3
    for r in range(rows):
        for c in range(cols):
            board[r][c] %= 2
    return board

Insert Interval MEDIUM

Problem: Insert a new interval into a list of sorted non-overlapping intervals, merging any overlaps. Return the result.
def insert_interval_classic(intervals, new_interval):
    result = []
    i = 0
    n = len(intervals)
    while i < n and intervals[i][1] < new_interval[0]:
        result.append(intervals[i])
        i += 1
    start, end = new_interval
    while i < n and intervals[i][0] <= end:
        start = min(start, intervals[i][0])
        end = max(end, intervals[i][1])
        i += 1
    result.append([start, end])
    while i < n:
        result.append(intervals[i])
        i += 1
    return result

H-Index MEDIUM

Problem: Given citation counts per paper, return the researcher's h-index: the largest h such that h papers each have at least h citations.
def h_index_classic(citations):
    citations.sort(reverse=True)
    h = 0
    for i, c in enumerate(citations):
        if c >= i + 1:
            h = i + 1
        else:
            break
    return h

Maximal Square HARD

Problem: Given a binary matrix, return the area of the largest square containing only 1s (DP on square side length).
def maximal_square_classic(matrix):
    if not matrix:
        return 0
    rows, cols = len(matrix), len(matrix[0])
    dp = [[0] * (cols + 1) for _ in range(rows + 1)]
    best = 0
    for r in range(1, rows + 1):
        for c in range(1, cols + 1):
            if matrix[r-1][c-1] == 1:
                dp[r][c] = min(dp[r-1][c], dp[r][c-1], dp[r-1][c-1]) + 1
                best = max(best, dp[r][c])
    return best * best

Dungeon Game HARD

Problem: A knight starts top-left and must reach the bottom-right of a grid of health deltas, keeping health above 0 at all times. Return the minimum starting health (DP from the goal backward).
def calculate_minimum_hp(dungeon):
    rows, cols = len(dungeon), len(dungeon[0])
    dp = [[float('inf')] * (cols + 1) for _ in range(rows + 1)]
    dp[rows][cols-1] = dp[rows-1][cols] = 1
    for r in range(rows - 1, -1, -1):
        for c in range(cols - 1, -1, -1):
            need = min(dp[r+1][c], dp[r][c+1]) - dungeon[r][c]
            dp[r][c] = max(1, need)
    return dp[0][0]

Longest Increasing Path in a Matrix HARD

Problem: Return the length of the longest strictly increasing path in a matrix, moving up/down/left/right (memoized DFS).
def longest_increasing_path(matrix):
    if not matrix:
        return 0
    rows, cols = len(matrix), len(matrix[0])
    memo = {}
    def dfs(r, c):
        if (r, c) in memo:
            return memo[(r, c)]
        best = 1
        for dr, dc in ((1,0),(-1,0),(0,1),(0,-1)):
            nr, nc = r + dr, c + dc
            if 0 <= nr < rows and 0 <= nc < cols and matrix[nr][nc] > matrix[r][c]:
                best = max(best, 1 + dfs(nr, nc))
        memo[(r, c)] = best
        return best
    return max(dfs(r, c) for r in range(rows) for c in range(cols))

Best Time to Buy and Sell Stock III HARD

Problem: You may complete at most two transactions (buy then sell). Return the maximum profit.
def max_profit_iii_classic(prices):
    buy1 = buy2 = float('-inf')
    sell1 = sell2 = 0
    for price in prices:
        buy1 = max(buy1, -price)
        sell1 = max(sell1, buy1 + price)
        buy2 = max(buy2, sell1 - price)
        sell2 = max(sell2, buy2 + price)
    return sell2

Best Time to Buy and Sell Stock IV HARD

Problem: You may complete at most k transactions. Return the maximum profit.
def max_profit_iv_classic(k, prices):
    if not prices or k == 0:
        return 0
    n = len(prices)
    if k >= n // 2:
        return sum(max(0, prices[i] - prices[i-1]) for i in range(1, n))
    buy = [float('-inf')] * (k + 1)
    sell = [0] * (k + 1)
    for price in prices:
        for j in range(1, k + 1):
            buy[j] = max(buy[j], sell[j-1] - price)
            sell[j] = max(sell[j], buy[j] + price)
    return sell[k]

Burst Balloons HARD

Problem: Bursting balloon i earns nums[left]*nums[i]*nums[right] (neighbors after removals). Return the maximum coins from bursting all balloons (interval DP).
def max_coins(nums):
    balloons = [1] + nums + [1]
    n = len(balloons)
    dp = [[0] * n for _ in range(n)]
    for length in range(2, n):
        for left in range(n - length):
            right = left + length
            for k in range(left + 1, right):
                dp[left][right] = max(
                    dp[left][right],
                    balloons[left] * balloons[k] * balloons[right] + dp[left][k] + dp[k][right])
    return dp[0][n-1]

Russian Doll Envelopes HARD

Problem: Each envelope is [width, height]; one fits inside another only if both dimensions are strictly greater. Return the maximum number of nested envelopes.
def max_envelopes(envelopes):
    import bisect
    envelopes.sort(key=lambda e: (e[0], -e[1]))
    tails = []
    for _, h in envelopes:
        i = bisect.bisect_left(tails, h)
        if i == len(tails):
            tails.append(h)
        else:
            tails[i] = h
    return len(tails)

Minimum Window Subsequence HARD

Problem: Return the smallest contiguous substring of s that contains t as a subsequence, or '' if none exists.
def min_window_subsequence(s, t):
    m, n = len(s), len(t)
    start = -1
    min_len = float('inf')
    i = 0
    while i < m:
        j = 0
        while i < m:
            if s[i] == t[j]:
                j += 1
                if j == n:
                    break
            i += 1
        if j != n:
            break
        end = i + 1
        j = n - 1
        while j >= 0:
            if s[i] == t[j]:
                j -= 1
            i -= 1
        i += 1
        if end - i < min_len:
            min_len = end - i
            start = i
        i += 1
    return '' if start == -1 else s[start:start + min_len]

Largest Number HARD

Problem: Arrange the non-negative integers so that concatenating them forms the largest possible number. Return it as a string.
def largest_number(nums):
    from functools import cmp_to_key
    strs = list(map(str, nums))
    def compare(a, b):
        if a + b > b + a:
            return -1
        elif a + b < b + a:
            return 1
        return 0
    strs.sort(key=cmp_to_key(compare))
    result = ''.join(strs)
    return '0' if result[0] == '0' else result

Max Points on a Line HARD

Problem: Given points on a plane, return the maximum number of points that lie on the same straight line.
def max_points_classic(points):
    from math import gcd
    if len(points) <= 2:
        return len(points)
    best = 0
    for i in range(len(points)):
        slopes = {}
        for j in range(len(points)):
            if i == j:
                continue
            dx = points[j][0] - points[i][0]
            dy = points[j][1] - points[i][1]
            g = gcd(dx, dy)
            if g != 0:
                dx //= g
                dy //= g
            if dx < 0 or (dx == 0 and dy < 0):
                dx, dy = -dx, -dy
            slopes[(dx, dy)] = slopes.get((dx, dy), 0) + 1
            best = max(best, slopes[(dx, dy)])
    return best + 1

Arrays & Matrix

Sorted Squared Array EASY

Square each number of a sorted array, return sorted. Two pointers from both ends (largest squares are at the ends). O(n).

class Solution:
    def sorted_squared(self, arr):
        res = [0] * len(arr)
        lo, hi = 0, len(arr) - 1
        for i in range(len(arr) - 1, -1, -1):
            if abs(arr[lo]) > abs(arr[hi]):
                res[i] = arr[lo] ** 2; lo += 1
            else:
                res[i] = arr[hi] ** 2; hi -= 1
        return res

Move Element To End EASY

Move all copies of a value to the end, in place. Two pointers. O(n).

class Solution:
    def move_to_end(self, arr, target):
        lo, hi = 0, len(arr) - 1
        while lo < hi:
            while lo < hi and arr[hi] == target: hi -= 1
            if arr[lo] == target:
                arr[lo], arr[hi] = arr[hi], arr[lo]
            lo += 1
        return arr

Monotonic Array EASY

Is the array entirely non-increasing or non-decreasing? One pass. O(n).

class Solution:
    def is_monotonic(self, arr):
        up = all(arr[i] <= arr[i+1] for i in range(len(arr)-1))
        down = all(arr[i] >= arr[i+1] for i in range(len(arr)-1))
        return up or down

Generate Document EASY

from collections import Counter

class Solution:
    def generate_document(self, characters, document):
        available = Counter(characters)
        for ch in document:
            if available[ch] <= 0: return False
            available[ch] -= 1
        return True

Best Seat EASY

class Solution:
    def best_seat(self, seats):
        best, max_space, left = -1, 0, 0
        while left < len(seats):
            right = left + 1
            while right < len(seats) and seats[right] == 0: right += 1
            if right - left - 1 > max_space:
                max_space = right - left - 1; best = (left + right) // 2
            left = right
        return best

Running Sum of 1D Array EASY

Problem: Return the running (prefix) sum of the array, where output[i] is the sum of nums[0..i].
def running_sum(nums):
    result = []
    total = 0
    for n in nums:
        total += n
        result.append(total)
    return result

Max Consecutive Ones EASY

Problem: Given a binary array, return the length of the longest run of consecutive 1s.
def find_max_consecutive_ones(nums):
    best = current = 0
    for n in nums:
        if n == 1:
            current += 1
            best = max(best, current)
        else:
            current = 0
    return best

Move Zeroes EASY

Problem: Move all zeroes to the end of the array while keeping the order of non-zero elements, then return it.
def move_zeroes(nums):
    a = list(nums)
    insert = 0
    for n in a:
        if n != 0:
            a[insert] = n
            insert += 1
    while insert < len(a):
        a[insert] = 0
        insert += 1
    return a

Two Sum II (Sorted Input) EASY

Problem: Given a sorted array, return the 1-based indices of the two numbers that add up to target, using two pointers.
def two_sum_sorted(numbers, target):
    lo, hi = 0, len(numbers) - 1
    while lo < hi:
        total = numbers[lo] + numbers[hi]
        if total == target:
            return [lo + 1, hi + 1]
        if total < target:
            lo += 1
        else:
            hi -= 1
    return [-1, -1]

Plus One EASY

Problem: A non-negative integer is stored as a digit array (most significant first). Add one and return the resulting digit array.
def plus_one_arrays(digits):
    a = list(digits)
    for i in range(len(a) - 1, -1, -1):
        if a[i] < 9:
            a[i] += 1
            return a
        a[i] = 0
    return [1] + a

Single Number EASY

Problem: Every element appears twice except one. Return the element that appears only once, using XOR in O(n) time and O(1) space.
def single_number_arrays(nums):
    result = 0
    for n in nums:
        result ^= n
    return result

Three Number Sum MEDIUM

Find all triplets that sum to the target. Sort, then for each number use two pointers. O(n²).

class Solution:
    def three_number_sum(self, nums, target):
        nums.sort()
        res = []
        for i in range(len(nums) - 2):
            lo, hi = i + 1, len(nums) - 1
            while lo < hi:
                s = nums[i] + nums[lo] + nums[hi]
                if s == target:
                    res.append([nums[i], nums[lo], nums[hi]]); lo += 1; hi -= 1
                elif s < target: lo += 1
                else: hi -= 1
        return res

Smallest Difference MEDIUM

Pick one number from each array so their difference is smallest. Sort both, walk two pointers. O(n log n).

class Solution:
    def smallest_difference(self, a, b):
        a.sort(); b.sort()
        i = j = 0; best = float("inf"); pair = []
        while i < len(a) and j < len(b):
            if abs(a[i] - b[j]) < best:
                best = abs(a[i] - b[j]); pair = [a[i], b[j]]
            if a[i] < b[j]: i += 1
            elif a[i] > b[j]: j += 1
            else: return [a[i], b[j]]
        return pair

Array Of Products MEDIUM

Each output[i] = product of all other numbers - without division. Prefix & suffix products. O(n).

class Solution:
    def array_of_products(self, arr):
        n = len(arr); res = [1] * n
        left = 1
        for i in range(n):
            res[i] = left; left *= arr[i]
        right = 1
        for i in range(n - 1, -1, -1):
            res[i] *= right; right *= arr[i]
        return res

First Duplicate Value MEDIUM

Return the first value that appears twice. Hash set. O(n).

class Solution:
    def first_duplicate(self, arr):
        seen = set()
        for n in arr:
            if n in seen: return n
            seen.add(n)
        return -1

Merge Overlapping Intervals MEDIUM

Combine overlapping [start, end] ranges. Sort by start, then merge. O(n log n).

class Solution:
    def merge_intervals(self, intervals):
        intervals.sort(key=lambda x: x[0])
        merged = [intervals[0]]
        for start, end in intervals[1:]:
            if start <= merged[-1][1]:
                merged[-1][1] = max(merged[-1][1], end)
            else:
                merged.append([start, end])
        return merged

Kadane's Algorithm - Max Subarray Sum MEDIUM

Largest sum of any contiguous subarray. Track best ending here vs. restart. O(n).

class Solution:
    def max_subarray(self, arr):
        cur = best = arr[0]
        for n in arr[1:]:
            cur = max(n, cur + n)
            best = max(best, cur)
        return best

Zigzag Traverse MEDIUM

class Solution:
    def zigzag_traverse(self, matrix):
        H, W = len(matrix) - 1, len(matrix[0]) - 1
        res = []; r = c = 0; down = True
        while 0 <= r <= H and 0 <= c <= W:
            res.append(matrix[r][c])
            if down:
                if c == 0 or r == H:
                    down = False
                    if r == H: c += 1
                    else: r += 1
                else: r += 1; c -= 1
            else:
                if r == 0 or c == W:
                    down = True
                    if c == W: r += 1
                    else: c += 1
                else: r -= 1; c += 1
        return res

Subarray Sort MEDIUM

Smallest subarray that, if sorted, makes the whole array sorted. O(n).

class Solution:
    def subarray_sort(self, arr):
        max_so_far = arr[0]; right = -1
        for i in range(len(arr)):
            if arr[i] < max_so_far: right = i
            else: max_so_far = arr[i]
        min_so_far = arr[-1]; left = -1
        for i in range(len(arr) - 1, -1, -1):
            if arr[i] > min_so_far: left = i
            else: min_so_far = arr[i]
        return [left, right]

Largest Range MEDIUM

Longest run of consecutive integers (any order). Hash set. O(n).

class Solution:
    def largest_range(self, arr):
        nums = set(arr); best = []; longest = 0
        for n in arr:
            if n - 1 not in nums:               # start of a run
                length = 1
                while n + length in nums: length += 1
                if length > longest:
                    longest = length; best = [n, n + length - 1]
        return best

Min Rewards MEDIUM

Give each child ≥1 reward; a higher score than a neighbor needs more. Two passes. O(n).

class Solution:
    def min_rewards(self, scores):
        rewards = [1] * len(scores)
        for i in range(1, len(scores)):
            if scores[i] > scores[i-1]: rewards[i] = rewards[i-1] + 1
        for i in range(len(scores) - 2, -1, -1):
            if scores[i] > scores[i+1]:
                rewards[i] = max(rewards[i], rewards[i+1] + 1)
        return sum(rewards)

Single Cycle Check MEDIUM

Jumping by each value, do you visit every index exactly once and land back at the start? O(n).

class Solution:
    def single_cycle_check(self, arr):
        visited = 0; idx = 0
        while visited < len(arr):
            if visited > 0 and idx == 0: return False   # back to start too early
            visited += 1
            idx = (idx + arr[idx]) % len(arr)
        return idx == 0

Valid Starting City MEDIUM

Circular road of cities with fuel - find the only city you can start from and finish the loop. O(n).

class Solution:
    def valid_starting_city(self, distances, fuel, mpg):
        min_remaining = 0; remaining = 0; start = 0
        for i in range(1, len(distances)):
            remaining += fuel[i-1] * mpg - distances[i-1]
            if remaining < min_remaining:
                min_remaining = remaining; start = i
        return start

Majority Element (Boyer-Moore) MEDIUM

The element appearing > n/2 times - in O(n) time, O(1) space.

class Solution:
    def majority_element(self, nums):
        count = 0; candidate = None
        for n in nums:
            if count == 0: candidate = n
            count += 1 if n == candidate else -1
        return candidate

Task Assignment MEDIUM

Pair tasks for k workers (2 each) to minimize total time - pair fastest with slowest. O(n log n).

class Solution:
    def task_assignment(self, k, durations):
        order = sorted(range(len(durations)), key=lambda i: durations[i])
        return [[order[i], order[len(durations) - 1 - i]] for i in range(k)]

Missing Numbers (two missing from 1..n) MEDIUM

class Solution:
    def missing_numbers(self, nums):
        total = sum(range(1, len(nums) + 3))
        missing_sum = total - sum(nums)
        avg = missing_sum // 2
        low = sum(x for x in nums if x <= avg)
        a = sum(range(1, avg + 1)) - low
        return [a, missing_sum - a]

Minimum Area Rectangle MEDIUM

Smallest axis-aligned rectangle from a set of points. Check diagonal corner pairs. O(n²).

class Solution:
    def minimum_area_rectangle(self, points):
        seen = set(map(tuple, points)); best = float("inf")
        for i in range(len(points)):
            for j in range(i):
                x1, y1 = points[i]; x2, y2 = points[j]
                if x1 != x2 and y1 != y2 and (x1, y2) in seen and (x2, y1) in seen:
                    best = min(best, abs(x1 - x2) * abs(y1 - y2))
        return best if best != float("inf") else 0

Maximum Sum Submatrix (fixed size) MEDIUM

Largest sum of any size×size square. 2-D prefix sums. O(w·h).

class Solution:
    def max_sum_submatrix(self, matrix, size):
        rows, cols = len(matrix), len(matrix[0])
        s = [[0] * (cols + 1) for _ in range(rows + 1)]
        for r in range(rows):
            for c in range(cols):
                s[r+1][c+1] = matrix[r][c] + s[r][c+1] + s[r+1][c] - s[r][c]
        best = float("-inf")
        for r in range(size, rows + 1):
            for c in range(size, cols + 1):
                total = s[r][c] - s[r-size][c] - s[r][c-size] + s[r-size][c-size]
                best = max(best, total)
        return best

Spiral Traverse MEDIUM

Read a matrix in a spiral. Shrink four borders inward. O(n).

class Solution:
    def spiral_traverse(self, matrix):
        res = []
        top, bottom = 0, len(matrix) - 1
        left, right = 0, len(matrix[0]) - 1
        while top <= bottom and left <= right:
            for c in range(left, right + 1): res.append(matrix[top][c])
            for r in range(top + 1, bottom + 1): res.append(matrix[r][right])
            if top < bottom:
                for c in range(right - 1, left - 1, -1): res.append(matrix[bottom][c])
            if left < right:
                for r in range(bottom - 1, top, -1): res.append(matrix[r][left])
            top += 1; bottom -= 1; left += 1; right -= 1
        return res

Longest Peak MEDIUM

Length of the longest "up then down" run. Find each peak, expand both ways. O(n).

class Solution:
    def longest_peak(self, arr):
        longest = 0; i = 1
        while i < len(arr) - 1:
            if not (arr[i-1] < arr[i] > arr[i+1]):
                i += 1; continue
            left = i - 2
            while left >= 0 and arr[left] < arr[left + 1]: left -= 1
            right = i + 2
            while right < len(arr) and arr[right] < arr[right - 1]: right += 1
            longest = max(longest, right - left - 1)
            i = right
        return longest

Three Number Sort (Dutch flag) MEDIUM

Sort an array containing three distinct values, in place, in one pass. O(n).

class Solution:
    def three_number_sort(self, arr, order):
        first, second = order[0], order[1]
        low, mid, high = 0, 0, len(arr) - 1
        while mid <= high:
            if arr[mid] == first:
                arr[low], arr[mid] = arr[mid], arr[low]; low += 1; mid += 1
            elif arr[mid] == second:
                mid += 1
            else:
                arr[mid], arr[high] = arr[high], arr[mid]; high -= 1
        return arr

Min Number Of Jumps MEDIUM

Fewest jumps to reach the end, where each value is the max jump length. Greedy. O(n).

class Solution:
    def min_jumps(self, arr):
        if len(arr) == 1: return 0
        jumps = 0; max_reach = arr[0]; steps = arr[0]
        for i in range(1, len(arr) - 1):
            max_reach = max(max_reach, i + arr[i])
            steps -= 1
            if steps == 0:
                jumps += 1; steps = max_reach - i
        return jumps + 1

Reveal Minesweeper MEDIUM

Click a cell: a mine → "X"; otherwise show the mine count, and flood-fill zeros. "M"=mine, "H"=hidden.

class Solution:
    def reveal_minesweeper(self, board, row, col):
        if board[row][col] == "M":
            board[row][col] = "X"; return board
        dirs = [(-1,-1),(-1,0),(-1,1),(0,-1),(0,1),(1,-1),(1,0),(1,1)]
        def mines(r, c):
            return sum(1 for dr, dc in dirs
                       if 0 <= r+dr < len(board) and 0 <= c+dc < len(board[0])
                       and board[r+dr][c+dc] == "M")
        stack = [(row, col)]
        while stack:
            r, c = stack.pop()
            if board[r][c] != "H": continue
            n = mines(r, c)
            board[r][c] = str(n) if n > 0 else "0"
            if n == 0:
                for dr, dc in dirs:
                    nr, nc = r+dr, c+dc
                    if 0 <= nr < len(board) and 0 <= nc < len(board[0]) and board[nr][nc] == "H":
                        stack.append((nr, nc))
        return board

Four Number Sum HARD

All quadruplets summing to target. Hash pair-sums while scanning. Average O(n²).

class Solution:
    def four_number_sum(self, nums, target):
        pair_sums = {}; res = []
        for i in range(1, len(nums) - 1):
            for j in range(i + 1, len(nums)):
                need = target - (nums[i] + nums[j])
                for pair in pair_sums.get(need, []):
                    res.append(pair + [nums[i], nums[j]])
            for k in range(i):
                s = nums[i] + nums[k]
                pair_sums.setdefault(s, []).append([nums[k], nums[i]])
        return res

Largest Rectangle Under Skyline HARD

Biggest rectangle in a histogram. Monotonic stack of increasing heights. O(n).

class Solution:
    def largest_rectangle(self, heights):
        stack = []; best = 0
        for i, h in enumerate(heights + [0]):     # sentinel flushes the stack
            while stack and heights[stack[-1]] >= h:
                height = heights[stack.pop()]
                width = i if not stack else i - stack[-1] - 1
                best = max(best, height * width)
            stack.append(i)
        return best

Apartment Hunting HARD

Pick the block minimizing the max distance to every requirement. Precompute nearest each side. O(b·r).

class Solution:
    def apartment_hunting(self, blocks, reqs):
        n = len(blocks); dists = []
        for req in reqs:
            closest = [float("inf")] * n; nearest = float("inf")
            for i in range(n):
                if blocks[i][req]: nearest = i
                closest[i] = abs(i - nearest)
            for i in range(n - 1, -1, -1):
                if blocks[i][req]: nearest = i
                closest[i] = min(closest[i], abs(i - nearest))
            dists.append(closest)
        best, best_max = 0, float("inf")
        for i in range(n):
            worst = max(d[i] for d in dists)
            if worst < best_max: best_max, best = worst, i
        return best

Water Area (trapping rain) HARD

Water trapped above each bar = min(tallest left, tallest right) − its height. O(n).

class Solution:
    def water_area(self, heights):
        n = len(heights)
        left_max = [0] * n; right_max = [0] * n
        m = 0
        for i in range(n): left_max[i] = m; m = max(m, heights[i])
        m = 0
        for i in range(n - 1, -1, -1): right_max[i] = m; m = max(m, heights[i])
        return sum(max(0, min(left_max[i], right_max[i]) - heights[i]) for i in range(n))

Trapping Rain Water (Two Pointers) HARD

Problem: Given an elevation map, compute how much water it can trap after raining, using two pointers in O(n) time and O(1) space.
def trap_arrays(height):
    if not height:
        return 0
    lo, hi = 0, len(height) - 1
    left_max, right_max = height[lo], height[hi]
    water = 0
    while lo < hi:
        if left_max < right_max:
            lo += 1
            left_max = max(left_max, height[lo])
            water += left_max - height[lo]
        else:
            hi -= 1
            right_max = max(right_max, height[hi])
            water += right_max - height[hi]
    return water

Product of Array Except Self HARD

Problem: Return an array where each element is the product of all other elements, without using division and in O(n).
def product_except_self_arrays(nums):
    n = len(nums)
    result = [1] * n
    prefix = 1
    for i in range(n):
        result[i] = prefix
        prefix *= nums[i]
    suffix = 1
    for i in range(n - 1, -1, -1):
        result[i] *= suffix
        suffix *= nums[i]
    return result

First Missing Positive HARD

Problem: Return the smallest positive integer missing from the array in O(n) time and O(1) extra space (index placement).
def first_missing_positive(nums):
    n = len(nums)
    for i in range(n):
        while 1 <= nums[i] <= n and nums[nums[i] - 1] != nums[i]:
            j = nums[i] - 1
            nums[i], nums[j] = nums[j], nums[i]
    for i in range(n):
        if nums[i] != i + 1:
            return i + 1
    return n + 1

Maximum Product Subarray HARD

Problem: Return the largest product of any contiguous subarray, tracking both the running max and min (for negatives).
def max_product(nums):
    best = cur_max = cur_min = nums[0]
    for n in nums[1:]:
        candidates = (n, cur_max * n, cur_min * n)
        cur_max = max(candidates)
        cur_min = min(candidates)
        best = max(best, cur_max)
    return best

Spiral Matrix HARD

Problem: Return all elements of the matrix in spiral order (clockwise from the top-left).
def spiral_order_arrays(matrix):
    if not matrix:
        return []
    result = []
    top, bottom = 0, len(matrix) - 1
    left, right = 0, len(matrix[0]) - 1
    while top <= bottom and left <= right:
        for c in range(left, right + 1):
            result.append(matrix[top][c])
        top += 1
        for r in range(top, bottom + 1):
            result.append(matrix[r][right])
        right -= 1
        if top <= bottom:
            for c in range(right, left - 1, -1):
                result.append(matrix[bottom][c])
            bottom -= 1
        if left <= right:
            for r in range(bottom, top - 1, -1):
                result.append(matrix[r][left])
            left += 1
    return result

Rotate Image HARD

Problem: Rotate an n x n matrix 90 degrees clockwise in place (transpose then reverse each row), then return it.
def rotate_image_arrays(matrix):
    n = len(matrix)
    for i in range(n):
        for j in range(i + 1, n):
            matrix[i][j], matrix[j][i] = matrix[j][i], matrix[i][j]
    for row in matrix:
        row.reverse()
    return matrix

Set Matrix Zeroes HARD

Problem: If a cell is 0, set its entire row and column to 0. Do it in place using the first row/column as markers, then return the matrix.
def set_zeroes_arrays(matrix):
    rows, cols = len(matrix), len(matrix[0])
    first_row = any(matrix[0][c] == 0 for c in range(cols))
    first_col = any(matrix[r][0] == 0 for r in range(rows))
    for r in range(1, rows):
        for c in range(1, cols):
            if matrix[r][c] == 0:
                matrix[r][0] = 0
                matrix[0][c] = 0
    for r in range(1, rows):
        for c in range(1, cols):
            if matrix[r][0] == 0 or matrix[0][c] == 0:
                matrix[r][c] = 0
    if first_row:
        for c in range(cols):
            matrix[0][c] = 0
    if first_col:
        for r in range(rows):
            matrix[r][0] = 0
    return matrix

Strings

Palindrome Check EASY

class Solution:
    def is_palindrome(self, s):
        lo, hi = 0, len(s) - 1
        while lo < hi:
            if s[lo] != s[hi]: return False
            lo += 1; hi -= 1
        return True
    # or simply: return s == s[::-1]

Caesar Cipher Encryptor EASY

Shift each letter by a key (wrapping around the alphabet). O(n).

class Solution:
    def caesar_cipher(self, s, key):
        out = []
        for ch in s:
            code = (ord(ch) - ord("a") + key) % 26
            out.append(chr(ord("a") + code))
        return "".join(out)

First Non-Repeating Character EASY

class Solution:
    def first_non_repeating(self, s):
        counts = {}
        for ch in s: counts[ch] = counts.get(ch, 0) + 1
        for i, ch in enumerate(s):
            if counts[ch] == 1: return i
        return -1

Run-Length Encoding EASY

class Solution:
    def run_length_encode(self, s):
        out = []; count = 1
        for i in range(1, len(s) + 1):
            if i < len(s) and s[i] == s[i-1] and count < 9:
                count += 1
            else:
                out.append(str(count) + s[i-1]); count = 1
        return "".join(out)
    # "AAAAAAAAAAAAABBCCCCDD" -> "9A4A2B4C2D"

Reverse the Word Order EASY

Problem: Reverse the order of words in a sentence, collapsing extra spaces (e.g. 'the sky is blue' -> 'blue is sky the').
def reverse_words_strings(s):
    return ' '.join(reversed(s.split()))

Valid Anagram EASY

Problem: Return True if t is an anagram of s (same characters with the same counts).
def is_anagram_strings(s, t):
    from collections import Counter
    return Counter(s) == Counter(t)

First Unique Character EASY

Problem: Return the index of the first non-repeating character in the string, or -1 if there is none.
def first_uniq_char(s):
    from collections import Counter
    counts = Counter(s)
    for i, c in enumerate(s):
        if counts[c] == 1:
            return i
    return -1

Capitalize Each Word EASY

Problem: Return the string with the first letter of each word capitalized and the rest lowercased.
def capitalize_words(s):
    return ' '.join(word[0].upper() + word[1:].lower() if word else word for word in s.split(' '))

Remove Vowels EASY

Problem: Return the string with all vowels (a, e, i, o, u) removed.
def remove_vowels(s):
    return ''.join(c for c in s if c.lower() not in 'aeiou')

Longest Common Prefix EASY

Problem: Return the longest common prefix shared by all strings in the list ('' if none).
def longest_common_prefix_strings(strs):
    if not strs:
        return ''
    prefix = strs[0]
    for s in strs[1:]:
        while not s.startswith(prefix):
            prefix = prefix[:-1]
            if not prefix:
                return ''
    return prefix

Valid Palindrome (Alphanumeric) EASY

Problem: Return True if the string is a palindrome considering only alphanumeric characters and ignoring case.
def is_palindrome_strings(s):
    cleaned = [c.lower() for c in s if c.isalnum()]
    return cleaned == cleaned[::-1]

Reverse Only Letters EASY

Problem: Reverse only the letters of the string, leaving every non-letter character in its original position.
def reverse_only_letters(s):
    chars = list(s)
    lo, hi = 0, len(chars) - 1
    while lo < hi:
        if not chars[lo].isalpha():
            lo += 1
        elif not chars[hi].isalpha():
            hi -= 1
        else:
            chars[lo], chars[hi] = chars[hi], chars[lo]
            lo += 1
            hi -= 1
    return ''.join(chars)

Longest Palindromic Substring MEDIUM

Expand around every center (odd & even). O(n²).

class Solution:
    def longest_palindrome(self, s):
        res = ""
        def expand(l, r):
            while l >= 0 and r < len(s) and s[l] == s[r]:
                l -= 1; r += 1
            return s[l+1:r]
        for i in range(len(s)):
            for cand in (expand(i, i), expand(i, i+1)):
                if len(cand) > len(res): res = cand
        return res

Group Anagrams MEDIUM

class Solution:
    def group_anagrams(self, words):
        groups = {}
        for w in words:
            key = "".join(sorted(w))
            groups.setdefault(key, []).append(w)
        return list(groups.values())

Valid IP Addresses MEDIUM

class Solution:
    def valid_ip_addresses(self, s):
        res = []
        def ok(part):
            return len(part) == 1 or (part[0] != "0" and int(part) <= 255)
        for a in range(1, 4):
            for b in range(a+1, a+4):
                for c in range(b+1, b+4):
                    p1, p2, p3, p4 = s[:a], s[a:b], s[b:c], s[c:]
                    if 1 <= len(p4) <= 3 and all(ok(p) for p in (p1,p2,p3,p4)):
                        res.append(f"{p1}.{p2}.{p3}.{p4}")
        return res

Reverse Words In String MEDIUM

class Solution:
    def reverse_words(self, s):
        return " ".join(s.split()[::-1])
    # "the sky is blue" -> "blue is sky the"

One Edit Away MEDIUM

Are two strings at most one insert/delete/replace apart? O(n).

class Solution:
    def one_edit(self, a, b):
        if abs(len(a) - len(b)) > 1: return False
        i = j = 0; edited = False
        while i < len(a) and j < len(b):
            if a[i] != b[j]:
                if edited: return False
                edited = True
                if len(a) > len(b): i += 1
                elif len(a) < len(b): j += 1
                else: i += 1; j += 1
            else:
                i += 1; j += 1
        return True

Sweet And Savory MEDIUM

Pick one negative (sweet) + one positive (savory) dish with sum closest to target without exceeding it. Two pointers. O(n log n).

class Solution:
    def sweet_and_savory(self, dishes, target):
        sweet = sorted(d for d in dishes if d < 0)
        savory = sorted(d for d in dishes if d > 0)
        best = [0, 0]; best_diff = float("inf")
        i, j = 0, len(savory) - 1
        while i < len(sweet) and j >= 0:
            total = sweet[i] + savory[j]
            if total > target:
                j -= 1
            else:
                if target - total < best_diff:
                    best_diff = target - total; best = [sweet[i], savory[j]]
                i += 1
        return best

Group Anagrams MEDIUM

Problem: Group the strings that are anagrams of one another. Return the groups as a list of lists.
def group_anagrams_strings(strs):
    from collections import defaultdict
    groups = defaultdict(list)
    for s in strs:
        key = ''.join(sorted(s))
        groups[key].append(s)
    return list(groups.values())

String Compression MEDIUM

Problem: Compress a string by replacing runs of the same character with the character followed by its count, e.g. 'aabcccccaaa' -> 'a2b1c5a3'.
def compress_string(s):
    if not s:
        return ''
    result = []
    count = 1
    for i in range(1, len(s)):
        if s[i] == s[i-1]:
            count += 1
        else:
            result.append(s[i-1] + str(count))
            count = 1
    result.append(s[-1] + str(count))
    return ''.join(result)

Count and Say MEDIUM

Problem: Generate the nth term of the count-and-say sequence, where each term describes the digit runs of the previous term.
def count_and_say(n):
    result = '1'
    for _ in range(n - 1):
        next_term = []
        i = 0
        while i < len(result):
            count = 1
            while i + 1 < len(result) and result[i] == result[i+1]:
                i += 1
                count += 1
            next_term.append(str(count) + result[i])
            i += 1
        result = ''.join(next_term)
    return result

Zigzag Conversion MEDIUM

Problem: Write the string in a zigzag pattern across the given number of rows, then read it off row by row.
def zigzag_convert(s, num_rows):
    if num_rows == 1 or num_rows >= len(s):
        return s
    rows = [''] * num_rows
    idx, step = 0, 1
    for c in s:
        rows[idx] += c
        if idx == 0:
            step = 1
        elif idx == num_rows - 1:
            step = -1
        idx += step
    return ''.join(rows)

Multiply Strings MEDIUM

Problem: Multiply two non-negative integers given as strings, without using built-in big-integer conversion of the whole number. Return the product as a string.
def multiply_strings(num1, num2):
    if num1 == '0' or num2 == '0':
        return '0'
    m, n = len(num1), len(num2)
    result = [0] * (m + n)
    for i in range(m - 1, -1, -1):
        for j in range(n - 1, -1, -1):
            mul = (ord(num1[i]) - 48) * (ord(num2[j]) - 48)
            p1, p2 = i + j, i + j + 1
            total = mul + result[p2]
            result[p2] = total % 10
            result[p1] += total // 10
    result_str = ''.join(map(str, result)).lstrip('0')
    return result_str or '0'

String to Integer (atoi) MEDIUM

Problem: Convert a string to a 32-bit signed integer following atoi rules: skip leading spaces, optional sign, read digits, and clamp to the 32-bit range.
def my_atoi(s):
    s = s.lstrip()
    if not s:
        return 0
    sign = 1
    i = 0
    if s[0] in '+-':
        sign = -1 if s[0] == '-' else 1
        i = 1
    num = 0
    while i < len(s) and s[i].isdigit():
        num = num * 10 + (ord(s[i]) - 48)
        i += 1
    num *= sign
    return max(-2**31, min(2**31 - 1, num))

Longest Substring Without Duplicates HARD

Sliding window + a map of last-seen positions. O(n).

class Solution:
    def longest_unique_substring(self, s):
        seen = {}; start = 0; best = [0, 1]
        for i, ch in enumerate(s):
            if ch in seen and seen[ch] >= start:
                start = seen[ch] + 1
            if i + 1 - start > best[1] - best[0]:
                best = [start, i + 1]
            seen[ch] = i
        return s[best[0]:best[1]]

Underscorify Substring HARD

Wrap every occurrence of a substring with underscores, merging overlaps. O(n+m).

class Solution:
    def underscorify_substring(self, string, substring):
        locs = []; start = 0
        while True:
            i = string.find(substring, start)
            if i == -1: break
            locs.append([i, i + len(substring)]); start = i + 1
        merged = []
        for loc in locs:
            if merged and loc[0] <= merged[-1][1]:
                merged[-1][1] = max(merged[-1][1], loc[1])
            else: merged.append(loc)
        out = []; m = 0
        for i in range(len(string) + 1):
            if m < len(merged) and i == merged[m][0]: out.append("_")
            if m < len(merged) and i == merged[m][1]: out.append("_"); m += 1
            if i < len(string): out.append(string[i])
        return "".join(out)

Multi String Search HARD

Which small strings appear inside the big string? Build a trie of the big string's suffixes.

class Solution:
    def multi_string_search(self, big, small):
        trie = {}
        for i in range(len(big)):                 # all suffixes
            node = trie
            for ch in big[i:]:
                node = node.setdefault(ch, {})
        def contains(word):
            node = trie
            for ch in word:
                if ch not in node: return False
                node = node[ch]
            return True
        return [contains(w) for w in small]

Regular Expression Matching HARD

Problem: Implement matching for '.' (any single char) and '*' (zero or more of the preceding element) covering the entire input string.
def is_match(s, p):
    from functools import lru_cache
    @lru_cache(None)
    def dp(i, j):
        if j == len(p):
            return i == len(s)
        first = i < len(s) and p[j] in (s[i], '.')
        if j + 1 < len(p) and p[j+1] == '*':
            return dp(i, j+2) or (first and dp(i+1, j))
        return first and dp(i+1, j+1)
    return dp(0, 0)

Wildcard Matching HARD

Problem: Implement matching for '?' (any single char) and '*' (any sequence including empty) covering the entire input string.
def wildcard_match(s, p):
    i = j = 0
    star = -1
    match = 0
    while i < len(s):
        if j < len(p) and p[j] in (s[i], '?'):
            i += 1
            j += 1
        elif j < len(p) and p[j] == '*':
            star = j
            match = i
            j += 1
        elif star != -1:
            j = star + 1
            match += 1
            i = match
        else:
            return False
    while j < len(p) and p[j] == '*':
        j += 1
    return j == len(p)

Edit Distance HARD

Problem: Return the minimum number of single-character insertions, deletions, or replacements needed to turn word1 into word2.
def edit_distance(word1, word2):
    m, n = len(word1), len(word2)
    dp = list(range(n + 1))
    for i in range(1, m + 1):
        prev = dp[0]
        dp[0] = i
        for j in range(1, n + 1):
            temp = dp[j]
            if word1[i-1] == word2[j-1]:
                dp[j] = prev
            else:
                dp[j] = 1 + min(prev, dp[j], dp[j-1])
            prev = temp
    return dp[n]

Text Justification HARD

Problem: Given words and a maximum line width, format the text so each line is fully justified (extra spaces distributed left-first); the last line is left-justified. Return the list of lines.
def full_justify_strings(words, max_width):
    result = []
    line = []
    length = 0
    for word in words:
        if length + len(line) + len(word) > max_width:
            spaces = max_width - length
            gaps = len(line) - 1
            if gaps == 0:
                result.append(line[0] + ' ' * spaces)
            else:
                for i in range(spaces):
                    line[i % gaps] += ' '
                result.append(''.join(line))
            line = []
            length = 0
        line.append(word)
        length += len(word)
    last = ' '.join(line)
    result.append(last + ' ' * (max_width - len(last)))
    return result

Integer to English Words HARD

Problem: Convert a non-negative integer to its English words representation, e.g. 1234567 -> 'One Million Two Hundred Thirty Four Thousand Five Hundred Sixty Seven'.
def number_to_words(num):
    if num == 0:
        return 'Zero'
    below_20 = ['','One','Two','Three','Four','Five','Six','Seven','Eight','Nine','Ten',
                'Eleven','Twelve','Thirteen','Fourteen','Fifteen','Sixteen','Seventeen','Eighteen','Nineteen']
    tens = ['','','Twenty','Thirty','Forty','Fifty','Sixty','Seventy','Eighty','Ninety']
    thousands = ['','Thousand','Million','Billion']
    def helper(n):
        if n == 0:
            return []
        if n < 20:
            return [below_20[n]]
        if n < 100:
            return [tens[n // 10]] + helper(n % 10)
        return [below_20[n // 100], 'Hundred'] + helper(n % 100)
    words = []
    group = 0
    while num > 0:
        if num % 1000 != 0:
            words = helper(num % 1000) + ([thousands[group]] if thousands[group] else []) + words
        num //= 1000
        group += 1
    return ' '.join(words)

Distinct Subsequences HARD

Problem: Return the number of distinct subsequences of s that equal t.
def num_distinct(s, t):
    dp = [0] * (len(t) + 1)
    dp[0] = 1
    for c in s:
        for j in range(len(t), 0, -1):
            if c == t[j-1]:
                dp[j] += dp[j-1]
    return dp[len(t)]

Shortest Palindrome HARD

Problem: Find the shortest palindrome you can make by adding characters only to the front of the string.
def shortest_palindrome(s):
    if not s:
        return s
    combined = s + '#' + s[::-1]
    lps = [0] * len(combined)
    for i in range(1, len(combined)):
        length = lps[i-1]
        while length > 0 and combined[i] != combined[length]:
            length = lps[length-1]
        if combined[i] == combined[length]:
            length += 1
        lps[i] = length
    return s[lps[-1]:][::-1] + s

Palindrome Partitioning II (Min Cuts) HARD

Problem: Return the minimum number of cuts needed to partition the string so that every part is a palindrome.
def min_cut(s):
    n = len(s)
    is_pal = [[False] * n for _ in range(n)]
    cuts = [0] * n
    for i in range(n):
        min_cuts = i
        for j in range(i + 1):
            if s[j] == s[i] and (i - j < 2 or is_pal[j+1][i-1]):
                is_pal[j][i] = True
                min_cuts = 0 if j == 0 else min(min_cuts, cuts[j-1] + 1)
        cuts[i] = min_cuts
    return cuts[n-1]

Searching & Sorting

Binary Search EASY

Problem: find a target's index in a sorted array.

Pattern: halve the search range each step. O(log n).

def binary_search(arr, target):
    low, high = 0, len(arr) - 1
    while low <= high:
        mid = (low + high) // 2
        if arr[mid] == target:
            return mid
        elif arr[mid] < target:
            low = mid + 1
        else:
            high = mid - 1
    return -1

Binary Search EASY

Problem: Return the index of target in a sorted array, or -1 if it is not present.
def binary_search_searchsort(nums, target):
    lo, hi = 0, len(nums) - 1
    while lo <= hi:
        mid = lo + (hi - lo) // 2
        if nums[mid] == target:
            return mid
        if nums[mid] < target:
            lo = mid + 1
        else:
            hi = mid - 1
    return -1

Bubble Sort EASY

Problem: Sort an array ascending using bubble sort and return it.
def bubble_sort_searchsort(nums):
    a = list(nums)
    for i in range(len(a) - 1):
        swapped = False
        for j in range(len(a) - 1 - i):
            if a[j] > a[j+1]:
                a[j], a[j+1] = a[j+1], a[j]
                swapped = True
        if not swapped:
            break
    return a

Selection Sort EASY

Problem: Sort an array ascending using selection sort and return it.
def selection_sort(nums):
    a = list(nums)
    for i in range(len(a)):
        min_idx = i
        for j in range(i + 1, len(a)):
            if a[j] < a[min_idx]:
                min_idx = j
        a[i], a[min_idx] = a[min_idx], a[i]
    return a

Insertion Sort EASY

Problem: Sort an array ascending using insertion sort and return it.
def insertion_sort(nums):
    a = list(nums)
    for i in range(1, len(a)):
        key = a[i]
        j = i - 1
        while j >= 0 and a[j] > key:
            a[j+1] = a[j]
            j -= 1
        a[j+1] = key
    return a

First Occurrence in a Sorted Array EASY

Problem: Return the index of the first occurrence of target in a sorted array with duplicates, or -1.
def first_occurrence(nums, target):
    lo, hi = 0, len(nums) - 1
    result = -1
    while lo <= hi:
        mid = (lo + hi) // 2
        if nums[mid] == target:
            result = mid
            hi = mid - 1
        elif nums[mid] < target:
            lo = mid + 1
        else:
            hi = mid - 1
    return result

Last Occurrence in a Sorted Array EASY

Problem: Return the index of the last occurrence of target in a sorted array with duplicates, or -1.
def last_occurrence(nums, target):
    lo, hi = 0, len(nums) - 1
    result = -1
    while lo <= hi:
        mid = (lo + hi) // 2
        if nums[mid] == target:
            result = mid
            lo = mid + 1
        elif nums[mid] < target:
            lo = mid + 1
        else:
            hi = mid - 1
    return result

Search Insert Position EASY

Problem: Return the index where target is found in a sorted array, or the index where it would be inserted to keep it sorted.
def search_insert_searchsort(nums, target):
    lo, hi = 0, len(nums)
    while lo < hi:
        mid = (lo + hi) // 2
        if nums[mid] < target:
            lo = mid + 1
        else:
            hi = mid
    return lo

Is the Array Sorted EASY

Problem: Return True if the array is sorted in non-decreasing order.
def is_sorted(nums):
    return all(nums[i] <= nums[i+1] for i in range(len(nums) - 1))

Count Occurrences in a Sorted Array EASY

Problem: Return how many times target appears in a sorted array, using binary search for the boundaries.
def count_occurrences_searchsort(nums, target):
    import bisect
    return bisect.bisect_right(nums, target) - bisect.bisect_left(nums, target)

Integer Square Root EASY

Problem: Return the floor of the square root of a non-negative integer x, using binary search.
def int_sqrt(x):
    if x < 2:
        return x
    lo, hi = 1, x
    while lo <= hi:
        mid = (lo + hi) // 2
        if mid * mid <= x:
            lo = mid + 1
        else:
            hi = mid - 1
    return hi

Search For Range MEDIUM

First and last index of a target in a sorted array. Two binary searches. O(log n).

class Solution:
    def search_range(self, arr, target):
        def bound(left):
            lo, hi, res = 0, len(arr) - 1, -1
            while lo <= hi:
                mid = (lo + hi) // 2
                if arr[mid] == target:
                    res = mid
                    if left: hi = mid - 1
                    else:    lo = mid + 1
                elif arr[mid] < target: lo = mid + 1
                else: hi = mid - 1
            return res
        return [bound(True), bound(False)]

Search In Sorted Matrix MEDIUM

Rows & columns both sorted. Start top-right, move left/down. O(n+m).

class Solution:
    def search_matrix(self, matrix, target):
        row, col = 0, len(matrix[0]) - 1
        while row < len(matrix) and col >= 0:
            if matrix[row][col] == target: return [row, col]
            elif matrix[row][col] > target: col -= 1
            else: row += 1
        return [-1, -1]

Bubble / Insertion / Selection (O(n²))

class Solution:
    def bubble_sort(self, a):
        for i in range(len(a)):
            for j in range(len(a) - 1 - i):
                if a[j] > a[j+1]: a[j], a[j+1] = a[j+1], a[j]
        return a

    def insertion_sort(self, a):
        for i in range(1, len(a)):
            j = i
            while j > 0 and a[j] < a[j-1]:
                a[j], a[j-1] = a[j-1], a[j]; j -= 1
        return a

    def selection_sort(self, a):
        for i in range(len(a)):
            m = i
            for j in range(i+1, len(a)):
                if a[j] < a[m]: m = j
            a[i], a[m] = a[m], a[i]
        return a

Quick Sort & Merge Sort (O(n log n))

class Solution:
    def quick_sort(self, a):
        if len(a) <= 1: return a
        pivot = a[len(a)//2]
        left  = [x for x in a if x < pivot]
        mid   = [x for x in a if x == pivot]
        right = [x for x in a if x > pivot]
        return self.quick_sort(left) + mid + self.quick_sort(right)

    def merge_sort(self, a):
        if len(a) <= 1: return a
        m = len(a) // 2
        L, R = self.merge_sort(a[:m]), self.merge_sort(a[m:])
        res = []; i = j = 0
        while i < len(L) and j < len(R):
            if L[i] <= R[j]: res.append(L[i]); i += 1
            else: res.append(R[j]); j += 1
        return res + L[i:] + R[j:]

Search in Rotated Sorted Array MEDIUM

Problem: A sorted array was rotated at an unknown pivot. Return the index of target, or -1, in O(log n).
def search_rotated_searchsort(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]:
            if nums[lo] <= target < nums[mid]:
                hi = mid - 1
            else:
                lo = mid + 1
        else:
            if nums[mid] < target <= nums[hi]:
                lo = mid + 1
            else:
                hi = mid - 1
    return -1

Find Minimum in Rotated Sorted Array MEDIUM

Problem: Return the minimum element of a rotated sorted array with distinct values, in O(log n).
def find_min_rotated(nums):
    lo, hi = 0, len(nums) - 1
    while lo < hi:
        mid = (lo + hi) // 2
        if nums[mid] > nums[hi]:
            lo = mid + 1
        else:
            hi = mid
    return nums[lo]

Find Peak Element MEDIUM

Problem: A peak is an element strictly greater than its neighbors. Return the index of any peak in O(log n).
def find_peak(nums):
    lo, hi = 0, len(nums) - 1
    while lo < hi:
        mid = (lo + hi) // 2
        if nums[mid] < nums[mid + 1]:
            lo = mid + 1
        else:
            hi = mid
    return lo

Merge Sort MEDIUM

Problem: Sort an array ascending using recursive merge sort and return it.
def merge_sort(nums):
    if len(nums) <= 1:
        return list(nums)
    mid = len(nums) // 2
    left = merge_sort(nums[:mid])
    right = merge_sort(nums[mid:])
    merged = []
    i = j = 0
    while i < len(left) and j < len(right):
        if left[i] <= right[j]:
            merged.append(left[i]); i += 1
        else:
            merged.append(right[j]); j += 1
    merged.extend(left[i:])
    merged.extend(right[j:])
    return merged

Quicksort MEDIUM

Problem: Sort an array ascending using quicksort and return it.
def quick_sort_searchsort(nums):
    a = list(nums)
    def sort(lo, hi):
        if lo >= hi:
            return
        pivot = a[hi]
        i = lo
        for j in range(lo, hi):
            if a[j] < pivot:
                a[i], a[j] = a[j], a[i]
                i += 1
        a[i], a[hi] = a[hi], a[i]
        sort(lo, i - 1)
        sort(i + 1, hi)
    sort(0, len(a) - 1)
    return a

Kth Largest via Quickselect MEDIUM

Problem: Return the kth largest element using the quickselect partitioning method (average O(n)).
def find_kth_largest_searchsort(nums, k):
    import random
    target = len(nums) - k
    a = list(nums)
    lo, hi = 0, len(a) - 1
    while lo <= hi:
        pivot = a[random.randint(lo, hi)]
        left, mid, right = [], [], []
        for x in a[lo:hi+1]:
            if x < pivot:
                left.append(x)
            elif x > pivot:
                right.append(x)
            else:
                mid.append(x)
        a[lo:hi+1] = left + mid + right
        if target < lo + len(left):
            hi = lo + len(left) - 1
        elif target >= lo + len(left) + len(mid):
            lo = lo + len(left) + len(mid)
        else:
            return a[target]
    return a[target]

Sort Colors (Dutch National Flag) MEDIUM

Problem: Sort an array of 0s, 1s, and 2s in place in one pass, then return it.
def sort_colors(nums):
    a = list(nums)
    lo, mid, hi = 0, 0, len(a) - 1
    while mid <= hi:
        if a[mid] == 0:
            a[lo], a[mid] = a[mid], a[lo]
            lo += 1; mid += 1
        elif a[mid] == 1:
            mid += 1
        else:
            a[mid], a[hi] = a[hi], a[mid]
            hi -= 1
    return a

First Bad Version MEDIUM

Problem: Versions 1..n exist; from some version on, all are bad. Given the first bad version, return it using the fewest checks (binary search).
def first_bad_version(n, first_bad):
    def is_bad(v):
        return v >= first_bad
    lo, hi = 1, n
    while lo < hi:
        mid = (lo + hi) // 2
        if is_bad(mid):
            hi = mid
        else:
            lo = mid + 1
    return lo

Search a 2D Matrix MEDIUM

Problem: In a matrix where each row is sorted and each row's first value exceeds the previous row's last, return True if target is present (treat it as one sorted list).
def search_matrix_searchsort(matrix, target):
    if not matrix or not matrix[0]:
        return False
    rows, cols = len(matrix), len(matrix[0])
    lo, hi = 0, rows * cols - 1
    while lo <= hi:
        mid = (lo + hi) // 2
        val = matrix[mid // cols][mid % cols]
        if val == target:
            return True
        if val < target:
            lo = mid + 1
        else:
            hi = mid - 1
    return False

Shifted Binary Search HARD

Binary search in a rotated sorted array. Decide which half is sorted each step. O(log n).

class Solution:
    def shifted_binary_search(self, arr, target):
        lo, hi = 0, len(arr) - 1
        while lo <= hi:
            mid = (lo + hi) // 2
            if arr[mid] == target: return mid
            if arr[lo] <= arr[mid]:                  # left half sorted
                if arr[lo] <= target < arr[mid]: hi = mid - 1
                else: lo = mid + 1
            else:                                    # right half sorted
                if arr[mid] < target <= arr[hi]: lo = mid + 1
                else: hi = mid - 1
        return -1

Quickselect - kth smallest HARD

Like quicksort but only recurse into the side with the answer. Average O(n).

class Solution:
    def quickselect(self, arr, k):           # k is 1-indexed position
        lo, hi = 0, len(arr) - 1
        while True:
            pivot = arr[hi]; p = lo
            for i in range(lo, hi):
                if arr[i] < pivot:
                    arr[i], arr[p] = arr[p], arr[i]; p += 1
            arr[p], arr[hi] = arr[hi], arr[p]
            if p == k - 1: return arr[p]
            elif p < k - 1: lo = p + 1
            else: hi = p - 1

Radix Sort HARD

Non-comparison sort for non-negative ints - bucket by each digit. O(d·n).

class Solution:
    def radix_sort(self, arr):
        if not arr: return arr
        max_val = max(arr); exp = 1
        while max_val // exp > 0:
            buckets = [[] for _ in range(10)]
            for num in arr:
                buckets[(num // exp) % 10].append(num)
            arr = [num for bucket in buckets for num in bucket]
            exp *= 10
        return arr

Count Inversions HARD

How many pairs are out of order? Piggyback on merge sort. O(n log n).

class Solution:
    def count_inversions(self, arr):
        def sort_count(a):
            if len(a) <= 1: return a, 0
            mid = len(a) // 2
            left, lc = sort_count(a[:mid])
            right, rc = sort_count(a[mid:])
            merged = []; i = j = inv = 0
            while i < len(left) and j < len(right):
                if left[i] <= right[j]:
                    merged.append(left[i]); i += 1
                else:
                    merged.append(right[j]); j += 1
                    inv += len(left) - i        # rest of left are all inversions
            merged += left[i:] + right[j:]
            return merged, lc + rc + inv
        return sort_count(arr)[1]

Median of Two Sorted Arrays HARD

Problem: Return the median of two sorted arrays in O(log(min(m,n))) using a binary-search partition.
def find_median_sorted_arrays_searchsort(a, b):
    if len(a) > len(b):
        a, b = b, a
    m, n = len(a), len(b)
    lo, hi, half = 0, m, (m + n + 1) // 2
    while lo <= hi:
        i = (lo + hi) // 2
        j = half - i
        a_left = a[i-1] if i > 0 else float('-inf')
        a_right = a[i] if i < m else float('inf')
        b_left = b[j-1] if j > 0 else float('-inf')
        b_right = b[j] if j < n else float('inf')
        if a_left <= b_right and b_left <= a_right:
            if (m + n) % 2:
                return float(max(a_left, b_left))
            return (max(a_left, b_left) + min(a_right, b_right)) / 2
        elif a_left > b_right:
            hi = i - 1
        else:
            lo = i + 1
    return 0.0

Search a 2D Matrix II HARD

Problem: In a matrix sorted ascending along both rows and columns, return True if target is present, in O(m+n).
def search_matrix_ii(matrix, target):
    if not matrix or not matrix[0]:
        return False
    row, col = 0, len(matrix[0]) - 1
    while row < len(matrix) and col >= 0:
        val = matrix[row][col]
        if val == target:
            return True
        if val > target:
            col -= 1
        else:
            row += 1
    return False

Find K-th Smallest Pair Distance HARD

Problem: Return the kth smallest absolute difference among all pairs, using binary search on the distance plus a sliding window count.
def smallest_distance_pair(nums, k):
    nums.sort()
    lo, hi = 0, nums[-1] - nums[0]
    while lo < hi:
        mid = (lo + hi) // 2
        count = 0
        left = 0
        for right in range(len(nums)):
            while nums[right] - nums[left] > mid:
                left += 1
            count += right - left
        if count >= k:
            hi = mid
        else:
            lo = mid + 1
    return lo

Split Array Largest Sum HARD

Problem: Split the array into k non-empty contiguous parts to minimize the largest part sum. Return that minimized value (binary search on the answer).
def split_array(nums, k):
    def can_split(limit):
        pieces = 1
        total = 0
        for n in nums:
            if total + n > limit:
                pieces += 1
                total = n
            else:
                total += n
        return pieces <= k
    lo, hi = max(nums), sum(nums)
    while lo < hi:
        mid = (lo + hi) // 2
        if can_split(mid):
            hi = mid
        else:
            lo = mid + 1
    return lo

Count of Smaller Numbers After Self HARD

Problem: For each element, count how many numbers to its right are smaller. Return the list of counts (merge-sort based).
def count_smaller(nums):
    counts = [0] * len(nums)
    indexed = list(enumerate(nums))
    def sort(arr):
        if len(arr) <= 1:
            return arr
        mid = len(arr) // 2
        left = sort(arr[:mid])
        right = sort(arr[mid:])
        merged = []
        i = j = 0
        while i < len(left) or j < len(right):
            if j >= len(right) or (i < len(left) and left[i][1] <= right[j][1]):
                counts[left[i][0]] += j
                merged.append(left[i]); i += 1
            else:
                merged.append(right[j]); j += 1
        return merged
    sort(indexed)
    return counts

Koko Eating Bananas HARD

Problem: Koko eats at k bananas/hour, finishing one pile per hour (leftovers count as a full hour). Return the minimum integer k to eat all piles within h hours.
def min_eating_speed(piles, h):
    import math
    lo, hi = 1, max(piles)
    while lo < hi:
        mid = (lo + hi) // 2
        hours = sum(math.ceil(p / mid) for p in piles)
        if hours <= h:
            hi = mid
        else:
            lo = mid + 1
    return lo

Capacity to Ship Packages in D Days HARD

Problem: Packages must ship in order within d days. Return the least ship capacity (per day) that gets everything shipped in time (binary search on capacity).
def ship_within_days(weights, days):
    def needed(cap):
        d = 1
        total = 0
        for w in weights:
            if total + w > cap:
                d += 1
                total = 0
            total += w
        return d
    lo, hi = max(weights), sum(weights)
    while lo < hi:
        mid = (lo + hi) // 2
        if needed(mid) <= days:
            hi = mid
        else:
            lo = mid + 1
    return lo

Sliding Window

Maximum Sum Subarray of Size K EASY

Problem: Given an array of integers and a number k, find the maximum sum of any contiguous subarray of size k.
def max_sum_subarray_k(nums, k):
    window = sum(nums[:k])
    best = window
    for i in range(k, len(nums)):
        window += nums[i] - nums[i - k]
        best = max(best, window)
    return best

Minimum Sum Subarray of Size K EASY

Problem: Find the minimum sum of any contiguous subarray of size k.
def min_sum_subarray_k(nums, k):
    window = sum(nums[:k])
    best = window
    for i in range(k, len(nums)):
        window += nums[i] - nums[i - k]
        best = min(best, window)
    return best

Averages of All Subarrays of Size K EASY

Problem: Return a list of the averages of every contiguous subarray of size k.
def average_of_subarrays_k(nums, k):
    out, window = [], sum(nums[:k])
    out.append(window / k)
    for i in range(k, len(nums)):
        window += nums[i] - nums[i - k]
        out.append(window / k)
    return out

Maximum Vowels in a Substring of Length K EASY

Problem: Given a string s and integer k, return the maximum number of vowels in any substring of length k.
def max_vowels_in_window(s, k):
    vowels = set("aeiou")
    count = sum(1 for c in s[:k] if c in vowels)
    best = count
    for i in range(k, len(s)):
        count += (s[i] in vowels) - (s[i - k] in vowels)
        best = max(best, count)
    return best

Contains Duplicate Within Distance K EASY

Problem: Return True if there are two equal values in the array whose indices differ by at most k.
def contains_nearby_duplicate_window(nums, k):
    window = set()
    for i, n in enumerate(nums):
        if n in window:
            return True
        window.add(n)
        if len(window) > k:
            window.discard(nums[i - k])
    return False

First Negative in Every Window of Size K EASY

Problem: For every contiguous window of size k, report the first negative number (or 0 if the window has none).
def first_negative_each_window(nums, k):
    from collections import deque
    neg, out = deque(), []
    for i, n in enumerate(nums):
        if n < 0:
            neg.append(i)
        if i >= k - 1:
            while neg and neg[0] <= i - k:
                neg.popleft()
            out.append(nums[neg[0]] if neg else 0)
    return out

Count Windows of Size K With Sum At Least Target EASY

Problem: Count how many contiguous subarrays of size k have a sum greater than or equal to target.
def count_windows_sum_at_least(nums, k, target):
    window = sum(nums[:k])
    count = 1 if window >= target else 0
    for i in range(k, len(nums)):
        window += nums[i] - nums[i - k]
        if window >= target:
            count += 1
    return count

Maximum Ones in a Window of Size K EASY

Problem: Given a binary array, return the greatest number of 1s contained in any window of size k.
def max_ones_in_window(nums, k):
    ones = sum(nums[:k])
    best = ones
    for i in range(k, len(nums)):
        ones += nums[i] - nums[i - k]
        best = max(best, ones)
    return best

Start Index of the Best Window of Size K EASY

Problem: Return the starting index of the size-k window with the largest sum (smallest index on ties).
def best_window_start_index(nums, k):
    window = sum(nums[:k])
    best, best_i = window, 0
    for i in range(k, len(nums)):
        window += nums[i] - nums[i - k]
        if window > best:
            best, best_i = window, i - k + 1
    return best_i

Does a Window of Size K Sum to Target EASY

Problem: Return True if some contiguous subarray of size k sums to exactly target.
def window_exists_with_sum(nums, k, target):
    window = sum(nums[:k])
    if window == target:
        return True
    for i in range(k, len(nums)):
        window += nums[i] - nums[i - k]
        if window == target:
            return True
    return False

Sliding Window - max subarray sum MEDIUM

Problem: the largest sum of any k consecutive numbers.

Pattern: keep a running window sum; slide it instead of recomputing. O(n).

def max_subarray_sum(nums, k):
    window = sum(nums[:k])
    best = window
    for i in range(k, len(nums)):
        window += nums[i] - nums[i - k]   # add new, drop old
        best = max(best, window)
    return best

Longest Substring Without Repeating Characters MEDIUM

Problem: Return the length of the longest substring of s that has no repeating characters.
def length_of_longest_substring_window(s):
    seen, left, best = {}, 0, 0
    for right, c in enumerate(s):
        if c in seen and seen[c] >= left:
            left = seen[c] + 1
        seen[c] = right
        best = max(best, right - left + 1)
    return best

Longest Substring With At Most K Distinct Characters MEDIUM

Problem: Return the length of the longest substring that contains at most k distinct characters.
def longest_substring_k_distinct(s, k):
    from collections import defaultdict
    count, left, best = defaultdict(int), 0, 0
    for right, c in enumerate(s):
        count[c] += 1
        while len(count) > k:
            count[s[left]] -= 1
            if count[s[left]] == 0:
                del count[s[left]]
            left += 1
        best = max(best, right - left + 1)
    return best

Minimum Size Subarray Sum MEDIUM

Problem: Return the minimal length of a contiguous subarray whose sum is >= target, or 0 if none exists.
def min_subarray_len_window(target, nums):
    left, total, best = 0, 0, float('inf')
    for right, n in enumerate(nums):
        total += n
        while total >= target:
            best = min(best, right - left + 1)
            total -= nums[left]
            left += 1
    return 0 if best == float('inf') else best

Max Consecutive Ones After Flipping K Zeros MEDIUM

Problem: Given a binary array, return the longest run of 1s you can get by flipping at most k zeros.
def longest_ones(nums, k):
    left, zeros, best = 0, 0, 0
    for right, n in enumerate(nums):
        if n == 0:
            zeros += 1
        while zeros > k:
            if nums[left] == 0:
                zeros -= 1
            left += 1
        best = max(best, right - left + 1)
    return best

Longest Repeating Character Replacement MEDIUM

Problem: You may replace at most k characters. Return the length of the longest substring of one repeating letter you can form.
def character_replacement(s, k):
    from collections import defaultdict
    count, left, maxf, best = defaultdict(int), 0, 0, 0
    for right, c in enumerate(s):
        count[c] += 1
        maxf = max(maxf, count[c])
        while (right - left + 1) - maxf > k:
            count[s[left]] -= 1
            left += 1
        best = max(best, right - left + 1)
    return best

Permutation in String MEDIUM

Problem: Return True if s2 contains a permutation of s1 as a contiguous substring.
def check_inclusion(s1, s2):
    from collections import Counter
    if len(s1) > len(s2):
        return False
    need = Counter(s1)
    window = Counter(s2[:len(s1)])
    if window == need:
        return True
    for i in range(len(s1), len(s2)):
        window[s2[i]] += 1
        window[s2[i - len(s1)]] -= 1
        if window[s2[i - len(s1)]] == 0:
            del window[s2[i - len(s1)]]
        if window == need:
            return True
    return False

Find All Anagrams in a String MEDIUM

Problem: Return the start indices of every substring of s that is an anagram of p.
def find_anagrams(s, p):
    from collections import Counter
    if len(p) > len(s):
        return []
    need, window, out = Counter(p), Counter(s[:len(p)]), []
    if window == need:
        out.append(0)
    for i in range(len(p), len(s)):
        window[s[i]] += 1
        window[s[i - len(p)]] -= 1
        if window[s[i - len(p)]] == 0:
            del window[s[i - len(p)]]
        if window == need:
            out.append(i - len(p) + 1)
    return out

Fruit Into Baskets MEDIUM

Problem: You can carry at most two types of fruit. Return the most fruit you can pick from a contiguous stretch of trees.
def total_fruit(fruits):
    from collections import defaultdict
    count, left, best = defaultdict(int), 0, 0
    for right, f in enumerate(fruits):
        count[f] += 1
        while len(count) > 2:
            count[fruits[left]] -= 1
            if count[fruits[left]] == 0:
                del count[fruits[left]]
            left += 1
        best = max(best, right - left + 1)
    return best

Subarray Product Less Than K MEDIUM

Problem: Count the contiguous subarrays whose product of elements is strictly less than k.
def num_subarray_product_less_than_k(nums, k):
    if k <= 1:
        return 0
    left, prod, count = 0, 1, 0
    for right, n in enumerate(nums):
        prod *= n
        while prod >= k:
            prod //= nums[left]
            left += 1
        count += right - left + 1
    return count

Maximum Frequency After K Increments MEDIUM

Problem: You may add 1 to an element up to k times total. Return the highest frequency of any single value you can reach.
def max_frequency(nums, k):
    nums.sort()
    left, total, best = 0, 0, 1
    for right in range(len(nums)):
        total += nums[right]
        while nums[right] * (right - left + 1) - total > k:
            total -= nums[left]
            left += 1
        best = max(best, right - left + 1)
    return best

Smallest Substring Containing HARD

Smallest window of big that contains every char of small (minimum window substring). O(n).

from collections import Counter

class Solution:
    def smallest_substring(self, big, small):
        need = Counter(small); missing = len(small)
        left = 0; best = ""
        for right, ch in enumerate(big):
            if need[ch] > 0: missing -= 1
            need[ch] -= 1
            while missing == 0:                     # window has everything
                if not best or right - left + 1 < len(best):
                    best = big[left:right + 1]
                need[big[left]] += 1
                if need[big[left]] > 0: missing += 1
                left += 1
        return best

Minimum Window Substring HARD

Problem: Return the smallest substring of s that contains every character of t (with multiplicity), or '' if none.
def min_window_window(s, t):
    from collections import Counter
    if not t or not s:
        return ""
    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]

Sliding Window Maximum HARD

Problem: Return a list of the maximum of every contiguous window of size k, using a monotonic deque in O(n).
def max_sliding_window(nums, k):
    from collections import deque
    dq, out = deque(), []
    for i, n in enumerate(nums):
        while dq and nums[dq[-1]] <= n:
            dq.pop()
        dq.append(i)
        if dq[0] <= i - k:
            dq.popleft()
        if i >= k - 1:
            out.append(nums[dq[0]])
    return out

Subarrays With K Different Integers HARD

Problem: Count contiguous subarrays that contain exactly k distinct integers (atMost(k) - atMost(k-1) trick).
def subarrays_with_k_distinct(nums, k):
    from collections import defaultdict
    def at_most(m):
        count, left, total = defaultdict(int), 0, 0
        for right, n in enumerate(nums):
            count[n] += 1
            while len(count) > m:
                count[nums[left]] -= 1
                if count[nums[left]] == 0:
                    del count[nums[left]]
                left += 1
            total += right - left + 1
        return total
    return at_most(k) - at_most(k - 1)

Shortest Subarray With Sum At Least K HARD

Problem: With possibly-negative numbers, return the length of the shortest subarray with sum >= k (or -1). Uses prefix sums + a monotonic deque.
def shortest_subarray_at_least_k(nums, k):
    from collections import deque
    n = len(nums)
    prefix = [0] * (n + 1)
    for i, x in enumerate(nums):
        prefix[i + 1] = prefix[i] + x
    dq, best = deque(), n + 1
    for i, cur in enumerate(prefix):
        while dq and cur - prefix[dq[0]] >= k:
            best = min(best, i - dq.popleft())
        while dq and prefix[dq[-1]] >= cur:
            dq.pop()
        dq.append(i)
    return best if best <= n else -1

Longest Subarray With Absolute Diff Within Limit HARD

Problem: Return the length of the longest subarray where the difference between its max and min is at most limit (two monotonic deques).
def longest_subarray_limit(nums, limit):
    from collections import deque
    maxd, mind, left, best = deque(), deque(), 0, 0
    for right, n in enumerate(nums):
        while maxd and nums[maxd[-1]] <= n:
            maxd.pop()
        while mind and nums[mind[-1]] >= n:
            mind.pop()
        maxd.append(right)
        mind.append(right)
        while nums[maxd[0]] - nums[mind[0]] > limit:
            left += 1
            if maxd[0] < left:
                maxd.popleft()
            if mind[0] < left:
                mind.popleft()
        best = max(best, right - left + 1)
    return best

Binary Subarrays With Sum HARD

Problem: In a 0/1 array, count the contiguous subarrays whose sum equals goal (atMost(goal) - atMost(goal-1)).
def num_subarrays_with_sum(nums, goal):
    def at_most(s):
        if s < 0:
            return 0
        left, total, count = 0, 0, 0
        for right, n in enumerate(nums):
            total += n
            while total > s:
                total -= nums[left]
                left += 1
            count += right - left + 1
        return count
    return at_most(goal) - at_most(goal - 1)

Count Nice Subarrays (Exactly K Odd Numbers) HARD

Problem: Count contiguous subarrays containing exactly k odd numbers (atMost trick on odd counts).
def count_nice_subarrays(nums, k):
    def at_most(m):
        if m < 0:
            return 0
        left, odd, count = 0, 0, 0
        for right, n in enumerate(nums):
            odd += n % 2
            while odd > m:
                odd -= nums[left] % 2
                left += 1
            count += right - left + 1
        return count
    return at_most(k) - at_most(k - 1)

Maximum Points From Cards HARD

Problem: Take exactly k cards from either end of the row; return the maximum total score (minimize the untaken middle window).
def max_score_cards(cards, k):
    n = len(cards)
    total = sum(cards)
    win = n - k
    if win == 0:
        return total
    cur = sum(cards[:win])
    min_mid = cur
    for i in range(win, n):
        cur += cards[i] - cards[i - win]
        min_mid = min(min_mid, cur)
    return total - min_mid

Minimum Operations to Reduce X to Zero HARD

Problem: Remove elements from either end so their sum equals x; return the fewest removals (found as the longest middle subarray summing to total - x).
def min_operations(nums, x):
    target = sum(nums) - x
    if target < 0:
        return -1
    if target == 0:
        return len(nums)
    left, total, best = 0, 0, -1
    for right, n in enumerate(nums):
        total += n
        while total > target and left <= right:
            total -= nums[left]
            left += 1
        if total == target:
            best = max(best, right - left + 1)
    return len(nums) - best if best != -1 else -1

Max Consecutive Ones After Deleting One Element HARD

Problem: Given a binary array, you must delete exactly one element; return the longest run of 1s in the result.
def longest_subarray_ones(nums):
    left, zeros, best = 0, 0, 0
    for right, n in enumerate(nums):
        if n == 0:
            zeros += 1
        while zeros > 1:
            if nums[left] == 0:
                zeros -= 1
            left += 1
        best = max(best, right - left)
    return best

Linked Lists

Merge Two Sorted Lists EASY

class Solution:
    def merge_two_lists(self, l1, l2):
        dummy = Node(0); tail = dummy
        while l1 and l2:
            if l1.value <= l2.value: tail.next = l1; l1 = l1.next
            else: tail.next = l2; l2 = l2.next
            tail = tail.next
        tail.next = l1 or l2
        return dummy.next

Middle Node EASY

class Solution:
    def middle_node(self, head):
        slow = fast = head
        while fast and fast.next:
            slow = slow.next; fast = fast.next.next
        return slow

Remove Duplicates From Linked List EASY

(sorted list - drop consecutive equal nodes). O(n).

class Solution:
    def dedup_sorted_list(self, head):
        node = head
        while node:
            while node.next and node.next.value == node.value:
                node.next = node.next.next
            node = node.next
        return head

Reverse a Linked List EASY

Problem: Reverse a singly linked list and return the new head.
def reverse_linked_list(head):
    prev = None
    while head:
        nxt = head.next
        head.next = prev
        prev = head
        head = nxt
    return prev

Middle of the Linked List EASY

Problem: Return the value at the middle node. For an even-length list, return the second of the two middle nodes.
def middle_value(head):
    slow = fast = head
    while fast and fast.next:
        slow = slow.next
        fast = fast.next.next
    return slow.val

Length of a Linked List EASY

Problem: Return the number of nodes in the linked list.
def list_length(head):
    n = 0
    while head:
        n += 1
        head = head.next
    return n

Sum of a Linked List EASY

Problem: Return the sum of all node values in the linked list.
def sum_list(head):
    total = 0
    while head:
        total += head.val
        head = head.next
    return total

Maximum Value in a Linked List EASY

Problem: Return the largest value stored in the linked list.
def max_list(head):
    best = head.val
    while head:
        best = max(best, head.val)
        head = head.next
    return best

Nth Node From the End EASY

Problem: Return the value of the nth node counting from the end (n=1 is the last node).
def nth_from_end_linked(head, n):
    fast = head
    for _ in range(n):
        fast = fast.next
    slow = head
    while fast:
        fast = fast.next
        slow = slow.next
    return slow.val

Delete All Nodes With a Value EASY

Problem: Remove every node whose value equals target and return the head of the resulting list.
def delete_value(head, target):
    dummy = _L(0, head)
    cur = dummy
    while cur.next:
        if cur.next.val == target:
            cur.next = cur.next.next
        else:
            cur = cur.next
    return dummy.next

Count Occurrences in a Linked List EASY

Problem: Return how many nodes hold the given value.
def count_occurrences(head, target):
    count = 0
    while head:
        if head.val == target:
            count += 1
        head = head.next
    return count

Search a Linked List EASY

Problem: Return True if the value appears anywhere in the linked list.
def search_list(head, target):
    while head:
        if head.val == target:
            return True
        head = head.next
    return False

Remove Duplicates From a Sorted List EASY

Problem: Given a sorted linked list, remove duplicate values so each appears once. Return the head.
def remove_duplicates_sorted(head):
    cur = head
    while cur and cur.next:
        if cur.next.val == cur.val:
            cur.next = cur.next.next
        else:
            cur = cur.next
    return head

Reverse a Linked List MEDIUM

Problem: reverse the direction of a singly linked list.

Pattern: walk the list, flipping each next pointer. O(n) time, O(1) space.

def reverse_list(head):
    prev = None
    while head:
        nxt = head.next      # save the next node
        head.next = prev     # flip the pointer
        prev = head          # move prev forward
        head = nxt           # move head forward
    return prev               # new head

Remove Kth Node From End MEDIUM

Two pointers k apart; when the fast one ends, slow is at the node before the target. O(n).

class Solution:
    def remove_kth_from_end(self, head, k):
        fast = slow = head
        for _ in range(k): fast = fast.next
        if fast is None: return head.next      # removing the head
        while fast.next:
            fast = fast.next; slow = slow.next
        slow.next = slow.next.next
        return head

Sum of Linked Lists MEDIUM

Two numbers stored as digit lists (ones digit first) - add them. O(n).

class Solution:
    def sum_lists(self, l1, l2):
        dummy = Node(0); cur = dummy; carry = 0
        while l1 or l2 or carry:
            total = carry
            if l1: total += l1.value; l1 = l1.next
            if l2: total += l2.value; l2 = l2.next
            carry, digit = divmod(total, 10)
            cur.next = Node(digit); cur = cur.next
        return dummy.next

Node Swap (swap adjacent pairs) MEDIUM

class Solution:
    def swap_pairs(self, head):
        dummy = Node(0); dummy.next = head; prev = dummy
        while prev.next and prev.next.next:
            a, b = prev.next, prev.next.next
            a.next = b.next; b.next = a; prev.next = b
            prev = a
        return dummy.next

Find Loop - Floyd's cycle detection MEDIUM

Detect where a linked list loops back. Slow/fast pointers, then reset one to the head. O(n), O(1).

class Solution:
    def find_loop(self, head):
        slow = head.next; fast = head.next.next
        while slow != fast:
            slow = slow.next; fast = fast.next.next
        slow = head
        while slow != fast:
            slow = slow.next; fast = fast.next
        return slow      # node where the loop starts

Remove Nth Node From End MEDIUM

Problem: Remove the nth node from the end of the list and return the head.
def remove_nth_from_end_linked(head, n):
    dummy = _L(0, head)
    fast = slow = dummy
    for _ in range(n):
        fast = fast.next
    while fast.next:
        fast = fast.next
        slow = slow.next
    slow.next = slow.next.next
    return dummy.next

Merge Two Sorted Lists MEDIUM

Problem: Merge two sorted linked lists into one sorted list and return its head.
def merge_sorted_lists(a, b):
    dummy = tail = _L(0)
    while a and b:
        if a.val <= b.val:
            tail.next = a; a = a.next
        else:
            tail.next = b; b = b.next
        tail = tail.next
    tail.next = a or b
    return dummy.next

Palindrome Linked List MEDIUM

Problem: Return True if the linked list reads the same forwards and backwards.
def is_palindrome_list_linked(head):
    vals = []
    while head:
        vals.append(head.val)
        head = head.next
    return vals == vals[::-1]

Odd Even Linked List MEDIUM

Problem: Group all odd-indexed nodes followed by the even-indexed nodes (by position, 1-based), keeping relative order. Return the head.
def odd_even_list(head):
    if not head:
        return head
    odd = head
    even = even_head = head.next
    while even and even.next:
        odd.next = even.next
        odd = odd.next
        even.next = odd.next
        even = even.next
    odd.next = even_head
    return head

Swap Nodes in Pairs MEDIUM

Problem: Swap every two adjacent nodes and return the head (swap the nodes themselves, not just values).
def swap_pairs_linked(head):
    dummy = _L(0, head)
    prev = dummy
    while prev.next and prev.next.next:
        first = prev.next
        second = first.next
        first.next = second.next
        second.next = first
        prev.next = second
        prev = first
    return dummy.next

Rotate List MEDIUM

Problem: Rotate the linked list to the right by k places and return the head.
def rotate_right_linked(head, k):
    if not head or not head.next:
        return head
    n = 1
    tail = head
    while tail.next:
        tail = tail.next
        n += 1
    k %= n
    if k == 0:
        return head
    tail.next = head
    steps = n - k
    new_tail = head
    for _ in range(steps - 1):
        new_tail = new_tail.next
    new_head = new_tail.next
    new_tail.next = None
    return new_head

Partition List MEDIUM

Problem: Partition the list so all nodes less than x come before nodes >= x, preserving relative order. Return the head.
def partition_list(head, x):
    less = lt = _L(0)
    greater = gt = _L(0)
    while head:
        if head.val < x:
            lt.next = head; lt = lt.next
        else:
            gt.next = head; gt = gt.next
        head = head.next
    gt.next = None
    lt.next = greater.next
    return less.next

Remove Duplicates From Sorted List II MEDIUM

Problem: From a sorted list, delete every value that appears more than once (leaving only distinct values). Return the head.
def remove_all_duplicates(head):
    dummy = _L(0, head)
    prev = dummy
    cur = head
    while cur:
        if cur.next and cur.next.val == cur.val:
            v = cur.val
            while cur and cur.val == v:
                cur = cur.next
            prev.next = cur
        else:
            prev = cur
            cur = cur.next
    return dummy.next

Add Two Numbers MEDIUM

Problem: Two numbers are stored as linked lists with digits in reverse order. Add them and return the sum as a linked list.
def add_two_numbers_linked(l1, l2):
    dummy = cur = _L(0)
    carry = 0
    while l1 or l2 or carry:
        total = carry
        if l1: total += l1.val; l1 = l1.next
        if l2: total += l2.val; l2 = l2.next
        carry, digit = divmod(total, 10)
        cur.next = _L(digit)
        cur = cur.next
    return dummy.next

Reorder List MEDIUM

Problem: Reorder the list from L0->L1->...->Ln into L0->Ln->L1->Ln-1->... Return the head.
def reorder_list(head):
    if not head or not head.next:
        return head
    slow = fast = head
    while fast.next and fast.next.next:
        slow = slow.next
        fast = fast.next.next
    second = slow.next
    slow.next = None
    prev = None
    while second:
        nxt = second.next
        second.next = prev
        prev = second
        second = nxt
    first = head
    while prev:
        n1, n2 = first.next, prev.next
        first.next = prev
        prev.next = n1
        first = n1
        prev = n2
    return head

Linked List Palindrome HARD

Find the middle, reverse the second half, compare. O(n) time, O(1) space.

class Solution:
    def is_palindrome_list(self, head):
        slow = fast = head
        while fast and fast.next:
            slow = slow.next; fast = fast.next.next
        prev = None                              # reverse second half
        while slow:
            slow.next, prev, slow = prev, slow, slow.next
        left, right = head, prev
        while right:
            if left.value != right.value: return False
            left = left.next; right = right.next
        return True

Zip Linked List HARD

1→2→3→4→5 becomes 1→5→2→4→3. Split, reverse the back half, interleave. O(n).

class Solution:
    def zip_linked_list(self, head):
        slow = fast = head
        while fast.next and fast.next.next:
            slow = slow.next; fast = fast.next.next
        second = slow.next; slow.next = None
        prev = None
        while second:
            second.next, prev, second = prev, second, second.next
        first, second = head, prev
        while second:
            f_next, s_next = first.next, second.next
            first.next = second; second.next = f_next
            first, second = f_next, s_next
        return head

Reverse Nodes in K-Group HARD

Problem: Reverse the nodes of the list k at a time; nodes that don't make a full group of k stay as-is. Return the head.
def reverse_in_k_groups(head, k):
    node = head
    count = 0
    while node and count < k:
        node = node.next
        count += 1
    if count < k:
        return head
    prev = reverse_in_k_groups(node, k)
    cur = head
    for _ in range(k):
        nxt = cur.next
        cur.next = prev
        prev = cur
        cur = nxt
    return prev

Merge K Sorted Linked Lists HARD

Problem: Merge k sorted linked lists into one sorted list using a min-heap of the current heads.
def merge_k_linked(lists):
    import heapq
    heap = []
    for i, node in enumerate(lists):
        if node:
            heapq.heappush(heap, (node.val, i, node))
    dummy = tail = _L(0)
    while heap:
        val, i, node = heapq.heappop(heap)
        tail.next = node
        tail = tail.next
        if node.next:
            heapq.heappush(heap, (node.next.val, i, node.next))
    return dummy.next

Sort a Linked List HARD

Problem: Sort the linked list in ascending order in O(n log n) time using merge sort.
def sort_list_linked(head):
    if not head or not head.next:
        return head
    slow, fast = head, head.next
    while fast and fast.next:
        slow = slow.next
        fast = fast.next.next
    mid = slow.next
    slow.next = None
    left = sort_list_linked(head)
    right = sort_list_linked(mid)
    dummy = tail = _L(0)
    while left and right:
        if left.val <= right.val:
            tail.next = left; left = left.next
        else:
            tail.next = right; right = right.next
        tail = tail.next
    tail.next = left or right
    return dummy.next

Reverse a Sublist HARD

Problem: Reverse the nodes of the list from position left to position right (1-indexed) and return the head.
def reverse_sublist(head, left, right):
    dummy = _L(0, head)
    prev = dummy
    for _ in range(left - 1):
        prev = prev.next
    cur = prev.next
    for _ in range(right - left):
        nxt = cur.next
        cur.next = nxt.next
        nxt.next = prev.next
        prev.next = nxt
    return dummy.next

Remove Zero Sum Consecutive Nodes HARD

Problem: Repeatedly delete consecutive sequences of nodes that sum to zero. Return the head of the final list.
def remove_zero_sum(head):
    dummy = _L(0, head)
    prefix = 0
    seen = {0: dummy}
    cur = dummy
    while cur:
        prefix += cur.val
        seen[prefix] = cur
        cur = cur.next
    prefix = 0
    cur = dummy
    while cur:
        prefix += cur.val
        cur.next = seen[prefix].next
        cur = cur.next
    return dummy.next

Split Linked List in Parts HARD

Problem: Split the list into k consecutive parts as equal in size as possible (earlier parts no smaller). Return the k parts.
def split_list_parts(head, k):
    n = 0
    node = head
    while node:
        n += 1
        node = node.next
    size, extra = divmod(n, k)
    parts = []
    cur = head
    for i in range(k):
        part_head = cur
        part_size = size + (1 if i < extra else 0)
        prev = None
        for _ in range(part_size):
            prev = cur
            cur = cur.next
        if prev:
            prev.next = None
        parts.append(part_head if part_size else None)
    return parts

Add Two Numbers II HARD

Problem: Two numbers are stored as linked lists with the most significant digit first. Add them and return the sum, most significant digit first.
def add_two_numbers_forward(l1, l2):
    s1, s2 = [], []
    while l1: s1.append(l1.val); l1 = l1.next
    while l2: s2.append(l2.val); l2 = l2.next
    carry = 0
    head = None
    while s1 or s2 or carry:
        total = carry
        if s1: total += s1.pop()
        if s2: total += s2.pop()
        carry, digit = divmod(total, 10)
        head = _L(digit, head)
    return head

Swapping Nodes in a Linked List HARD

Problem: Swap the values of the kth node from the beginning and the kth node from the end. Return the head.
def swap_kth_nodes(head, k):
    first = head
    for _ in range(k - 1):
        first = first.next
    second = head
    runner = first
    while runner.next:
        runner = runner.next
        second = second.next
    first.val, second.val = second.val, first.val
    return head

Next Greater Node in Linked List HARD

Problem: For each node, find the value of the next node with a strictly greater value (0 if none). Return the answers as a list.
def next_greater_nodes(head):
    vals = []
    while head:
        vals.append(head.val)
        head = head.next
    res = [0] * len(vals)
    stack = []
    for i, v in enumerate(vals):
        while stack and vals[stack[-1]] < v:
            res[stack.pop()] = v
        stack.append(i)
    return res

Plus One on a Linked List HARD

Problem: A non-negative number is stored as a linked list, most significant digit first. Add one and return the resulting list.
def plus_one_list(head):
    def helper(node):
        if not node:
            return 1
        carry = helper(node.next)
        total = node.val + carry
        node.val = total % 10
        return total // 10
    carry = helper(head)
    if carry:
        return _L(1, head)
    return head

Stacks & Queues

Valid Parentheses EASY

Problem: Given a string of brackets ()[]{}, return True if every bracket is correctly opened and closed in order.
def valid_parentheses(s):
    pairs = {')': '(', ']': '[', '}': '{'}
    stack = []
    for c in s:
        if c in pairs:
            if not stack or stack.pop() != pairs[c]:
                return False
        else:
            stack.append(c)
    return not stack

Reverse a String Using a Stack EASY

Problem: Reverse the string by pushing each character onto a stack and popping them off.
def reverse_with_stack(s):
    stack = list(s)
    out = []
    while stack:
        out.append(stack.pop())
    return ''.join(out)

Baseball Game EASY

Problem: Process operations: an integer records a score, '+' records the sum of the last two, 'D' doubles the last, 'C' cancels the last. Return the total.
def baseball_game(ops):
    stack = []
    for op in ops:
        if op == '+':
            stack.append(stack[-1] + stack[-2])
        elif op == 'D':
            stack.append(stack[-1] * 2)
        elif op == 'C':
            stack.pop()
        else:
            stack.append(int(op))
    return sum(stack)

Remove All Adjacent Duplicates EASY

Problem: Repeatedly remove two adjacent equal characters until none remain. Return the final string.
def remove_adjacent_duplicates(s):
    stack = []
    for c in s:
        if stack and stack[-1] == c:
            stack.pop()
        else:
            stack.append(c)
    return ''.join(stack)

Minimum Add to Make Parentheses Valid EASY

Problem: Return the minimum number of parentheses to insert so the string becomes valid.
def min_add_to_make_valid(s):
    open_needed = 0
    close_needed = 0
    for c in s:
        if c == '(':
            close_needed += 1
        elif close_needed > 0:
            close_needed -= 1
        else:
            open_needed += 1
    return open_needed + close_needed

Backspace String Compare EASY

Problem: '#' means a backspace. Return True if the two strings are equal after applying backspaces.
def backspace_compare(s, t):
    def build(string):
        stack = []
        for c in string:
            if c == '#':
                if stack:
                    stack.pop()
            else:
                stack.append(c)
        return stack
    return build(s) == build(t)

Make String Great EASY

Problem: Repeatedly remove adjacent pairs of the same letter in opposite case (like 'aA' or 'Bb'). Return the result.
def make_good(s):
    stack = []
    for c in s:
        if stack and stack[-1] != c and stack[-1].lower() == c.lower():
            stack.pop()
        else:
            stack.append(c)
    return ''.join(stack)

Remove Outermost Parentheses EASY

Problem: Remove the outermost parentheses of every primitive group in the valid parentheses string. Return the result.
def remove_outer_parentheses(s):
    out = []
    depth = 0
    for c in s:
        if c == '(':
            if depth > 0:
                out.append(c)
            depth += 1
        else:
            depth -= 1
            if depth > 0:
                out.append(c)
    return ''.join(out)

Maximum Nesting Depth of Parentheses EASY

Problem: Return the maximum nesting depth of the parentheses in the expression string.
def max_depth_stacks(s):
    depth = best = 0
    for c in s:
        if c == '(':
            depth += 1
            best = max(best, depth)
        elif c == ')':
            depth -= 1
    return best

Build an Array With Stack Operations EASY

Problem: Push integers 1,2,3,... and use 'Pop' to discard, to build exactly the target list. Return the sequence of operations.
def build_array(target, n):
    ops = []
    cur = 1
    for want in target:
        while cur < want:
            ops.append('Push')
            ops.append('Pop')
            cur += 1
        ops.append('Push')
        cur += 1
    return ops

Balanced Brackets MEDIUM

Are all brackets ()[]{} properly matched? Push opens, match on closes. O(n).

class Solution:
    def balanced_brackets(self, s):
        pairs = {")": "(", "]": "[", "}": "{"}
        stack = []
        for ch in s:
            if ch in "([{":
                stack.append(ch)
            elif ch in pairs:
                if not stack or stack.pop() != pairs[ch]:
                    return False
        return not stack

Sunset Views MEDIUM

Buildings that can see the sunset (taller than all buildings toward the direction). O(n).

class Solution:
    def sunset_views(self, buildings, direction):
        res, tallest = [], 0
        rng = range(len(buildings)) if direction == "WEST" else range(len(buildings)-1, -1, -1)
        for i in rng:
            if buildings[i] > tallest:
                res.append(i); tallest = buildings[i]
        return res if direction == "WEST" else res[::-1]

Next Greater Element MEDIUM

For each item, the next larger one to its right (wrapping around). Monotonic stack. O(n).

class Solution:
    def next_greater(self, arr):
        n = len(arr); res = [-1] * n; stack = []
        for i in range(2 * n):
            idx = i % n
            while stack and arr[stack[-1]] < arr[idx]:
                res[stack.pop()] = arr[idx]
            if i < n: stack.append(idx)
        return res

Reverse Polish Notation (evaluate) MEDIUM

class Solution:
    def eval_rpn(self, tokens):
        ops = {"+": lambda a,b: a+b, "-": lambda a,b: a-b,
               "*": lambda a,b: a*b, "/": lambda a,b: int(a/b)}
        stack = []
        for t in tokens:
            if t in ops:
                b = stack.pop(); a = stack.pop(); stack.append(ops[t](a, b))
            else:
                stack.append(int(t))
        return stack[0]

Sort a Stack (recursively) MEDIUM

class Solution:
    def sort_stack(self, stack):
        if not stack: return stack
        top = stack.pop()
        self.sort_stack(stack)
        self._insert(stack, top)
        return stack
    def _insert(self, stack, val):
        if not stack or stack[-1] <= val:
            stack.append(val); return
        top = stack.pop(); self._insert(stack, val); stack.append(top)

Min-Max Stack Construction MEDIUM

A stack that also returns its current min & max in O(1) (store them at each level).

class MinMaxStack:
    def __init__(self):
        self.stack = []; self.minmax = []
    def push(self, num):
        if self.minmax:
            mn, mx = self.minmax[-1]
            self.minmax.append((min(mn, num), max(mx, num)))
        else:
            self.minmax.append((num, num))
        self.stack.append(num)
    def pop(self):  self.minmax.pop(); return self.stack.pop()
    def peek(self): return self.stack[-1]
    def get_min(self): return self.minmax[-1][0]
    def get_max(self): return self.minmax[-1][1]

Calendar Matching MEDIUM

Find free slots ≥ duration in two people's calendars. Merge busy blocks, return the gaps.

class Solution:
    def calendar_matching(self, c1, b1, c2, b2, duration):
        to_min = lambda t: int(t.split(":")[0]) * 60 + int(t.split(":")[1])
        to_str = lambda m: f"{m//60}:{m%60:02d}"
        blocks = [["0:00", b1[0]]] + c1 + [[b1[1], "23:59"]] \
               + [["0:00", b2[0]]] + c2 + [[b2[1], "23:59"]]
        busy = sorted([[to_min(s), to_min(e)] for s, e in blocks])
        merged = [busy[0]]
        for s, e in busy[1:]:
            if s <= merged[-1][1]: merged[-1][1] = max(merged[-1][1], e)
            else: merged.append([s, e])
        free = []
        for i in range(1, len(merged)):
            if merged[i][0] - merged[i-1][1] >= duration:
                free.append([to_str(merged[i-1][1]), to_str(merged[i][0])])
        return free

Next Greater Element II (Circular) MEDIUM

Problem: For each element in a circular array, find the next greater element scanning forward (wrapping around), or -1.
def next_greater_circular(nums):
    n = len(nums)
    res = [-1] * n
    stack = []
    for i in range(2 * n):
        while stack and nums[stack[-1]] < nums[i % n]:
            res[stack.pop()] = nums[i % n]
        if i < n:
            stack.append(i)
    return res

Daily Temperatures MEDIUM

Problem: For each day, return how many days you must wait for a warmer temperature (0 if none).
def daily_temperatures(temps):
    res = [0] * len(temps)
    stack = []
    for i, t in enumerate(temps):
        while stack and temps[stack[-1]] < t:
            j = stack.pop()
            res[j] = i - j
        stack.append(i)
    return res

Evaluate Reverse Polish Notation MEDIUM

Problem: Evaluate the arithmetic expression given in Reverse Polish (postfix) notation. Division truncates toward zero.
def eval_rpn_stacks(tokens):
    stack = []
    for tok in tokens:
        if tok in '+-*/':
            b = stack.pop(); a = stack.pop()
            if tok == '+': stack.append(a + b)
            elif tok == '-': stack.append(a - b)
            elif tok == '*': stack.append(a * b)
            else: stack.append(int(a / b))
        else:
            stack.append(int(tok))
    return stack[0]

Decode String MEDIUM

Problem: Decode strings like '3[a2[c]]' where k[...] means the bracketed part repeats k times. Return the expanded string.
def decode_string(s):
    stack = []
    cur = ''
    num = 0
    for c in s:
        if c.isdigit():
            num = num * 10 + int(c)
        elif c == '[':
            stack.append((cur, num))
            cur = ''; num = 0
        elif c == ']':
            prev, k = stack.pop()
            cur = prev + cur * k
        else:
            cur += c
    return cur

Asteroid Collision MEDIUM

Problem: Asteroids move right (+) or left (-); equal sizes annihilate, otherwise the smaller explodes. Return the survivors.
def asteroid_collision(asteroids):
    stack = []
    for a in asteroids:
        alive = True
        while alive and a < 0 and stack and stack[-1] > 0:
            if stack[-1] < -a:
                stack.pop()
            elif stack[-1] == -a:
                stack.pop()
                alive = False
            else:
                alive = False
        if alive:
            stack.append(a)
    return stack

Validate Stack Sequences MEDIUM

Problem: Given a push order and a pop order, return True if they could describe valid push/pop operations on one stack.
def validate_stack_sequences(pushed, popped):
    stack = []
    i = 0
    for x in pushed:
        stack.append(x)
        while stack and i < len(popped) and stack[-1] == popped[i]:
            stack.pop()
            i += 1
    return not stack

Simplify Absolute Path MEDIUM

Problem: Simplify a Unix-style absolute path (handling '.', '..', and repeated slashes). Return the canonical path.
def simplify_path_stacks(path):
    stack = []
    for part in path.split('/'):
        if part == '' or part == '.':
            continue
        if part == '..':
            if stack:
                stack.pop()
        else:
            stack.append(part)
    return '/' + '/'.join(stack)

Next Greater Element I MEDIUM

Problem: For each value in nums1 (a subset of nums2), find its next greater element to the right within nums2, or -1.
def next_greater_element(nums1, nums2):
    greater = {}
    stack = []
    for n in nums2:
        while stack and stack[-1] < n:
            greater[stack.pop()] = n
        stack.append(n)
    return [greater.get(n, -1) for n in nums1]

Score of Parentheses MEDIUM

Problem: () scores 1, AB scores A+B, and (A) scores 2*A. Return the total score of the balanced parentheses string.
def score_parentheses(s):
    stack = [0]
    for c in s:
        if c == '(':
            stack.append(0)
        else:
            v = stack.pop()
            stack[-1] += max(2 * v, 1)
    return stack[0]

Remove Invalid Parentheses (Minimal) MEDIUM

Problem: Remove the minimum number of parentheses so the result is valid, keeping all letters. Return one valid result.
def min_remove_valid(s):
    s = list(s)
    stack = []
    for i, c in enumerate(s):
        if c == '(':
            stack.append(i)
        elif c == ')':
            if stack:
                stack.pop()
            else:
                s[i] = ''
    for i in stack:
        s[i] = ''
    return ''.join(s)

Same BSTs (without building them) HARD

Do two arrays produce identical BSTs? Compare roots, then left/right subsets recursively. O(n²).

class Solution:
    def same_bsts(self, a, b):
        if len(a) != len(b): return False
        if not a: return True
        if a[0] != b[0]: return False
        left_a  = [x for x in a[1:] if x < a[0]]
        left_b  = [x for x in b[1:] if x < b[0]]
        right_a = [x for x in a[1:] if x >= a[0]]
        right_b = [x for x in b[1:] if x >= b[0]]
        return self.same_bsts(left_a, left_b) and self.same_bsts(right_a, right_b)

Largest Rectangle in Histogram HARD

Problem: Given bar heights, return the area of the largest rectangle that fits entirely under the histogram.
def largest_rectangle_stacks(heights):
    stack = []
    best = 0
    for i, h in enumerate(heights + [0]):
        while stack and heights[stack[-1]] >= h:
            height = heights[stack.pop()]
            width = i if not stack else i - stack[-1] - 1
            best = max(best, height * width)
        stack.append(i)
    return best

Maximal Rectangle HARD

Problem: Given a binary matrix, return the area of the largest rectangle containing only 1s.
def maximal_rectangle(matrix):
    if not matrix:
        return 0
    heights = [0] * len(matrix[0])
    best = 0
    for row in matrix:
        for i, v in enumerate(row):
            heights[i] = heights[i] + 1 if v == 1 else 0
        stack = []
        for i, h in enumerate(heights + [0]):
            while stack and heights[stack[-1]] >= h:
                height = heights[stack.pop()]
                width = i if not stack else i - stack[-1] - 1
                best = max(best, height * width)
            stack.append(i)
    return best

Trapping Rain Water (Stack) HARD

Problem: Given an elevation map, compute how much rain water it can trap, using a monotonic stack.
def trap_rain_water(height):
    stack = []
    water = 0
    for i, h in enumerate(height):
        while stack and height[stack[-1]] < h:
            bottom = height[stack.pop()]
            if not stack:
                break
            width = i - stack[-1] - 1
            bounded = min(height[stack[-1]], h) - bottom
            water += width * bounded
        stack.append(i)
    return water

Basic Calculator HARD

Problem: Evaluate an expression containing non-negative integers, '+', '-', and parentheses.
def calculate_stacks(s):
    stack = []
    result = 0
    num = 0
    sign = 1
    for c in s:
        if c.isdigit():
            num = num * 10 + int(c)
        elif c in '+-':
            result += sign * num
            num = 0
            sign = 1 if c == '+' else -1
        elif c == '(':
            stack.append(result)
            stack.append(sign)
            result = 0
            sign = 1
        elif c == ')':
            result += sign * num
            num = 0
            result *= stack.pop()
            result += stack.pop()
    return result + sign * num

Basic Calculator II HARD

Problem: Evaluate an expression with non-negative integers and the operators + - * / (integer division truncates toward zero), respecting precedence.
def calculate_ii(s):
    stack = []
    num = 0
    op = '+'
    s = s + '+'
    for c in s:
        if c.isdigit():
            num = num * 10 + int(c)
        elif c in '+-*/':
            if op == '+':
                stack.append(num)
            elif op == '-':
                stack.append(-num)
            elif op == '*':
                stack.append(stack.pop() * num)
            else:
                stack.append(int(stack.pop() / num))
            op = c
            num = 0
    return sum(stack)

Remove Duplicate Letters HARD

Problem: Remove duplicate letters so every letter appears once and the result is the smallest in lexicographic order.
def remove_duplicate_letters(s):
    last = {c: i for i, c in enumerate(s)}
    stack = []
    seen = set()
    for i, c in enumerate(s):
        if c in seen:
            continue
        while stack and stack[-1] > c and last[stack[-1]] > i:
            seen.discard(stack.pop())
        stack.append(c)
        seen.add(c)
    return ''.join(stack)

Sum of Subarray Minimums HARD

Problem: Return the sum of the minimum of every contiguous subarray, modulo 1e9+7, using a monotonic stack.
def sum_subarray_mins(arr):
    MOD = 10**9 + 7
    n = len(arr)
    stack = []
    total = 0
    arr = arr + [float('-inf')]
    for i in range(n + 1):
        while stack and arr[stack[-1]] >= arr[i]:
            mid = stack.pop()
            left = stack[-1] if stack else -1
            total += arr[mid] * (mid - left) * (i - mid)
        stack.append(i)
    return total % MOD

Longest Valid Parentheses HARD

Problem: Return the length of the longest substring of well-formed parentheses.
def longest_valid_parentheses(s):
    stack = [-1]
    best = 0
    for i, c in enumerate(s):
        if c == '(':
            stack.append(i)
        else:
            stack.pop()
            if not stack:
                stack.append(i)
            else:
                best = max(best, i - stack[-1])
    return best

Exclusive Time of Functions HARD

Problem: Given start/end logs of nested function calls on a single thread, return the exclusive running time of each function.
def exclusive_time(n, logs):
    res = [0] * n
    stack = []
    prev = 0
    for log in logs:
        fid, typ, ts = log.split(':')
        fid, ts = int(fid), int(ts)
        if typ == 'start':
            if stack:
                res[stack[-1]] += ts - prev
            stack.append(fid)
            prev = ts
        else:
            res[stack.pop()] += ts - prev + 1
            prev = ts + 1
    return res

132 Pattern HARD

Problem: Return True if there exist indices i
def find_132_pattern(nums):
    stack = []
    third = float('-inf')
    for n in reversed(nums):
        if n < third:
            return True
        while stack and stack[-1] < n:
            third = stack.pop()
        stack.append(n)
    return False

Trees & BST

Find Closest Value In BST EASY

class Solution:
    def find_closest(self, root, target):
        closest = root.value; node = root
        while node:
            if abs(target - node.value) < abs(target - closest):
                closest = node.value
            node = node.left if target < node.value else node.right
        return closest

Evaluate Expression Tree EASY

Leaves are numbers (≥0); internal nodes are operators (−1 add, −2 subtract, −3 divide, −4 multiply).

class Solution:
    def evaluate_expression_tree(self, node):
        if node.value >= 0: return node.value
        left = self.evaluate_expression_tree(node.left)
        right = self.evaluate_expression_tree(node.right)
        if node.value == -1: return left + right
        if node.value == -2: return left - right
        if node.value == -3: return int(left / right)
        return left * right

Maximum Depth of Binary Tree EASY

Problem: Return the maximum depth (number of nodes along the longest root-to-leaf path) of a binary tree.
def max_depth_trees(root):
    if not root:
        return 0
    return 1 + max(max_depth_trees(root.left), max_depth_trees(root.right))

Minimum Depth of Binary Tree EASY

Problem: Return the minimum depth: the number of nodes along the shortest path from the root to a leaf.
def min_depth(root):
    if not root:
        return 0
    if not root.left:
        return 1 + min_depth(root.right)
    if not root.right:
        return 1 + min_depth(root.left)
    return 1 + min(min_depth(root.left), min_depth(root.right))

Count Nodes in a Binary Tree EASY

Problem: Return the total number of nodes in the tree.
def count_nodes_trees(root):
    if not root:
        return 0
    return 1 + count_nodes_trees(root.left) + count_nodes_trees(root.right)

Sum of All Nodes EASY

Problem: Return the sum of all node values in the tree.
def sum_tree(root):
    if not root:
        return 0
    return root.val + sum_tree(root.left) + sum_tree(root.right)

Invert a Binary Tree EASY

Problem: Swap the left and right child of every node (mirror the tree) and return the root.
def invert_tree_trees(root):
    if not root:
        return None
    root.left, root.right = invert_tree_trees(root.right), invert_tree_trees(root.left)
    return root

Same Tree EASY

Problem: Return True if two binary trees are structurally identical and have the same node values.
def is_same_tree_trees(p, q):
    if not p and not q:
        return True
    if not p or not q or p.val != q.val:
        return False
    return is_same_tree_trees(p.left, q.left) and is_same_tree_trees(p.right, q.right)

Maximum Value in a Binary Tree EASY

Problem: Return the largest value stored anywhere in the tree.
def find_max_value(root):
    if not root:
        return float('-inf')
    return max(root.val, find_max_value(root.left), find_max_value(root.right))

Count Leaf Nodes EASY

Problem: Return the number of leaf nodes (nodes with no children) in the tree.
def count_leaves(root):
    if not root:
        return 0
    if not root.left and not root.right:
        return 1
    return count_leaves(root.left) + count_leaves(root.right)

Search in a Binary Search Tree EASY

Problem: Return True if the value exists in the binary search tree, using the BST ordering to guide the search.
def bst_contains(root, target):
    while root:
        if root.val == target:
            return True
        root = root.left if target < root.val else root.right
    return False

Range Sum of BST EASY

Problem: Return the sum of all node values in the BST that fall within the inclusive range [low, high].
def range_sum_bst(root, low, high):
    if not root:
        return 0
    if root.val < low:
        return range_sum_bst(root.right, low, high)
    if root.val > high:
        return range_sum_bst(root.left, low, high)
    return root.val + range_sum_bst(root.left, low, high) + range_sum_bst(root.right, low, high)

Tree Traversal - BFS & DFS MEDIUM

Problem: visit every node in a binary tree.

DFS (depth-first) uses recursion / a stack; BFS (breadth-first, level by level) uses a queue.

def dfs(node):                # depth-first (pre-order)
    if node is None:
        return
    print(node.value)
    dfs(node.left)
    dfs(node.right)

from collections import deque
def bfs(root):                # breadth-first (level order)
    queue = deque([root])
    while queue:
        node = queue.popleft()
        print(node.value)
        if node.left:  queue.append(node.left)
        if node.right: queue.append(node.right)

Validate BST MEDIUM

class Solution:
    def validate_bst(self, node, lo=float("-inf"), hi=float("inf")):
        if node is None: return True
        if not (lo < node.value < hi): return False
        return (self.validate_bst(node.left, lo, node.value) and
                self.validate_bst(node.right, node.value, hi))

Invert Binary Tree MEDIUM

class Solution:
    def invert_tree(self, node):
        if node is None: return
        node.left, node.right = node.right, node.left
        self.invert_tree(node.left)
        self.invert_tree(node.right)

BST Traversal (in / pre / post order) MEDIUM

class Solution:
    def in_order(self, node, out):     # left, root, right -> sorted!
        if node:
            self.in_order(node.left, out); out.append(node.value); self.in_order(node.right, out)
    def pre_order(self, node, out):    # root, left, right
        if node:
            out.append(node.value); self.pre_order(node.left, out); self.pre_order(node.right, out)
    def post_order(self, node, out):   # left, right, root
        if node:
            self.post_order(node.left, out); self.post_order(node.right, out); out.append(node.value)

Youngest Common Ancestor (LCA) MEDIUM

Lowest common ancestor when nodes have a .parent. Equalize depths, then climb together. O(d).

class Solution:
    def get_depth(self, node, top):
        d = 0
        while node != top: node = node.parent; d += 1
        return d
    def youngest_common_ancestor(self, top, a, b):
        da, db = self.get_depth(a, top), self.get_depth(b, top)
        while da > db: a = a.parent; da -= 1
        while db > da: b = b.parent; db -= 1
        while a != b: a = a.parent; b = b.parent
        return a

Iterative In-order Traversal MEDIUM

class Solution:
    def inorder(self, root):
        res = []; stack = []; node = root
        while stack or node:
            while node:
                stack.append(node); node = node.left
            node = stack.pop()
            res.append(node.value)
            node = node.right
        return res

Symmetrical Tree MEDIUM

class Solution:
    def is_symmetric(self, root):
        def mirror(a, b):
            if not a and not b: return True
            if not a or not b or a.value != b.value: return False
            return mirror(a.left, b.right) and mirror(a.right, b.left)
        return mirror(root, root)

Height Balanced Binary Tree MEDIUM

class Solution:
    def is_balanced(self, root):
        def check(node):
            if not node: return 0
            lh = check(node.left); rh = check(node.right)
            if lh == -1 or rh == -1 or abs(lh - rh) > 1: return -1
            return max(lh, rh) + 1
        return check(root) != -1

Flatten Binary Tree (to a list) MEDIUM

class Solution:
    def flatten_tree(self, root):
        node = root
        while node:
            if node.left:
                rightmost = node.left
                while rightmost.right: rightmost = rightmost.right
                rightmost.right = node.right
                node.right = node.left; node.left = None
            node = node.right
        return root

Min Height BST MEDIUM

Build a balanced BST from a sorted array - recurse on the middle. O(n).

class Solution:
    def min_height_bst(self, arr):
        def build(lo, hi):
            if lo > hi: return None
            mid = (lo + hi) // 2
            node = BST(arr[mid])
            node.left = build(lo, mid - 1)
            node.right = build(mid + 1, hi)
            return node
        return build(0, len(arr) - 1)

Find Kth Largest Value In BST MEDIUM

Reverse in-order (right, node, left) gives values largest-first. O(h+k).

class Solution:
    def kth_largest_bst(self, root, k):
        stack = []; node = root; count = 0
        while stack or node:
            while node:
                stack.append(node); node = node.right
            node = stack.pop(); count += 1
            if count == k: return node.value
            node = node.left

Reconstruct BST (from pre-order) MEDIUM

class Solution:
    def reconstruct_bst(self, preorder):
        idx = [0]
        def build(bound=float("inf")):
            if idx[0] == len(preorder) or preorder[idx[0]] >= bound:
                return None
            val = preorder[idx[0]]; idx[0] += 1
            node = BST(val)
            node.left = build(val)
            node.right = build(bound)
            return node
        return build()

Find Successor (in-order) MEDIUM

class Solution:
    def find_successor(self, node):
        if node.right:                       # leftmost of the right subtree
            node = node.right
            while node.left: node = node.left
            return node
        while node.parent and node.parent.right == node:
            node = node.parent               # climb until we go up-left
        return node.parent

Merge Binary Trees MEDIUM

class Solution:
    def merge_trees(self, t1, t2):
        if not t1: return t2
        if not t2: return t1
        t1.value += t2.value
        t1.left = self.merge_trees(t1.left, t2.left)
        t1.right = self.merge_trees(t1.right, t2.right)
        return t1

Max Path Sum In Binary Tree HARD

Largest sum along any path. At each node, best = node + best-left-branch + best-right-branch. O(n).

class Solution:
    def max_path_sum(self, root):
        best = [float("-inf")]
        def helper(node):
            if not node: return 0
            left = max(helper(node.left), 0)
            right = max(helper(node.right), 0)
            best[0] = max(best[0], node.value + left + right)
            return node.value + max(left, right)
        helper(root)
        return best[0]

All Kinds Of Node Depths HARD

Sum of node depths across every subtree. O(n) with a helper that returns sum + count.

class Solution:
    def all_kinds_of_node_depths(self, root):
        def helper(node):
            if not node: return (0, 0)          # (sum_of_depths, node_count)
            ls, lc = helper(node.left)
            rs, rc = helper(node.right)
            depth_sum = ls + lc + rs + rc        # +1 depth for each descendant
            return (depth_sum, lc + rc + 1)
        def total(node):
            if not node: return 0
            return helper(node)[0] + total(node.left) + total(node.right)
        return total(root)

Number Of Binary Tree Topologies HARD

How many tree shapes with n nodes (the Catalan numbers). DP. O(n²).

class Solution:
    def number_of_binary_tree_topologies(self, n):
        cache = [1]
        for m in range(1, n + 1):
            total = 0
            for left in range(m):
                total += cache[left] * cache[m - 1 - left]
            cache.append(total)
        return cache[n]

Compare Leaf Traversal HARD

Do two trees have the same left-to-right leaf sequence?

class Solution:
    def compare_leaf_traversal(self, t1, t2):
        def leaves(node, out):
            if not node: return
            if not node.left and not node.right:
                out.append(node.value); return
            leaves(node.left, out); leaves(node.right, out)
        a, b = [], []
        leaves(t1, a); leaves(t2, b)
        return a == b

Find Nodes Distance K HARD

All nodes exactly k edges from a target. Map parents, then BFS treating the tree as a graph. O(n).

from collections import deque

class Solution:
    def find_nodes_distance_k(self, tree, target, k):
        parents = {}
        def map_parents(node, parent=None):
            if not node: return
            parents[node.value] = parent
            map_parents(node.left, node); map_parents(node.right, node)
        def find(node):
            if not node or node.value == target: return node
            return find(node.left) or find(node.right)
        map_parents(tree)
        start = find(tree)
        queue = deque([(start, 0)]); seen = {start.value}; res = []
        while queue:
            node, dist = queue.popleft()
            if dist == k: res.append(node.value); continue
            for nb in (node.left, node.right, parents[node.value]):
                if nb and nb.value not in seen:
                    seen.add(nb.value); queue.append((nb, dist + 1))
        return res

Binary Tree Maximum Path Sum HARD

Problem: A path may start and end at any nodes and goes through parent-child connections. Return the maximum sum of any such path.
def max_path_sum_trees(root):
    best = float('-inf')
    def gain(node):
        nonlocal best
        if not node:
            return 0
        left = max(gain(node.left), 0)
        right = max(gain(node.right), 0)
        best = max(best, node.val + left + right)
        return node.val + max(left, right)
    gain(root)
    return best

Lowest Common Ancestor HARD

Problem: Given the values of two nodes in a binary tree, return the value of their lowest common ancestor.
def lowest_common_ancestor_trees(root, p, q):
    def helper(node):
        if not node:
            return None
        if node.val == p or node.val == q:
            return node
        left = helper(node.left)
        right = helper(node.right)
        if left and right:
            return node
        return left or right
    return helper(root).val

Path Sum III (Count Paths) HARD

Problem: Count the number of downward paths (any node to any descendant) whose values sum to the target, using prefix sums.
def path_sum_count(root, target):
    from collections import defaultdict
    prefix = defaultdict(int)
    prefix[0] = 1
    def dfs(node, current):
        if not node:
            return 0
        current += node.val
        count = prefix[current - target]
        prefix[current] += 1
        count += dfs(node.left, current) + dfs(node.right, current)
        prefix[current] -= 1
        return count
    return dfs(root, 0)

Vertical Order Traversal HARD

Problem: Return the node values grouped by column (left to right). Within a column, order by row, and by value on ties.
def vertical_order(root):
    from collections import defaultdict
    columns = defaultdict(list)
    def dfs(node, row, col):
        if not node:
            return
        columns[col].append((row, node.val))
        dfs(node.left, row + 1, col - 1)
        dfs(node.right, row + 1, col + 1)
    dfs(root, 0, 0)
    result = []
    for col in sorted(columns):
        result.append([val for row, val in sorted(columns[col])])
    return result

Maximum Width of Binary Tree HARD

Problem: Return the maximum width of the tree: the longest distance between the leftmost and rightmost non-null nodes on any level (counting the null gaps between them).
def max_width(root):
    if not root:
        return 0
    from collections import deque
    best = 0
    queue = deque([(root, 0)])
    while queue:
        n = len(queue)
        _, first = queue[0]
        last = first
        for _ in range(n):
            node, idx = queue.popleft()
            last = idx
            if node.left:
                queue.append((node.left, 2 * idx))
            if node.right:
                queue.append((node.right, 2 * idx + 1))
        best = max(best, last - first + 1)
    return best

Distribute Coins in Binary Tree HARD

Problem: Each node has some coins; there are exactly as many coins as nodes. In one move you may pass a coin between adjacent nodes. Return the minimum moves to give every node exactly one coin.
def distribute_coins(root):
    moves = 0
    def dfs(node):
        nonlocal moves
        if not node:
            return 0
        left = dfs(node.left)
        right = dfs(node.right)
        moves += abs(left) + abs(right)
        return node.val + left + right - 1
    dfs(root)
    return moves

Graphs

Build an Adjacency List EASY

Problem: Given n nodes (0..n-1) and a list of undirected edges, build and return an adjacency list (a list of neighbor lists).
def build_adjacency_list(n, edges):
    adj = [[] for _ in range(n)]
    for u, v in edges:
        adj[u].append(v)
        adj[v].append(u)
    return adj

Node Degree EASY

Problem: Return the degree (number of neighbors) of a node in an adjacency list.
def node_degree(adj, node):
    return len(adj[node])

Count Edges in an Undirected Graph EASY

Problem: Given an adjacency list of an undirected graph, return the number of distinct edges.
def count_edges(adj):
    total = sum(len(neighbors) for neighbors in adj)
    return total // 2

Check if an Edge Exists EASY

Problem: Return True if there is a direct edge from u to v in the adjacency list.
def has_edge(adj, u, v):
    return v in adj[u]

Breadth-First Search Order EASY

Problem: Return the order in which nodes are visited by BFS starting from the given node.
def bfs_order(adj, start):
    from collections import deque
    visited = [False] * len(adj)
    order = []
    queue = deque([start])
    visited[start] = True
    while queue:
        node = queue.popleft()
        order.append(node)
        for neighbor in adj[node]:
            if not visited[neighbor]:
                visited[neighbor] = True
                queue.append(neighbor)
    return order

Depth-First Search Order EASY

Problem: Return the order in which nodes are visited by DFS starting from the given node.
def dfs_order(adj, start):
    visited = [False] * len(adj)
    order = []
    def dfs(node):
        visited[node] = True
        order.append(node)
        for neighbor in adj[node]:
            if not visited[neighbor]:
                dfs(neighbor)
    dfs(start)
    return order

Count Reachable Nodes EASY

Problem: Return how many nodes are reachable from the start node (including itself).
def count_reachable(adj, start):
    visited = set()
    stack = [start]
    while stack:
        node = stack.pop()
        if node in visited:
            continue
        visited.add(node)
        for neighbor in adj[node]:
            if neighbor not in visited:
                stack.append(neighbor)
    return len(visited)

List a Node's Neighbors EASY

Problem: Return the sorted list of neighbors of a node in an adjacency list.
def get_neighbors(adj, node):
    return sorted(adj[node])

Path Exists Between Two Nodes EASY

Problem: Return True if there is a path from source to destination in an undirected graph given by an adjacency list.
def path_exists(adj, source, dest):
    visited = set()
    stack = [source]
    while stack:
        node = stack.pop()
        if node == dest:
            return True
        if node in visited:
            continue
        visited.add(node)
        stack.extend(adj[node])
    return dest in visited

Count Connected Components EASY

Problem: Given n nodes and a list of undirected edges, return the number of connected components.
def count_components(n, edges):
    adj = [[] for _ in range(n)]
    for u, v in edges:
        adj[u].append(v)
        adj[v].append(u)
    visited = [False] * n
    count = 0
    for i in range(n):
        if not visited[i]:
            count += 1
            stack = [i]
            while stack:
                node = stack.pop()
                if visited[node]:
                    continue
                visited[node] = True
                stack.extend(adj[node])
    return count

Cycle In Graph MEDIUM

Detect a cycle in a directed graph (adjacency list). DFS tracking the current path. O(V+E).

class Solution:
    def cycle_in_graph(self, edges):
        n = len(edges)
        visited = [False] * n; in_path = [False] * n
        def dfs(node):
            visited[node] = in_path[node] = True
            for nb in edges[node]:
                if not visited[nb]:
                    if dfs(nb): return True
                elif in_path[nb]:
                    return True
            in_path[node] = False
            return False
        return any(dfs(i) for i in range(n) if not visited[i])

Remove Islands MEDIUM

Flip any group of 1s not connected to the border to 0. Mark border-connected 1s, then clear the rest. O(w·h).

class Solution:
    def remove_islands(self, matrix):
        rows, cols = len(matrix), len(matrix[0])
        def fill(r, c):
            stack = [(r, c)]
            while stack:
                r, c = stack.pop()
                if 0 <= r < rows and 0 <= c < cols and matrix[r][c] == 1:
                    matrix[r][c] = 2                      # mark as safe
                    stack += [(r+1,c),(r-1,c),(r,c+1),(r,c-1)]
        for r in range(rows):
            for c in range(cols):
                if (r in (0, rows-1) or c in (0, cols-1)) and matrix[r][c] == 1:
                    fill(r, c)
        for r in range(rows):
            for c in range(cols):
                matrix[r][c] = 1 if matrix[r][c] == 2 else 0
        return matrix

River Sizes MEDIUM

Sizes of every connected group of 1s in a grid. Flood-fill each. O(w·h).

class Solution:
    def river_sizes(self, matrix):
        sizes = []
        visited = [[False] * len(matrix[0]) for _ in matrix]
        for r in range(len(matrix)):
            for c in range(len(matrix[0])):
                if matrix[r][c] == 1 and not visited[r][c]:
                    size = 0; stack = [(r, c)]
                    while stack:
                        i, j = stack.pop()
                        if (i < 0 or j < 0 or i >= len(matrix) or j >= len(matrix[0])
                                or visited[i][j] or matrix[i][j] == 0):
                            continue
                        visited[i][j] = True; size += 1
                        stack += [(i+1,j), (i-1,j), (i,j+1), (i,j-1)]
                    sizes.append(size)
        return sizes

Union-Find (Disjoint Set) MEDIUM

Track which items are connected. Near-O(1) with path compression.

class UnionFind:
    def __init__(self): self.parent = {}
    def add(self, x): self.parent.setdefault(x, x)
    def find(self, x):
        while self.parent[x] != x:
            self.parent[x] = self.parent[self.parent[x]]   # path compression
            x = self.parent[x]
        return x
    def union(self, a, b):
        self.parent[self.find(a)] = self.find(b)

Two-Colorable (bipartite check) MEDIUM

Can the graph be 2-colored so no edge joins same colors? BFS, alternate colors. O(V+E).

from collections import deque

class Solution:
    def two_colorable(self, edges):
        colors = [None] * len(edges)
        colors[0] = True
        queue = deque([0])
        while queue:
            node = queue.popleft()
            for nb in edges[node]:
                if colors[nb] is None:
                    colors[nb] = not colors[node]; queue.append(nb)
                elif colors[nb] == colors[node]:
                    return False
        return True

Min Knight Moves (BFS) MEDIUM

from collections import deque

class Solution:
    def min_knight_moves(self, start, target):
        moves = [(1,2),(2,1),(-1,2),(-2,1),(1,-2),(2,-1),(-1,-2),(-2,-1)]
        q = deque([(start[0], start[1], 0)]); seen = {tuple(start)}
        while q:
            r, c, d = q.popleft()
            if [r, c] == target: return d
            for dr, dc in moves:
                nxt = (r + dr, c + dc)
                if nxt not in seen:
                    seen.add(nxt); q.append((nxt[0], nxt[1], d + 1))

Number of Islands MEDIUM

Problem: Given a grid of '1' (land) and '0' (water), return the number of islands (groups of land connected horizontally or vertically).
def num_islands_graphs(grid):
    if not grid:
        return 0
    rows, cols = len(grid), len(grid[0])
    count = 0
    def sink(r, c):
        if r < 0 or r >= rows or c < 0 or c >= cols or grid[r][c] != 1:
            return
        grid[r][c] = 0
        sink(r+1, c); sink(r-1, c); sink(r, c+1); sink(r, c-1)
    for r in range(rows):
        for c in range(cols):
            if grid[r][c] == 1:
                count += 1
                sink(r, c)
    return count

Max Area of Island MEDIUM

Problem: Return the size (in cells) of the largest island in the grid.
def max_area_of_island(grid):
    rows, cols = len(grid), len(grid[0])
    def area(r, c):
        if r < 0 or r >= rows or c < 0 or c >= cols or grid[r][c] != 1:
            return 0
        grid[r][c] = 0
        return 1 + area(r+1, c) + area(r-1, c) + area(r, c+1) + area(r, c-1)
    best = 0
    for r in range(rows):
        for c in range(cols):
            if grid[r][c] == 1:
                best = max(best, area(r, c))
    return best

Flood Fill MEDIUM

Problem: Starting from pixel (sr, sc), replace the color of that region (connected same-colored pixels) with new_color. Return the image.
def flood_fill(image, sr, sc, new_color):
    old = image[sr][sc]
    if old == new_color:
        return image
    rows, cols = len(image), len(image[0])
    def fill(r, c):
        if r < 0 or r >= rows or c < 0 or c >= cols or image[r][c] != old:
            return
        image[r][c] = new_color
        fill(r+1, c); fill(r-1, c); fill(r, c+1); fill(r, c-1)
    fill(sr, sc)
    return image

Is Graph Bipartite MEDIUM

Problem: Given an adjacency list, return True if the graph can be 2-colored so no edge connects two nodes of the same color.
def is_bipartite(graph):
    color = {}
    for start in range(len(graph)):
        if start in color:
            continue
        color[start] = 0
        stack = [start]
        while stack:
            node = stack.pop()
            for neighbor in graph[node]:
                if neighbor not in color:
                    color[neighbor] = color[node] ^ 1
                    stack.append(neighbor)
                elif color[neighbor] == color[node]:
                    return False
    return True

Course Schedule (Can Finish) MEDIUM

Problem: Given numCourses and prerequisite pairs [a, b] (take b before a), return True if all courses can be finished (no cycle).
def can_finish_graphs(num_courses, prerequisites):
    from collections import deque
    adj = [[] for _ in range(num_courses)]
    indegree = [0] * num_courses
    for a, b in prerequisites:
        adj[b].append(a)
        indegree[a] += 1
    queue = deque(i for i in range(num_courses) if indegree[i] == 0)
    taken = 0
    while queue:
        node = queue.popleft()
        taken += 1
        for nxt in adj[node]:
            indegree[nxt] -= 1
            if indegree[nxt] == 0:
                queue.append(nxt)
    return taken == num_courses

Topological Sort HARD

Order tasks so each comes after its prerequisites. Kahn's algorithm (in-degrees + queue). O(V+E).

from collections import deque

class Solution:
    def topological_sort(self, jobs, deps):
        graph = {j: [] for j in jobs}
        indegree = {j: 0 for j in jobs}
        for a, b in deps:                 # a must come before b
            graph[a].append(b); indegree[b] += 1
        queue = deque(j for j in jobs if indegree[j] == 0)
        order = []
        while queue:
            j = queue.popleft(); order.append(j)
            for nb in graph[j]:
                indegree[nb] -= 1
                if indegree[nb] == 0: queue.append(nb)
        return order if len(order) == len(jobs) else []   # [] means a cycle

Dijkstra's Algorithm (shortest paths) HARD

Shortest distance from a start node to all others (non-negative weights). Min-heap. O(E log V).

import heapq

class Solution:
    def dijkstra(self, start, edges):
        # edges[i] = list of [destination, weight]
        dist = [float("inf")] * len(edges); dist[start] = 0
        pq = [(0, start)]
        while pq:
            d, node = heapq.heappop(pq)
            if d > dist[node]: continue
            for nb, w in edges[node]:
                if d + w < dist[nb]:
                    dist[nb] = d + w; heapq.heappush(pq, (d + w, nb))
        return [-1 if d == float("inf") else d for d in dist]

Boggle Board HARD

Find which words appear on the board (8-directional). Trie of words + DFS.

class Solution:
    def boggle_board(self, board, words):
        trie = {}
        for w in words:                 # build a trie
            node = trie
            for ch in w: node = node.setdefault(ch, {})
            node["*"] = w
        rows, cols = len(board), len(board[0])
        found = set()
        def dfs(r, c, node, visited):
            if (r < 0 or c < 0 or r >= rows or c >= cols or (r, c) in visited
                    or board[r][c] not in node):
                return
            node = node[board[r][c]]; visited = visited | {(r, c)}
            if "*" in node: found.add(node["*"])
            for dr in (-1, 0, 1):
                for dc in (-1, 0, 1):
                    dfs(r + dr, c + dc, node, visited)
        for r in range(rows):
            for c in range(cols):
                dfs(r, c, trie, set())
        return list(found)

A* Algorithm (heuristic pathfinding) HARD

Like Dijkstra but guided by a heuristic toward the goal. Min-heap on (cost + estimate).

import heapq

class Solution:
    def a_star(self, start, end, graph):
        # graph[node] = list of (neighbor, cost); positions are (row, col)
        h = lambda a, b: abs(a[0]-b[0]) + abs(a[1]-b[1])   # Manhattan estimate
        pq = [(h(start, end), 0, start)]
        g = {start: 0}
        while pq:
            _, cost, node = heapq.heappop(pq)
            if node == end: return cost
            for nb, w in graph.get(node, []):
                ng = cost + w
                if nb not in g or ng < g[nb]:
                    g[nb] = ng
                    heapq.heappush(pq, (ng + h(nb, end), ng, nb))
        return -1

Detect Arbitrage HARD

Is there a currency cycle that multiplies to > 1? Take −log of rates → a profitable cycle is a negative cycle (Bellman-Ford). O(n³).

import math

class Solution:
    def detect_arbitrage(self, rates):
        n = len(rates)
        graph = [[-math.log(rates[i][j]) for j in range(n)] for i in range(n)]
        dist = [0] * n
        for _ in range(n - 1):                 # relax edges n-1 times
            for u in range(n):
                for v in range(n):
                    if dist[u] + graph[u][v] < dist[v]:
                        dist[v] = dist[u] + graph[u][v]
        for u in range(n):                     # one more pass detects a neg cycle
            for v in range(n):
                if dist[u] + graph[u][v] < dist[v]:
                    return True
        return False

Kruskal's Algorithm (MST) HARD

Cheapest set of edges connecting every node. Sort edges, add if they join two different groups (union-find). O(E log E).

class Solution:
    def kruskal(self, n, edges):
        # edges = [(weight, u, v), ...]
        parent = list(range(n))
        def find(x):
            while parent[x] != x:
                parent[x] = parent[parent[x]]; x = parent[x]
            return x
        total = 0
        for w, u, v in sorted(edges):
            ru, rv = find(u), find(v)
            if ru != rv:
                parent[ru] = rv; total += w
        return total

Prim's Algorithm (MST) HARD

import heapq

class Solution:
    def prim(self, n, adj):
        # adj[u] = list of (weight, v)
        visited = [False] * n; pq = [(0, 0)]; total = 0
        while pq:
            w, u = heapq.heappop(pq)
            if visited[u]: continue
            visited[u] = True; total += w
            for weight, v in adj[u]:
                if not visited[v]: heapq.heappush(pq, (weight, v))
        return total

Course Schedule II (Order) HARD

Problem: Return a valid ordering of courses to take all of them, or an empty list if it is impossible (there is a cycle).
def find_order_graphs(num_courses, prerequisites):
    from collections import deque
    adj = [[] for _ in range(num_courses)]
    indegree = [0] * num_courses
    for a, b in prerequisites:
        adj[b].append(a)
        indegree[a] += 1
    queue = deque(i for i in range(num_courses) if indegree[i] == 0)
    order = []
    while queue:
        node = queue.popleft()
        order.append(node)
        for nxt in adj[node]:
            indegree[nxt] -= 1
            if indegree[nxt] == 0:
                queue.append(nxt)
    return order if len(order) == num_courses else []

Network Delay Time HARD

Problem: Given directed weighted edges [u, v, w], n nodes (1-indexed), and a source k, return the time for all nodes to receive a signal, or -1 if some are unreachable.
def network_delay_time(times, n, k):
    import heapq
    graph = [[] for _ in range(n + 1)]
    for u, v, w in times:
        graph[u].append((v, w))
    dist = {}
    heap = [(0, k)]
    while heap:
        d, node = heapq.heappop(heap)
        if node in dist:
            continue
        dist[node] = d
        for neighbor, weight in graph[node]:
            if neighbor not in dist:
                heapq.heappush(heap, (d + weight, neighbor))
    return max(dist.values()) if len(dist) == n else -1

Word Ladder Length HARD

Problem: Return the number of words in the shortest transformation sequence from begin_word to end_word, changing one letter at a time (each intermediate word must be in the word list), or 0 if impossible.
def ladder_length_graphs(begin_word, end_word, word_list):
    from collections import deque
    words = set(word_list)
    if end_word not in words:
        return 0
    queue = deque([(begin_word, 1)])
    while queue:
        word, steps = queue.popleft()
        if word == end_word:
            return steps
        for i in range(len(word)):
            for c in 'abcdefghijklmnopqrstuvwxyz':
                candidate = word[:i] + c + word[i+1:]
                if candidate in words:
                    words.remove(candidate)
                    queue.append((candidate, steps + 1))
    return 0

Cheapest Flights Within K Stops HARD

Problem: Given flights [from, to, price], find the cheapest price from src to dst using at most k stops, or -1 if there is no such route (Bellman-Ford).
def find_cheapest_price(n, flights, src, dst, k):
    prices = [float('inf')] * n
    prices[src] = 0
    for _ in range(k + 1):
        temp = prices[:]
        for u, v, w in flights:
            if prices[u] + w < temp[v]:
                temp[v] = prices[u] + w
        prices = temp
    return prices[dst] if prices[dst] != float('inf') else -1

Heaps

Kth Largest Element in an Array EASY

Problem: Return the kth largest element in an unsorted array (1-indexed, k=1 means the maximum).
def kth_largest_element(nums, k):
    import heapq
    return heapq.nlargest(k, nums)[-1]

Kth Smallest Element in an Array EASY

Problem: Return the kth smallest element in an unsorted array (1-indexed).
def kth_smallest_element(nums, k):
    import heapq
    return heapq.nsmallest(k, nums)[-1]

K Smallest Elements EASY

Problem: Return the k smallest elements of the array, sorted ascending.
def k_smallest(nums, k):
    import heapq
    return sorted(heapq.nsmallest(k, nums))

K Largest Elements EASY

Problem: Return the k largest elements of the array, sorted descending.
def k_largest(nums, k):
    import heapq
    return sorted(heapq.nlargest(k, nums), reverse=True)

Last Stone Weight EASY

Problem: Repeatedly smash the two heaviest stones; if unequal, the difference returns to the pile. Return the weight of the last stone (0 if none).
def last_stone_weight(stones):
    import heapq
    heap = [-s for s in stones]
    heapq.heapify(heap)
    while len(heap) > 1:
        a = -heapq.heappop(heap)
        b = -heapq.heappop(heap)
        if a != b:
            heapq.heappush(heap, -(a - b))
    return -heap[0] if heap else 0

Heap Sort EASY

Problem: Sort the array ascending using a heap (push all, then pop the minimum repeatedly).
def heap_sort_heaps(nums):
    import heapq
    heap = list(nums)
    heapq.heapify(heap)
    return [heapq.heappop(heap) for _ in range(len(heap))]

Is Array a Valid Min-Heap EASY

Problem: An array is a min-heap if every parent is <= its children. Return True or False.
def is_min_heap(arr):
    n = len(arr)
    for i in range(n):
        l, r = 2 * i + 1, 2 * i + 2
        if l < n and arr[i] > arr[l]:
            return False
        if r < n and arr[i] > arr[r]:
            return False
    return True

Sum of K Smallest Elements EASY

Problem: Return the sum of the k smallest elements in the array.
def sum_k_smallest(nums, k):
    import heapq
    return sum(heapq.nsmallest(k, nums))

Sum of K Largest Elements EASY

Problem: Return the sum of the k largest elements in the array.
def sum_k_largest(nums, k):
    import heapq
    return sum(heapq.nlargest(k, nums))

Minimum Cost to Connect Ropes EASY

Problem: Connecting two ropes costs the sum of their lengths. Return the minimum total cost to connect all ropes into one.
def connect_ropes(ropes):
    import heapq
    heapq.heapify(ropes)
    total = 0
    while len(ropes) > 1:
        a = heapq.heappop(ropes)
        b = heapq.heappop(ropes)
        total += a + b
        heapq.heappush(ropes, a + b)
    return total

Top K Frequent Elements MEDIUM

Problem: Return the k most frequent elements (any order), using a heap over the frequency counts.
def top_k_frequent(nums, k):
    import heapq
    from collections import Counter
    freq = Counter(nums)
    return heapq.nlargest(k, freq.keys(), key=freq.get)

K Closest Points to Origin MEDIUM

Problem: Return the k points closest to the origin (0,0), ordered by increasing distance.
def k_closest_points(points, k):
    import heapq
    return heapq.nsmallest(k, points, key=lambda p: p[0]**2 + p[1]**2)

Kth Largest in a Stream MEDIUM

Problem: Given an initial array and a stream of added values, return the kth largest element after each addition.
def kth_largest_stream(k, initial, adds):
    import heapq
    heap = initial[:]
    heapq.heapify(heap)
    while len(heap) > k:
        heapq.heappop(heap)
    out = []
    for x in adds:
        heapq.heappush(heap, x)
        if len(heap) > k:
            heapq.heappop(heap)
        out.append(heap[0])
    return out

Sort Characters by Frequency MEDIUM

Problem: Return the string with characters sorted by decreasing frequency (ties broken by heap order).
def frequency_sort(s):
    import heapq
    from collections import Counter
    freq = Counter(s)
    heap = [(-c, ch) for ch, c in freq.items()]
    heapq.heapify(heap)
    out = []
    while heap:
        c, ch = heapq.heappop(heap)
        out.append(ch * (-c))
    return ''.join(out)

Find K Pairs With Smallest Sums MEDIUM

Problem: Given two sorted arrays, return the k pairs (a,b) with the smallest sums a+b.
def k_smallest_pairs_heaps(nums1, nums2, k):
    import heapq
    if not nums1 or not nums2:
        return []
    heap = [(nums1[i] + nums2[0], i, 0) for i in range(min(k, len(nums1)))]
    heapq.heapify(heap)
    out = []
    while heap and len(out) < k:
        _, i, j = heapq.heappop(heap)
        out.append([nums1[i], nums2[j]])
        if j + 1 < len(nums2):
            heapq.heappush(heap, (nums1[i] + nums2[j + 1], i, j + 1))
    return out

Minimum Number of Meeting Rooms MEDIUM

Problem: Given meeting intervals, return the minimum number of rooms needed so no two overlapping meetings share a room.
def min_meeting_rooms(intervals):
    import heapq
    if not intervals:
        return 0
    intervals.sort()
    heap = []
    for s, e in intervals:
        if heap and heap[0] <= s:
            heapq.heappop(heap)
        heapq.heappush(heap, e)
    return len(heap)

Furthest Building You Can Reach MEDIUM

Problem: Climbing up needs either a ladder or that many bricks. Use ladders on the biggest climbs. Return the furthest building index reachable.
def furthest_building(heights, bricks, ladders):
    import heapq
    heap = []
    for i in range(len(heights) - 1):
        diff = heights[i + 1] - heights[i]
        if diff > 0:
            heapq.heappush(heap, diff)
            if len(heap) > ladders:
                bricks -= heapq.heappop(heap)
                if bricks < 0:
                    return i
    return len(heights) - 1

Reduce Array Size to Half MEDIUM

Problem: Remove all occurrences of chosen values. Return the minimum number of distinct values to remove so at least half the array is gone.
def min_set_size(arr):
    import heapq
    from collections import Counter
    counts = [-c for c in Counter(arr).values()]
    heapq.heapify(counts)
    removed = 0
    ops = 0
    while removed < len(arr) // 2:
        removed += -heapq.heappop(counts)
        ops += 1
    return ops

Maximum Score After K Operations MEDIUM

Problem: In each operation, take the largest element x, add it to your score, and replace it with ceil(x/3). Return the max score after k operations.
def max_score_k_ops(nums, k):
    import heapq, math
    heap = [-n for n in nums]
    heapq.heapify(heap)
    score = 0
    for _ in range(k):
        x = -heapq.heappop(heap)
        score += x
        heapq.heappush(heap, -math.ceil(x / 3))
    return score

Take Gifts With Maximum Value MEDIUM

Problem: Each second, take the pile with the most gifts and leave floor(sqrt(pile)) behind. Return the total gifts remaining after k seconds.
def take_gifts(gifts, k):
    import heapq, math
    heap = [-g for g in gifts]
    heapq.heapify(heap)
    for _ in range(k):
        x = -heapq.heappop(heap)
        heapq.heappush(heap, -math.isqrt(x))
    return -sum(heap)

Continuous Median HARD

Median of a growing stream. Keep a max-heap of the low half & a min-heap of the high half. O(log n) per add.

import heapq
class ContinuousMedian:
    def __init__(self):
        self.lo = []   # max-heap (store negatives)
        self.hi = []   # min-heap
    def add(self, num):
        heapq.heappush(self.lo, -num)
        heapq.heappush(self.hi, -heapq.heappop(self.lo))  # balance
        if len(self.hi) > len(self.lo):
            heapq.heappush(self.lo, -heapq.heappop(self.hi))
    def median(self):
        if len(self.lo) > len(self.hi): return -self.lo[0]
        return (-self.lo[0] + self.hi[0]) / 2

Heap Sort HARD

import heapq

class Solution:
    def heap_sort(self, arr):
        heapq.heapify(arr)                       # O(n)
        return [heapq.heappop(arr) for _ in range(len(arr))]   # n × O(log n)

Median Of Two Sorted Arrays HARD

O(log(min(m,n))) by binary-searching the partition point.

class Solution:
    def median_two_sorted(self, a, b):
        if len(a) > len(b): a, b = b, a
        m, n = len(a), len(b)
        lo, hi = 0, m
        while lo <= hi:
            i = (lo + hi) // 2
            j = (m + n + 1) // 2 - i
            a_l = a[i-1] if i > 0 else float("-inf")
            a_r = a[i]   if i < m else float("inf")
            b_l = b[j-1] if j > 0 else float("-inf")
            b_r = b[j]   if j < n else float("inf")
            if a_l <= b_r and b_l <= a_r:
                if (m + n) % 2: return max(a_l, b_l)
                return (max(a_l, b_l) + min(a_r, b_r)) / 2
            elif a_l > b_r: hi = i - 1
            else: lo = i + 1

Find Median From a Data Stream HARD

Problem: Return the running median after each number arrives, maintained with a max-heap (low half) and min-heap (high half).
def running_median(nums):
    import heapq
    low, high, out = [], [], []
    for n in nums:
        heapq.heappush(low, -n)
        heapq.heappush(high, -heapq.heappop(low))
        if len(high) > len(low):
            heapq.heappush(low, -heapq.heappop(high))
        if len(low) > len(high):
            out.append(float(-low[0]))
        else:
            out.append((-low[0] + high[0]) / 2)
    return out

Merge K Sorted Lists HARD

Problem: Merge k already-sorted lists into one sorted list using a min-heap of the current heads.
def merge_k_sorted(lists):
    import heapq
    heap = []
    for i, lst in enumerate(lists):
        if lst:
            heapq.heappush(heap, (lst[0], i, 0))
    out = []
    while heap:
        val, i, j = heapq.heappop(heap)
        out.append(val)
        if j + 1 < len(lists[i]):
            heapq.heappush(heap, (lists[i][j + 1], i, j + 1))
    return out

Kth Smallest Element in a Sorted Matrix HARD

Problem: Each row and column is sorted ascending. Return the kth smallest value using a heap of row heads.
def kth_smallest_matrix(matrix, k):
    import heapq
    n = len(matrix)
    heap = [(matrix[r][0], r, 0) for r in range(n)]
    heapq.heapify(heap)
    val = None
    for _ in range(k):
        val, r, c = heapq.heappop(heap)
        if c + 1 < len(matrix[r]):
            heapq.heappush(heap, (matrix[r][c + 1], r, c + 1))
    return val

Smallest Range Covering K Lists HARD

Problem: Given k sorted lists, return the smallest range [a,b] that includes at least one number from each list.
def smallest_range(nums):
    import heapq
    heap = [(lst[0], i, 0) for i, lst in enumerate(nums)]
    heapq.heapify(heap)
    cur_max = max(lst[0] for lst in nums)
    best = [heap[0][0], cur_max]
    while True:
        val, i, j = heapq.heappop(heap)
        if cur_max - val < best[1] - best[0]:
            best = [val, cur_max]
        if j + 1 == len(nums[i]):
            break
        nxt = nums[i][j + 1]
        cur_max = max(cur_max, nxt)
        heapq.heappush(heap, (nxt, i, j + 1))
    return best

Sliding Window Median HARD

Problem: Return the median of every contiguous window of size k as it slides across the array.
def median_sliding_window(nums, k):
    import bisect
    window = sorted(nums[:k])
    out = []
    for i in range(k, len(nums) + 1):
        if k % 2:
            out.append(float(window[k // 2]))
        else:
            out.append((window[k // 2 - 1] + window[k // 2]) / 2)
        if i < len(nums):
            window.pop(bisect.bisect_left(window, nums[i - k]))
            bisect.insort(window, nums[i])
    return out

Minimum Cost to Hire K Workers HARD

Problem: Each worker has quality and a minimum wage expectation. Pay must be proportional to quality and meet each worker's minimum. Return the minimum cost to hire exactly k workers.
def min_cost_hire(quality, wage, k):
    import heapq
    workers = sorted(zip(wage, quality), key=lambda w: w[0] / w[1])
    heap = []
    total_q = 0
    best = float('inf')
    for w, q in workers:
        heapq.heappush(heap, -q)
        total_q += q
        if len(heap) > k:
            total_q += heapq.heappop(heap)
        if len(heap) == k:
            best = min(best, total_q * (w / q))
    return round(best, 5)

Single-Threaded CPU HARD

Problem: Tasks are [enqueue_time, processing_time]. The CPU always picks the available task with the shortest processing time (ties by index). Return the processing order.
def get_task_order(tasks):
    import heapq
    indexed = sorted(range(len(tasks)), key=lambda i: tasks[i][0])
    heap = []
    order = []
    time = 0
    i = 0
    while i < len(indexed) or heap:
        if not heap and time < tasks[indexed[i]][0]:
            time = tasks[indexed[i]][0]
        while i < len(indexed) and tasks[indexed[i]][0] <= time:
            idx = indexed[i]
            heapq.heappush(heap, (tasks[idx][1], idx))
            i += 1
        proc, idx = heapq.heappop(heap)
        time += proc
        order.append(idx)
    return order

Maximum Performance of a Team HARD

Problem: A team's performance is (sum of its members' speeds) x (minimum efficiency among them). Choose at most k engineers to maximize performance, modulo 1e9+7.
def max_performance(n, speed, efficiency, k):
    import heapq
    workers = sorted(zip(efficiency, speed), reverse=True)
    heap = []
    speed_sum = 0
    best = 0
    for eff, spd in workers:
        heapq.heappush(heap, spd)
        speed_sum += spd
        if len(heap) > k:
            speed_sum -= heapq.heappop(heap)
        best = max(best, speed_sum * eff)
    return best % (10**9 + 7)

Minimize Deviation in Array HARD

Problem: You may double any odd number and halve any even number, any number of times. Return the minimum possible difference between the largest and smallest elements.
def minimum_deviation(nums):
    import heapq
    heap = [-(n * 2 if n % 2 else n) for n in nums]
    heapq.heapify(heap)
    low = -max(heap)
    best = float('inf')
    while True:
        x = -heapq.heappop(heap)
        best = min(best, x - low)
        if x % 2:
            break
        x //= 2
        low = min(low, x)
        heapq.heappush(heap, -x)
    return best

Rearrange String K Distance Apart HARD

Problem: Rearrange the string so identical characters are at least k apart. Return a valid arrangement, or '' if impossible.
def rearrange_k_distance(s, k):
    import heapq
    from collections import Counter, deque
    if k <= 1:
        return s
    heap = [(-c, ch) for ch, c in Counter(s).items()]
    heapq.heapify(heap)
    wait = deque()
    out = []
    while heap:
        c, ch = heapq.heappop(heap)
        out.append(ch)
        wait.append((c + 1, ch))
        if len(wait) >= k:
            cnt, chr_ = wait.popleft()
            if cnt < 0:
                heapq.heappush(heap, (cnt, chr_))
    return ''.join(out) if len(out) == len(s) else ''

Recursion & Backtracking

Factorial (Recursive) EASY

Problem: Compute n! recursively (0! = 1).
def factorial(n):
    if n <= 1:
        return 1
    return n * factorial(n - 1)

Fibonacci Number EASY

Problem: Return the nth Fibonacci number (fib(0)=0, fib(1)=1) using memoized recursion.
def fibonacci(n, memo=None):
    if memo is None:
        memo = {}
    if n < 2:
        return n
    if n in memo:
        return memo[n]
    memo[n] = fibonacci(n - 1, memo) + fibonacci(n - 2, memo)
    return memo[n]

Sum From 1 to N (Recursive) EASY

Problem: Return the sum 1 + 2 + ... + n using recursion.
def sum_to_n(n):
    if n <= 0:
        return 0
    return n + sum_to_n(n - 1)

Power Function (Recursive) EASY

Problem: Compute base raised to a non-negative integer exponent using recursion.
def power(base, exp):
    if exp == 0:
        return 1
    return base * power(base, exp - 1)

Reverse a String Recursively EASY

Problem: Reverse a string using recursion (no slicing tricks in the base logic).
def reverse_string(s):
    if len(s) <= 1:
        return s
    return reverse_string(s[1:]) + s[0]

Greatest Common Divisor EASY

Problem: Compute gcd(a, b) using the recursive Euclidean algorithm.
def gcd(a, b):
    if b == 0:
        return a
    return gcd(b, a % b)

Count Digits (Recursive) EASY

Problem: Count how many digits are in a non-negative integer using recursion.
def count_digits(n):
    if n < 10:
        return 1
    return 1 + count_digits(n // 10)

Sum of Digits (Recursive) EASY

Problem: Return the sum of the digits of a non-negative integer using recursion.
def sum_of_digits(n):
    if n < 10:
        return n
    return n % 10 + sum_of_digits(n // 10)

Recursive Palindrome Check EASY

Problem: Return True if the string is a palindrome, checked recursively.
def is_palindrome_recursion(s):
    if len(s) <= 1:
        return True
    if s[0] != s[-1]:
        return False
    return is_palindrome_recursion(s[1:-1])

Sum of an Array (Recursive) EASY

Problem: Return the sum of a list of numbers using recursion.
def array_sum_recursion(nums, i=0):
    if i == len(nums):
        return 0
    return nums[i] + array_sum_recursion(nums, i + 1)

Permutations MEDIUM

class Solution:
    def permutations(self, arr):
        if len(arr) <= 1: return [arr[:]]
        res = []
        for i in range(len(arr)):
            rest = arr[:i] + arr[i+1:]
            for p in self.permutations(rest):
                res.append([arr[i]] + p)
        return res

Powerset (all subsets) MEDIUM

class Solution:
    def powerset(self, arr):
        subsets = [[]]
        for n in arr:
            subsets += [s + [n] for s in subsets]
        return subsets
    # [1,2] -> [[], [1], [2], [1,2]]

Phone Number Mnemonics MEDIUM

class Solution:
    def phone_mnemonics(self, digits):
        keys = {"2":"abc","3":"def","4":"ghi","5":"jkl","6":"mno",
                "7":"pqrs","8":"tuv","9":"wxyz","0":"0","1":"1"}
        res = []
        def backtrack(i, cur):
            if i == len(digits):
                res.append("".join(cur)); return
            for ch in keys[digits[i]]:
                cur.append(ch); backtrack(i+1, cur); cur.pop()
        backtrack(0, [])
        return res

Staircase Traversal MEDIUM

Ways to climb height stairs taking 1..maxSteps at a time. DP. O(n·k).

class Solution:
    def staircase(self, height, max_steps):
        ways = [1] + [0] * height
        for h in range(1, height + 1):
            for step in range(1, min(h, max_steps) + 1):
                ways[h] += ways[h - step]
        return ways[height]

Generate Div Tags (balanced) MEDIUM

All valid arrangements of n <div></div> pairs (same idea as "generate parentheses").

class Solution:
    def generate_div_tags(self, n):
        res = []
        def build(open_used, close_used, cur):
            if open_used < n:
                build(open_used + 1, close_used, cur + "<div>")
            if close_used < open_used:
                build(open_used, close_used + 1, cur + "</div>")
            if close_used == n:
                res.append(cur)
        build(0, 0, "")
        return res

Subsets MEDIUM

Problem: Return all possible subsets (the power set) of a list of distinct integers.
def subsets(nums):
    res = []
    def backtrack(start, path):
        res.append(path[:])
        for i in range(start, len(nums)):
            path.append(nums[i])
            backtrack(i + 1, path)
            path.pop()
    backtrack(0, [])
    return res

Permutations MEDIUM

Problem: Return all permutations of a list of distinct integers.
def permutations_recursion(nums):
    res = []
    def backtrack(path, remaining):
        if not remaining:
            res.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 res

Combinations MEDIUM

Problem: Return all combinations of k numbers chosen from the range 1..n.
def combine_recursion(n, k):
    res = []
    def backtrack(start, path):
        if len(path) == k:
            res.append(path[:])
            return
        for i in range(start, n + 1):
            path.append(i)
            backtrack(i + 1, path)
            path.pop()
    backtrack(1, [])
    return res

Combination Sum MEDIUM

Problem: Given distinct candidates and a target, return all unique combinations that sum to target. Each candidate may be reused.
def combination_sum_recursion(candidates, target):
    res = []
    def backtrack(start, path, remaining):
        if remaining == 0:
            res.append(path[:])
            return
        for i in range(start, len(candidates)):
            if candidates[i] <= remaining:
                path.append(candidates[i])
                backtrack(i, path, remaining - candidates[i])
                path.pop()
    backtrack(0, [], target)
    return res

Generate Parentheses MEDIUM

Problem: Given n pairs of parentheses, generate all combinations of well-formed parentheses.
def generate_parentheses(n):
    res = []
    def backtrack(s, open_count, close_count):
        if len(s) == 2 * n:
            res.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 res

Letter Combinations of a Phone Number MEDIUM

Problem: Given digits 2-9, return all letter combinations the number could spell (like on a phone keypad).
def letter_combinations_recursion(digits):
    if not digits:
        return []
    mapping = {'2':'abc','3':'def','4':'ghi','5':'jkl','6':'mno','7':'pqrs','8':'tuv','9':'wxyz'}
    res = []
    def backtrack(i, path):
        if i == len(digits):
            res.append(path)
            return
        for c in mapping[digits[i]]:
            backtrack(i + 1, path + c)
    backtrack(0, '')
    return res

Subsets II (With Duplicates) MEDIUM

Problem: Return all unique subsets of a list that may contain duplicate integers.
def subsets_with_dup(nums):
    nums.sort()
    res = []
    def backtrack(start, path):
        res.append(path[:])
        for i in range(start, len(nums)):
            if i > start and nums[i] == nums[i-1]:
                continue
            path.append(nums[i])
            backtrack(i + 1, path)
            path.pop()
    backtrack(0, [])
    return res

Permutations II (With Duplicates) MEDIUM

Problem: Return all unique permutations of a list that may contain duplicate integers.
def permute_unique(nums):
    nums.sort()
    res = []
    used = [False] * len(nums)
    def backtrack(path):
        if len(path) == len(nums):
            res.append(path[:])
            return
        for i in range(len(nums)):
            if used[i] or (i > 0 and nums[i] == nums[i-1] and not used[i-1]):
                continue
            used[i] = True
            path.append(nums[i])
            backtrack(path)
            path.pop()
            used[i] = False
    backtrack([])
    return res

Combination Sum II MEDIUM

Problem: Given candidates (possibly with duplicates) and a target, return all unique combinations summing to target. Each number is used at most once.
def combination_sum2(candidates, target):
    candidates.sort()
    res = []
    def backtrack(start, path, remaining):
        if remaining == 0:
            res.append(path[:])
            return
        for i in range(start, len(candidates)):
            if i > start and candidates[i] == candidates[i-1]:
                continue
            if candidates[i] > remaining:
                break
            path.append(candidates[i])
            backtrack(i + 1, path, remaining - candidates[i])
            path.pop()
    backtrack(0, [], target)
    return res

Restore IP Addresses MEDIUM

Problem: Given a string of digits, return all valid IP addresses that can be formed by inserting dots (each part 0-255, no leading zeros).
def restore_ip_addresses(s):
    res = []
    def backtrack(start, parts):
        if len(parts) == 4:
            if start == len(s):
                res.append('.'.join(parts))
            return
        for length in range(1, 4):
            if start + length > len(s):
                break
            part = s[start:start+length]
            if (part[0] == '0' and len(part) > 1) or int(part) > 255:
                continue
            backtrack(start + length, parts + [part])
    backtrack(0, [])
    return res

Solve Sudoku HARD

Fill the grid by trying digits and backtracking when stuck. 0 = empty.

class Solution:
    def solve_sudoku(self, board):
        def valid(r, c, val):
            for i in range(9):
                if board[r][i] == val or board[i][c] == val: return False
            br, bc = 3 * (r // 3), 3 * (c // 3)
            for i in range(br, br + 3):
                for j in range(bc, bc + 3):
                    if board[i][j] == val: return False
            return True
        def solve():
            for r in range(9):
                for c in range(9):
                    if board[r][c] == 0:
                        for val in range(1, 10):
                            if valid(r, c, val):
                                board[r][c] = val
                                if solve(): return True
                                board[r][c] = 0      # backtrack
                        return False
            return True
        solve()
        return board

Count solutions HARD

Place N queens so none attack each other. Backtrack column by column, tracking used columns & diagonals.

class Solution:
    def non_attacking_queens(self, n):
        cols = set(); diag1 = set(); diag2 = set()
        def place(row):
            if row == n: return 1
            count = 0
            for col in range(n):
                if col in cols or (row+col) in diag1 or (row-col) in diag2:
                    continue
                cols.add(col); diag1.add(row+col); diag2.add(row-col)
                count += place(row + 1)
                cols.discard(col); diag1.discard(row+col); diag2.discard(row-col)
            return count
        return place(0)

N-Queens (Count Solutions) HARD

Problem: Return the number of distinct ways to place n queens on an n x n board so none attack each other.
def total_n_queens_recursion(n):
    cols = set()
    diag1 = set()
    diag2 = set()
    def backtrack(row):
        if row == n:
            return 1
        count = 0
        for col in range(n):
            if col in cols or (row - col) in diag1 or (row + col) in diag2:
                continue
            cols.add(col); diag1.add(row - col); diag2.add(row + col)
            count += backtrack(row + 1)
            cols.remove(col); diag1.remove(row - col); diag2.remove(row + col)
        return count
    return backtrack(0)

Word Search HARD

Problem: Given a grid of letters, return True if the word can be spelled by moving to adjacent cells (up/down/left/right) without reusing a cell.
def word_search(board, word):
    rows, cols = len(board), len(board[0])
    def dfs(r, c, i):
        if i == len(word):
            return True
        if r < 0 or r >= rows or c < 0 or c >= cols or board[r][c] != word[i]:
            return False
        tmp = board[r][c]
        board[r][c] = '#'
        found = (dfs(r+1, c, i+1) or dfs(r-1, c, i+1) or
                 dfs(r, c+1, i+1) or dfs(r, c-1, i+1))
        board[r][c] = tmp
        return found
    for r in range(rows):
        for c in range(cols):
            if dfs(r, c, 0):
                return True
    return False

Palindrome Partitioning HARD

Problem: Partition the string so every substring is a palindrome. Return all possible partitionings.
def palindrome_partition(s):
    res = []
    def is_pal(sub):
        return sub == sub[::-1]
    def backtrack(start, path):
        if start == len(s):
            res.append(path[:])
            return
        for end in range(start + 1, len(s) + 1):
            sub = s[start:end]
            if is_pal(sub):
                path.append(sub)
                backtrack(end, path)
                path.pop()
    backtrack(0, [])
    return res

Combination Sum III HARD

Problem: Find all combinations of k distinct numbers from 1..9 that sum to n (each number used at most once).
def combination_sum3(k, n):
    res = []
    def backtrack(start, path, remaining):
        if len(path) == k:
            if remaining == 0:
                res.append(path[:])
            return
        for i in range(start, 10):
            if i > remaining:
                break
            path.append(i)
            backtrack(i + 1, path, remaining - i)
            path.pop()
    backtrack(1, [], n)
    return res

Permutation Sequence HARD

Problem: Return the kth permutation (1-indexed) of the numbers 1..n in lexicographic order.
def get_permutation(n, k):
    import math
    numbers = list(range(1, n + 1))
    k -= 1
    result = []
    for i in range(n, 0, -1):
        fact = math.factorial(i - 1)
        idx = k // fact
        result.append(str(numbers.pop(idx)))
        k %= fact
    return ''.join(result)

Gray Code HARD

Problem: Return an n-bit gray code sequence: a list of 2^n integers where consecutive values (and the last and first) differ by exactly one bit.
def gray_code(n):
    res = [0]
    for i in range(n):
        res += [x | (1 << i) for x in reversed(res)]
    return res

Beautiful Arrangement HARD

Problem: Count the permutations of 1..n where for every position i (1-indexed), either the value is divisible by i or i is divisible by the value.
def count_arrangement(n):
    used = [False] * (n + 1)
    def backtrack(pos):
        if pos > n:
            return 1
        count = 0
        for num in range(1, n + 1):
            if not used[num] and (num % pos == 0 or pos % num == 0):
                used[num] = True
                count += backtrack(pos + 1)
                used[num] = False
        return count
    return backtrack(1)

Partition to K Equal Sum Subsets HARD

Problem: Return True if the array can be partitioned into k non-empty subsets that all have the same sum.
def can_partition_k_subsets(nums, k):
    total = sum(nums)
    if total % k != 0:
        return False
    target = total // k
    nums.sort(reverse=True)
    if nums[0] > target:
        return False
    used = [False] * len(nums)
    def backtrack(count, current, start):
        if count == k:
            return True
        if current == target:
            return backtrack(count + 1, 0, 0)
        for i in range(start, len(nums)):
            if used[i] or current + nums[i] > target:
                continue
            used[i] = True
            if backtrack(count, current + nums[i], i + 1):
                return True
            used[i] = False
        return False
    return backtrack(0, 0, 0)

Matchsticks to Square HARD

Problem: Given matchstick lengths, return True if they can all be used to form the four equal sides of a square.
def makesquare(matchsticks):
    total = sum(matchsticks)
    if total % 4 != 0:
        return False
    side = total // 4
    matchsticks.sort(reverse=True)
    if matchsticks[0] > side:
        return False
    sides = [0] * 4
    def backtrack(i):
        if i == len(matchsticks):
            return True
        for j in range(4):
            if sides[j] + matchsticks[i] <= side:
                sides[j] += matchsticks[i]
                if backtrack(i + 1):
                    return True
                sides[j] -= matchsticks[i]
            if sides[j] == 0:
                break
        return False
    return backtrack(0)

Letter Case Permutation HARD

Problem: Given a string of letters and digits, return all strings formed by toggling the case of each letter in every possible way.
def letter_case_permutation(s):
    res = []
    def backtrack(i, path):
        if i == len(s):
            res.append(path)
            return
        if s[i].isalpha():
            backtrack(i + 1, path + s[i].lower())
            backtrack(i + 1, path + s[i].upper())
        else:
            backtrack(i + 1, path + s[i])
    backtrack(0, '')
    return res

Dynamic Programming

Climbing Stairs EASY

Problem: You can climb 1 or 2 steps at a time. Return the number of distinct ways to reach the top of n stairs.
def climbing_stairs(n):
    if n <= 2:
        return n
    a, b = 1, 2
    for _ in range(3, n + 1):
        a, b = b, a + b
    return b

Fibonacci Number (Bottom-Up) EASY

Problem: Return the nth Fibonacci number using bottom-up dynamic programming (fib(0)=0, fib(1)=1).
def fib_dp(n):
    if n < 2:
        return n
    a, b = 0, 1
    for _ in range(2, n + 1):
        a, b = b, a + b
    return b

N-th Tribonacci Number EASY

Problem: The Tribonacci sequence starts 0, 1, 1 and each term is the sum of the previous three. Return the nth term.
def tribonacci(n):
    if n == 0:
        return 0
    if n <= 2:
        return 1
    a, b, c = 0, 1, 1
    for _ in range(3, n + 1):
        a, b, c = b, c, a + b + c
    return c

Min Cost Climbing Stairs EASY

Problem: Each stair has a cost to step on. You may start at index 0 or 1 and climb 1 or 2 steps. Return the minimum cost to reach the top (past the last stair).
def min_cost_climbing_stairs(cost):
    a, b = 0, 0
    for i in range(2, len(cost) + 1):
        a, b = b, min(b + cost[i-1], a + cost[i-2])
    return b

House Robber EASY

Problem: Given house values along a street, return the maximum you can rob without robbing two adjacent houses.
def house_robber(nums):
    prev, curr = 0, 0
    for n in nums:
        prev, curr = curr, max(curr, prev + n)
    return curr

Is Subsequence EASY

Problem: Return True if s is a subsequence of t (its characters appear in order within t).
def is_subsequence_dp(s, t):
    i = 0
    for c in t:
        if i < len(s) and s[i] == c:
            i += 1
    return i == len(s)

Pascal's Triangle Row EASY

Problem: Return the row at the given 0-based index of Pascal's triangle.
def pascal_row(row_index):
    row = [1]
    for _ in range(row_index):
        row = [1] + [row[i] + row[i+1] for i in range(len(row)-1)] + [1]
    return row

Unique Paths EASY

Problem: A robot moves only right or down on an m x n grid from the top-left to the bottom-right. Return the number of unique paths.
def unique_paths(m, n):
    dp = [1] * n
    for _ in range(1, m):
        for j in range(1, n):
            dp[j] += dp[j-1]
    return dp[-1]

Minimum Path Sum EASY

Problem: Given a grid of non-negative numbers, return the minimum sum along a path from the top-left to the bottom-right, moving only right or down.
def min_path_sum_dp(grid):
    rows, cols = len(grid), len(grid[0])
    dp = [float('inf')] * cols
    dp[0] = 0
    for r in range(rows):
        dp[0] += grid[r][0]
        for c in range(1, cols):
            dp[c] = min(dp[c], dp[c-1]) + grid[r][c]
    return dp[-1]

Maximum Subarray (DP) EASY

Problem: Return the largest sum of any contiguous subarray using Kadane's dynamic programming.
def max_subarray_dp(nums):
    best = current = nums[0]
    for n in nums[1:]:
        current = max(n, current + n)
        best = max(best, current)
    return best

Dynamic Programming - Fibonacci MEDIUM

Problem: the nth Fibonacci number - fast.

Pattern: naive recursion is O(2ⁿ). Memoize (cache) results to make it O(n).

def fib(n, memo={}):
    if n <= 1:
        return n
    if n in memo:
        return memo[n]
    memo[n] = fib(n - 1, memo) + fib(n - 2, memo)
    return memo[n]

Min Number Of Coins For Change MEDIUM

class Solution:
    def min_coins(self, n, denoms):
        dp = [float("inf")] * (n + 1)
        dp[0] = 0
        for coin in denoms:
            for amount in range(coin, n + 1):
                dp[amount] = min(dp[amount], dp[amount - coin] + 1)
        return dp[n] if dp[n] != float("inf") else -1

Number Of Ways To Make Change MEDIUM

class Solution:
    def ways_to_make_change(self, n, denoms):
        dp = [0] * (n + 1)
        dp[0] = 1
        for coin in denoms:
            for amount in range(coin, n + 1):
                dp[amount] += dp[amount - coin]
        return dp[n]

Max Subset Sum No Adjacent MEDIUM

class Solution:
    def max_subset_no_adjacent(self, arr):
        if not arr: return 0
        prev, cur = 0, arr[0]
        for n in arr[1:]:
            prev, cur = cur, max(cur, prev + n)
        return cur

Levenshtein Distance (edit distance) MEDIUM

class Solution:
    def levenshtein(self, a, b):
        dp = [[0]*(len(b)+1) for _ in range(len(a)+1)]
        for i in range(len(a)+1): dp[i][0] = i
        for j in range(len(b)+1): dp[0][j] = j
        for i in range(1, len(a)+1):
            for j in range(1, len(b)+1):
                if a[i-1] == b[j-1]:
                    dp[i][j] = dp[i-1][j-1]
                else:
                    dp[i][j] = 1 + min(dp[i-1][j], dp[i][j-1], dp[i-1][j-1])
        return dp[-1][-1]

Max Sum Increasing Subsequence MEDIUM

class Solution:
    def max_sum_increasing(self, arr):
        sums = arr[:]
        for i in range(len(arr)):
            for j in range(i):
                if arr[j] < arr[i] and sums[j] + arr[i] > sums[i]:
                    sums[i] = sums[j] + arr[i]
        return max(sums)

Number Of Ways To Traverse Graph (grid) MEDIUM

Paths from top-left to bottom-right moving only right/down. O(w·h).

class Solution:
    def num_ways(self, width, height):
        dp = [[1] * width for _ in range(height)]
        for r in range(1, height):
            for c in range(1, width):
                dp[r][c] = dp[r-1][c] + dp[r][c-1]
        return dp[height-1][width-1]

Coin Change (Number of Combinations) MEDIUM

Problem: Given coin denominations and a target amount, return the number of distinct combinations that make up the amount.
def change_dp(amount, coins):
    dp = [0] * (amount + 1)
    dp[0] = 1
    for coin in coins:
        for a in range(coin, amount + 1):
            dp[a] += dp[a - coin]
    return dp[amount]

Word Break MEDIUM

Problem: Return True if the string can be segmented into a space-separated sequence of words from the given dictionary.
def word_break_dp(s, word_dict):
    words = set(word_dict)
    dp = [False] * (len(s) + 1)
    dp[0] = True
    for i in range(1, len(s) + 1):
        for j in range(i):
            if dp[j] and s[j:i] in words:
                dp[i] = True
                break
    return dp[len(s)]

Decode Ways MEDIUM

Problem: A message of digits is decoded with 'A'->1 ... 'Z'->26. Return the number of ways to decode the string.
def num_decodings(s):
    if not s or s[0] == '0':
        return 0
    prev, curr = 1, 1
    for i in range(1, len(s)):
        temp = 0
        if s[i] != '0':
            temp += curr
        if 10 <= int(s[i-1:i+1]) <= 26:
            temp += prev
        prev, curr = curr, temp
    return curr

Longest Palindromic Substring MEDIUM

Problem: Return the longest contiguous substring of s that is a palindrome (expand around each center).
def longest_palindrome_dp(s):
    if not s:
        return ''
    start, end = 0, 0
    def expand(left, right):
        while left >= 0 and right < len(s) and s[left] == s[right]:
            left -= 1
            right += 1
        return left + 1, right - 1
    for i in range(len(s)):
        l1, r1 = expand(i, i)
        l2, r2 = expand(i, i + 1)
        if r1 - l1 > end - start:
            start, end = l1, r1
        if r2 - l2 > end - start:
            start, end = l2, r2
    return s[start:end + 1]

Numbers In Pi HARD

Fewest spaces to break a digit string into "known" numbers. Memoized recursion. O(n³).

class Solution:
    def numbers_in_pi(self, pi, numbers):
        known = set(numbers); cache = {}
        def helper(idx):
            if idx == len(pi): return -1          # -1 cancels the last +1
            if idx in cache: return cache[idx]
            best = float("inf")
            for i in range(idx, len(pi)):
                if pi[idx:i+1] in known:
                    best = min(best, 1 + helper(i + 1))
            cache[idx] = best
            return best
        result = helper(0)
        return -1 if result == float("inf") else result

Max Profit With K Transactions (stocks) HARD

class Solution:
    def max_profit_k(self, prices, k):
        if not prices: return 0
        dp = [0] * len(prices)
        for _ in range(k):
            max_diff = -prices[0]; new = [0] * len(prices)
            for d in range(1, len(prices)):
                new[d] = max(new[d-1], prices[d] + max_diff)
                max_diff = max(max_diff, dp[d] - prices[d])
            dp = new
        return dp[-1]

Dice Throws (ways to reach a target) HARD

class Solution:
    def dice_throws(self, num_dice, num_sides, target):
        dp = [[0] * (target + 1) for _ in range(num_dice + 1)]
        dp[0][0] = 1
        for d in range(1, num_dice + 1):
            for t in range(1, target + 1):
                for s in range(1, min(t, num_sides) + 1):
                    dp[d][t] += dp[d-1][t-s]
        return dp[num_dice][target]

Disk Stacking HARD

Tallest stack of disks where each must be strictly smaller in all 3 dimensions. DP after sorting by height. O(n²).

class Solution:
    def disk_stacking(self, disks):
        disks.sort(key=lambda d: d[2])         # by height
        heights = [d[2] for d in disks]
        seq = [None] * len(disks); max_i = 0
        for i in range(len(disks)):
            for j in range(i):
                if all(disks[j][k] < disks[i][k] for k in range(3)):
                    if heights[j] + disks[i][2] > heights[i]:
                        heights[i] = heights[j] + disks[i][2]; seq[i] = j
            if heights[i] >= heights[max_i]: max_i = i
        stack = []; i = max_i
        while i is not None:
            stack.append(disks[i]); i = seq[i]
        return stack[::-1]

Interweaving Strings HARD

Is three an interleaving of one and two (keeping each one's order)? Memoized recursion. O(n·m).

class Solution:
    def interweaving(self, one, two, three):
        if len(one) + len(two) != len(three): return False
        cache = {}
        def helper(i, j):
            if i == len(one) and j == len(two): return True
            if (i, j) in cache: return cache[(i, j)]
            res = False
            k = i + j
            if i < len(one) and one[i] == three[k]: res = helper(i + 1, j)
            if not res and j < len(two) and two[j] == three[k]: res = helper(i, j + 1)
            cache[(i, j)] = res
            return res
        return helper(0, 0)

Longest String Chain HARD

Longest chain where each word becomes the next by adding one letter. Sort by length, DP. O(n·L²).

class Solution:
    def longest_string_chain(self, words):
        words.sort(key=len)
        best = {}; longest = 1
        for w in words:
            best[w] = 1
            for i in range(len(w)):
                pred = w[:i] + w[i+1:]          # remove one char
                if pred in best:
                    best[w] = max(best[w], best[pred] + 1)
            longest = max(longest, best[w])
        return longest

Palindrome Partitioning Min Cuts HARD

Fewest cuts so every piece is a palindrome. Precompute palindromes, then DP. O(n²).

class Solution:
    def palindrome_min_cuts(self, s):
        n = len(s)
        is_pal = [[False] * n for _ in range(n)]
        for i in range(n): is_pal[i][i] = True
        for length in range(2, n + 1):
            for i in range(n - length + 1):
                j = i + length - 1
                if s[i] == s[j] and (length == 2 or is_pal[i+1][j-1]):
                    is_pal[i][j] = True
        cuts = [0] * n
        for i in range(n):
            if is_pal[0][i]:
                cuts[i] = 0
            else:
                cuts[i] = min(cuts[j] + 1 for j in range(i) if is_pal[j+1][i])
        return cuts[-1]

Longest Common Subsequence HARD

class Solution:
    def lcs(self, a, b):
        dp = [[0] * (len(b) + 1) for _ in range(len(a) + 1)]
        for i in range(1, len(a) + 1):
            for j in range(1, len(b) + 1):
                if a[i-1] == b[j-1]:
                    dp[i][j] = dp[i-1][j-1] + 1
                else:
                    dp[i][j] = max(dp[i-1][j], dp[i][j-1])
        return dp[-1][-1]

Longest Increasing Subsequence HARD

class Solution:
    def lis(self, arr):
        if not arr: return 0
        dp = [1] * len(arr)
        for i in range(len(arr)):
            for j in range(i):
                if arr[j] < arr[i]:
                    dp[i] = max(dp[i], dp[j] + 1)
        return max(dp)

0/1 Knapsack HARD

Maximize value within a weight capacity. Classic 2-D DP. O(n·capacity).

class Solution:
    def knapsack(self, items, capacity):
        # items = [[value, weight], ...]
        dp = [[0] * (capacity + 1) for _ in range(len(items) + 1)]
        for i in range(1, len(items) + 1):
            value, weight = items[i-1]
            for c in range(capacity + 1):
                if weight > c:
                    dp[i][c] = dp[i-1][c]
                else:
                    dp[i][c] = max(dp[i-1][c], dp[i-1][c - weight] + value)
        return dp[-1][-1]

Greedy

Array Partition (Max Sum of Min Pairs) EASY

Problem: Split 2n integers into n pairs so the sum of the smaller value in each pair is as large as possible. Return that sum.
def array_pair_sum(nums):
    nums.sort()
    return sum(nums[::2])

Maximum Units on a Truck EASY

Problem: Each box type is [count, units_per_box]. Load at most truck_size boxes to maximize total units.
def maximum_units(boxes, truck_size):
    boxes.sort(key=lambda b: -b[1])
    total = 0
    for count, units in boxes:
        take = min(count, truck_size)
        total += take * units
        truck_size -= take
        if truck_size == 0:
            break
    return total

Largest Perimeter Triangle EASY

Problem: Return the largest perimeter of a triangle with three of these side lengths, or 0 if impossible.
def largest_perimeter(nums):
    nums.sort(reverse=True)
    for i in range(len(nums) - 2):
        if nums[i] < nums[i + 1] + nums[i + 2]:
            return nums[i] + nums[i + 1] + nums[i + 2]
    return 0

Can Place Flowers EASY

Problem: A flowerbed (0=empty, 1=planted) allows no two adjacent flowers. Return True if you can plant n more.
def can_place_flowers(bed, n):
    bed = [0] + bed + [0]
    for i in range(1, len(bed) - 1):
        if bed[i - 1] == 0 and bed[i] == 0 and bed[i + 1] == 0:
            bed[i] = 1
            n -= 1
    return n <= 0

Lemonade Change EASY

Problem: Customers pay with 5, 10, or 20 bills for a 5-dollar lemonade. Return True if you can give correct change to everyone in order.
def lemonade_change(bills):
    five = ten = 0
    for b in bills:
        if b == 5:
            five += 1
        elif b == 10:
            if five == 0:
                return False
            five -= 1; ten += 1
        else:
            if ten and five:
                ten -= 1; five -= 1
            elif five >= 3:
                five -= 3
            else:
                return False
    return True

Distribute Candies EASY

Problem: Given candies labeled by type, a sister may eat only half of them. Return the maximum number of distinct types she can keep.
def distribute_candies(candy_type):
    return min(len(set(candy_type)), len(candy_type) // 2)

Best Time to Buy and Sell Stock II EASY

Problem: You may buy and sell as many times as you like. Return the maximum total profit from the price series.
def max_profit_greedy(prices):
    profit = 0
    for i in range(1, len(prices)):
        if prices[i] > prices[i - 1]:
            profit += prices[i] - prices[i - 1]
    return profit

Assign Cookies EASY

Problem: Each child i needs a cookie of size >= greed[i]. Match cookies to children to maximize the number of content children.
def find_content_children(greed, sizes):
    greed.sort(); sizes.sort()
    i = j = 0
    while i < len(greed) and j < len(sizes):
        if sizes[j] >= greed[i]:
            i += 1
        j += 1
    return i

Maximize Sum After K Negations EASY

Problem: You may negate any element up to k times (an element can be negated more than once). Maximize the array's sum.
def largest_sum_after_k_negations(nums, k):
    nums.sort()
    i = 0
    while k > 0 and i < len(nums) and nums[i] < 0:
        nums[i] = -nums[i]
        i += 1; k -= 1
    if k % 2 == 1:
        nums.sort()
        nums[0] = -nums[0]
    return sum(nums)

Split a String in Balanced Strings EASY

Problem: A balanced string has equal numbers of 'L' and 'R'. Return the maximum number of balanced pieces you can split s into.
def balanced_string_split(s):
    balance = count = 0
    for c in s:
        balance += 1 if c == 'R' else -1
        if balance == 0:
            count += 1
    return count

Class Photos / Tandem Bicycle / Non-Constructible Change

class Solution:
    # Non-Constructible Change: smallest amount you CAN'T make
    def non_constructible_change(self, coins):
        coins.sort()
        change = 0
        for c in coins:
            if c > change + 1: break
            change += c
        return change + 1

    # Tandem Bicycle: pair fastest with slowest (fastest total speed)
    def tandem_bicycle(self, red, blue, fastest=True):
        red.sort(); blue.sort()
        if fastest: blue.reverse()
        return sum(max(r, b) for r, b in zip(red, blue))

Jump Game MEDIUM

Problem: Each value is your maximum jump length from that index. Return True if you can reach the last index.
def can_jump_greedy(nums):
    reach = 0
    for i, n in enumerate(nums):
        if i > reach:
            return False
        reach = max(reach, i + n)
    return True

Jump Game II MEDIUM

Problem: Each value is your maximum jump length. Return the minimum number of jumps to reach the last index.
def jump_greedy(nums):
    jumps = cur_end = farthest = 0
    for i in range(len(nums) - 1):
        farthest = max(farthest, i + nums[i])
        if i == cur_end:
            jumps += 1
            cur_end = farthest
    return jumps

Gas Station MEDIUM

Problem: gas[i] fuel is available at station i and it costs cost[i] to reach the next. Return the start index of a full circular trip, or -1.
def can_complete_circuit_greedy(gas, cost):
    if sum(gas) < sum(cost):
        return -1
    total = start = 0
    for i in range(len(gas)):
        total += gas[i] - cost[i]
        if total < 0:
            total = 0
            start = i + 1
    return start

Task Scheduler MEDIUM

Problem: Given task labels and a cooldown n between identical tasks, return the least total time (including idles) to finish all tasks.
def least_interval(tasks, n):
    from collections import Counter
    freq = Counter(tasks)
    max_f = max(freq.values())
    max_count = sum(1 for v in freq.values() if v == max_f)
    return max(len(tasks), (max_f - 1) * (n + 1) + max_count)

Partition Labels MEDIUM

Problem: Partition the string into as many parts as possible so each letter appears in at most one part. Return the part sizes.
def partition_labels(s):
    last = {c: i for i, c in enumerate(s)}
    out, start, end = [], 0, 0
    for i, c in enumerate(s):
        end = max(end, last[c])
        if i == end:
            out.append(i - start + 1)
            start = i + 1
    return out

Non-overlapping Intervals MEDIUM

Problem: Return the minimum number of intervals to remove so the rest are non-overlapping.
def erase_overlap_intervals(intervals):
    intervals.sort(key=lambda x: x[1])
    end = float('-inf')
    removed = 0
    for s, e in intervals:
        if s >= end:
            end = e
        else:
            removed += 1
    return removed

Minimum Arrows to Burst Balloons MEDIUM

Problem: Balloons span [start,end] on a wall. An arrow at x bursts every balloon covering x. Return the fewest arrows to burst all.
def find_min_arrows(points):
    if not points:
        return 0
    points.sort(key=lambda x: x[1])
    arrows = 1
    end = points[0][1]
    for s, e in points[1:]:
        if s > end:
            arrows += 1
            end = e
    return arrows

Merge Intervals MEDIUM

Problem: Merge all overlapping intervals and return the resulting non-overlapping intervals.
def merge_intervals_greedy(intervals):
    intervals.sort()
    out = []
    for s, e in intervals:
        if out and s <= out[-1][1]:
            out[-1][1] = max(out[-1][1], e)
        else:
            out.append([s, e])
    return out

Hand of Straights MEDIUM

Problem: Can the hand be rearranged into groups of group_size consecutive cards? Return True or False.
def is_n_straight_hand(hand, group_size):
    from collections import Counter
    if len(hand) % group_size != 0:
        return False
    count = Counter(hand)
    for card in sorted(count):
        if count[card] > 0:
            need = count[card]
            for k in range(card, card + group_size):
                if count[k] < need:
                    return False
                count[k] -= need
    return True

Minimum Increments to Make Array Unique MEDIUM

Problem: You may increment elements by 1 repeatedly. Return the minimum number of increments to make every value unique.
def min_increment_for_unique(nums):
    nums.sort()
    moves = 0
    for i in range(1, len(nums)):
        if nums[i] <= nums[i - 1]:
            need = nums[i - 1] + 1
            moves += need - nums[i]
            nums[i] = need
    return moves

Candy HARD

Problem: Each child has a rating. Every child gets >=1 candy and a child with a higher rating than a neighbor gets more candy. Return the minimum total candies.
def candy_greedy(ratings):
    n = len(ratings)
    give = [1] * n
    for i in range(1, n):
        if ratings[i] > ratings[i - 1]:
            give[i] = give[i - 1] + 1
    for i in range(n - 2, -1, -1):
        if ratings[i] > ratings[i + 1]:
            give[i] = max(give[i], give[i + 1] + 1)
    return sum(give)

Remove K Digits HARD

Problem: Remove k digits from the number string so the remaining number is as small as possible. Return it without leading zeros.
def remove_k_digits(num, k):
    stack = []
    for d in num:
        while k and stack and stack[-1] > d:
            stack.pop(); k -= 1
        stack.append(d)
    stack = stack[:len(stack) - k] if k else stack
    return ''.join(stack).lstrip('0') or '0'

Reorganize String HARD

Problem: Rearrange the string so no two adjacent characters are the same. Return any valid arrangement, or '' if impossible.
def reorganize_string(s):
    import heapq
    from collections import Counter
    heap = [(-c, ch) for ch, c in Counter(s).items()]
    heapq.heapify(heap)
    prev = None
    out = []
    while heap:
        cnt, ch = heapq.heappop(heap)
        out.append(ch)
        if prev and prev[0] < 0:
            heapq.heappush(heap, prev)
        prev = (cnt + 1, ch)
    result = ''.join(out)
    return result if len(result) == len(s) else ''

IPO (Maximize Capital) HARD

Problem: Start with capital w. Each project needs capital[i] to start and yields profit[i]. Complete at most k projects to maximize final capital.
def find_maximized_capital_greedy(k, w, profits, capital):
    import heapq
    projects = sorted(zip(capital, profits))
    heap = []
    i = 0
    for _ in range(k):
        while i < len(projects) and projects[i][0] <= w:
            heapq.heappush(heap, -projects[i][1])
            i += 1
        if not heap:
            break
        w += -heapq.heappop(heap)
    return w

Minimum Taps to Water a Garden HARD

Problem: Garden is [0,n]. Tap i covers [i-ranges[i], i+ranges[i]]. Return the fewest taps to water the whole garden, or -1.
def min_taps(n, ranges):
    reach = [0] * (n + 1)
    for i, r in enumerate(ranges):
        left = max(0, i - r)
        reach[left] = max(reach[left], i + r)
    taps = end = farthest = 0
    for i in range(n):
        farthest = max(farthest, reach[i])
        if i == end:
            if farthest <= i:
                return -1
            taps += 1
            end = farthest
    return taps

Video Stitching HARD

Problem: Given clips [start,end], return the fewest clips needed to cover [0, time], or -1 if impossible.
def video_stitching(clips, time):
    reach = [0] * (time + 1)
    for s, e in clips:
        if s <= time:
            reach[s] = max(reach[s], e)
    count = end = farthest = 0
    for i in range(time):
        farthest = max(farthest, reach[i])
        if i == end:
            if farthest <= i:
                return -1
            count += 1
            end = farthest
    return count

Two City Scheduling HARD

Problem: costs[i] = [cost_to_A, cost_to_B]. Send exactly half the people to each city, minimizing total cost.
def two_city_sched_cost(costs):
    costs.sort(key=lambda c: c[0] - c[1])
    n = len(costs) // 2
    return sum(c[0] for c in costs[:n]) + sum(c[1] for c in costs[n:])

Connect Sticks at Minimum Cost HARD

Problem: Combining two sticks costs the sum of their lengths and produces one stick. Return the minimum cost to combine all sticks into one.
def connect_sticks(sticks):
    import heapq
    heapq.heapify(sticks)
    total = 0
    while len(sticks) > 1:
        a = heapq.heappop(sticks)
        b = heapq.heappop(sticks)
        total += a + b
        heapq.heappush(sticks, a + b)
    return total

Car Fleet HARD

Problem: Cars at positions with speeds head to a target. Slower cars ahead cap faster ones behind into fleets. Return the number of fleets that arrive.
def car_fleet(target, position, speed):
    pairs = sorted(zip(position, speed), reverse=True)
    fleets = 0
    cur = 0.0
    for pos, spd in pairs:
        time = (target - pos) / spd
        if time > cur:
            fleets += 1
            cur = time
    return fleets

Bag of Tokens HARD

Problem: Each token can be played face-up (spend token value, gain 1 point, needs enough power) or face-down (spend 1 point, gain its power). Maximize points.
def bag_of_tokens_score(tokens, power):
    tokens.sort()
    lo, hi = 0, len(tokens) - 1
    score = best = 0
    while lo <= hi:
        if power >= tokens[lo]:
            power -= tokens[lo]
            score += 1
            best = max(best, score)
            lo += 1
        elif score > 0:
            power += tokens[hi]
            score -= 1
            hi -= 1
        else:
            break
    return best

Data Structures & Design

Min Stack EASY

Problem: Design a stack that supports push, pop, top, and retrieving the minimum element, all in O(1). Operations are given as a list; return each op's result (None for void ops).
class MinStack:
    def __init__(self):
        self.stack = []
        self.mins = []
    def push(self, x):
        self.stack.append(x)
        self.mins.append(x if not self.mins else min(x, self.mins[-1]))
    def pop(self):
        self.mins.pop()
        return self.stack.pop()
    def top(self):
        return self.stack[-1]
    def getMin(self):
        return self.mins[-1]

def min_stack_ops(ops):
    obj = MinStack()
    out = []
    for op in ops:
        r = getattr(obj, op[0])(*op[1:])
        out.append(r if op[0] in ('top', 'getMin') else None)
    return out

Queue Using Two Stacks EASY

Problem: Implement a FIFO queue using only two stacks. Support push, pop, peek, empty. Return each op's result.
class MyQueue:
    def __init__(self):
        self.a = []
        self.b = []
    def push(self, x):
        self.a.append(x)
    def _shift(self):
        if not self.b:
            while self.a:
                self.b.append(self.a.pop())
    def pop(self):
        self._shift()
        return self.b.pop()
    def peek(self):
        self._shift()
        return self.b[-1]
    def empty(self):
        return not self.a and not self.b

def queue_using_stacks(ops):
    obj = MyQueue()
    out = []
    for op in ops:
        r = getattr(obj, op[0])(*op[1:])
        out.append(r if op[0] in ('pop', 'peek', 'empty') else None)
    return out

Stack Using Two Queues EASY

Problem: Implement a LIFO stack using two queues. Support push, pop, top, empty. Return each op's result.
from collections import deque
class MyStack:
    def __init__(self):
        self.q = deque()
    def push(self, x):
        self.q.append(x)
        for _ in range(len(self.q) - 1):
            self.q.append(self.q.popleft())
    def pop(self):
        return self.q.popleft()
    def top(self):
        return self.q[0]
    def empty(self):
        return not self.q

def stack_using_queues(ops):
    obj = MyStack()
    out = []
    for op in ops:
        r = getattr(obj, op[0])(*op[1:])
        out.append(r if op[0] in ('pop', 'top', 'empty') else None)
    return out

Moving Average From Data Stream EASY

Problem: Given a window size, return the moving average of the last size values after each new value arrives.
from collections import deque
def moving_average(size, values):
    window = deque()
    total = 0
    out = []
    for v in values:
        window.append(v)
        total += v
        if len(window) > size:
            total -= window.popleft()
        out.append(total / len(window))
    return out

Design HashSet EASY

Problem: Design a hash set supporting add, remove, and contains without using a built-in set. Return the result of each contains call (None for void ops).
class MyHashSet:
    def __init__(self):
        self.buckets = [[] for _ in range(769)]
    def _b(self, key):
        return self.buckets[key % 769]
    def add(self, key):
        b = self._b(key)
        if key not in b:
            b.append(key)
    def remove(self, key):
        b = self._b(key)
        if key in b:
            b.remove(key)
    def contains(self, key):
        return key in self._b(key)

def design_hashset(ops):
    obj = MyHashSet()
    out = []
    for op in ops:
        r = getattr(obj, op[0])(*op[1:])
        out.append(r if op[0] == 'contains' else None)
    return out

Design HashMap EASY

Problem: Design a hash map supporting put, get (return -1 if absent), and remove without built-in dict. Return each get result.
class MyHashMap:
    def __init__(self):
        self.buckets = [[] for _ in range(769)]
    def _b(self, key):
        return self.buckets[key % 769]
    def put(self, key, value):
        b = self._b(key)
        for i, (k, v) in enumerate(b):
            if k == key:
                b[i] = (key, value)
                return
        b.append((key, value))
    def get(self, key):
        for k, v in self._b(key):
            if k == key:
                return v
        return -1
    def remove(self, key):
        b = self._b(key)
        for i, (k, v) in enumerate(b):
            if k == key:
                b.pop(i)
                return

def design_hashmap(ops):
    obj = MyHashMap()
    out = []
    for op in ops:
        r = getattr(obj, op[0])(*op[1:])
        out.append(r if op[0] == 'get' else None)
    return out

Logger Rate Limiter EASY

Problem: Each message may be printed only if it hasn't been printed in the last 10 seconds. Given [timestamp, message] pairs, return True/False for each (whether it prints).
def logger_rate_limiter(logs):
    last = {}
    out = []
    for ts, msg in logs:
        if msg not in last or ts - last[msg] >= 10:
            last[msg] = ts
            out.append(True)
        else:
            out.append(False)
    return out

Range Sum Query (Immutable) EASY

Problem: Precompute prefix sums so each query [i, j] returns the sum of nums[i..j] in O(1). Return the answers.
def range_sum_immutable(nums, queries):
    prefix = [0]
    for n in nums:
        prefix.append(prefix[-1] + n)
    return [prefix[j + 1] - prefix[i] for i, j in queries]

Design Parking System EASY

Problem: A parking lot has a fixed number of big, medium, and small spots. For each incoming car (type 1/2/3), return whether it can park.
def parking_system(slots, cars):
    remaining = {1: slots[0], 2: slots[1], 3: slots[2]}
    out = []
    for car in cars:
        if remaining[car] > 0:
            remaining[car] -= 1
            out.append(True)
        else:
            out.append(False)
    return out

Number of Recent Calls EASY

Problem: Each ping arrives at a timestamp. After each ping, return how many pings occurred in the last 3000 ms (inclusive).
from collections import deque
def recent_counter(pings):
    window = deque()
    out = []
    for t in pings:
        window.append(t)
        while window[0] < t - 3000:
            window.popleft()
        out.append(len(window))
    return out

BST Construction MEDIUM

class BST:
    def __init__(self, value):
        self.value = value; self.left = None; self.right = None
    def insert(self, value):
        node = self
        while True:
            if value < node.value:
                if node.left is None: node.left = BST(value); break
                node = node.left
            else:
                if node.right is None: node.right = BST(value); break
                node = node.right
        return self
    def contains(self, value):
        node = self
        while node:
            if value < node.value: node = node.left
            elif value > node.value: node = node.right
            else: return True
        return False

Min Heap (with Python's heapq) MEDIUM

import heapq
nums = [5, 2, 8, 1]
heapq.heapify(nums)         # O(n) -> smallest is always nums[0]
heapq.heappush(nums, 3)     # O(log n)
smallest = heapq.heappop(nums)   # remove + return the min, O(log n)
# Max-heap trick: push/pop the NEGATIVE of each value

Suffix Trie Construction MEDIUM

class SuffixTrie:
    def __init__(self, string):
        self.root = {}; self.end = "*"
        for i in range(len(string)):
            self._insert(string[i:])
    def _insert(self, s):
        node = self.root
        for ch in s: node = node.setdefault(ch, {})
        node[self.end] = True
    def contains(self, s):
        node = self.root
        for ch in s:
            if ch not in node: return False
            node = node[ch]
        return self.end in node

LRU Cache MEDIUM

Problem: Design a Least-Recently-Used cache with a capacity, supporting get (return -1 if absent) and put in O(1). Return each get result.
from collections import OrderedDict
class LRUCache:
    def __init__(self, capacity):
        self.cache = OrderedDict()
        self.cap = capacity
    def get(self, key):
        if key not in self.cache:
            return -1
        self.cache.move_to_end(key)
        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.cap:
            self.cache.popitem(last=False)

def lru_cache_ops(capacity, ops):
    obj = LRUCache(capacity)
    out = []
    for op in ops:
        r = getattr(obj, op[0])(*op[1:])
        out.append(r if op[0] == 'get' else None)
    return out

Design Circular Queue MEDIUM

Problem: Implement a fixed-size circular queue supporting enQueue, deQueue, Front, Rear, isEmpty, isFull. Return each op's result.
class MyCircularQueue:
    def __init__(self, k):
        self.q = [0] * k
        self.head = 0
        self.count = 0
        self.cap = k
    def enQueue(self, v):
        if self.count == self.cap:
            return False
        self.q[(self.head + self.count) % self.cap] = v
        self.count += 1
        return True
    def deQueue(self):
        if self.count == 0:
            return False
        self.head = (self.head + 1) % self.cap
        self.count -= 1
        return True
    def Front(self):
        return -1 if self.count == 0 else self.q[self.head]
    def Rear(self):
        return -1 if self.count == 0 else self.q[(self.head + self.count - 1) % self.cap]
    def isEmpty(self):
        return self.count == 0
    def isFull(self):
        return self.count == self.cap

def circular_queue_ops(k, ops):
    obj = MyCircularQueue(k)
    out = []
    for op in ops:
        r = getattr(obj, op[0])(*op[1:])
        out.append(r)
    return out

Time-Based Key-Value Store MEDIUM

Problem: Store multiple values per key with timestamps. get(key, t) returns the value set at the greatest timestamp <= t (or '' if none). Return each get result.
import bisect
class TimeMap:
    def __init__(self):
        self.store = {}
    def set(self, key, value, timestamp):
        self.store.setdefault(key, []).append((timestamp, value))
    def get(self, key, timestamp):
        arr = self.store.get(key, [])
        i = bisect.bisect_right(arr, (timestamp, chr(127)))
        return arr[i - 1][1] if i else ''

def time_map_ops(ops):
    obj = TimeMap()
    out = []
    for op in ops:
        r = getattr(obj, op[0])(*op[1:])
        out.append(r if op[0] == 'get' else None)
    return out

Design Underground System MEDIUM

Problem: Track passenger check-ins and check-outs at stations. getAverageTime(start, end) returns the average travel time between two stations. Return query results (None for void ops).
class UndergroundSystem:
    def __init__(self):
        self.checkins = {}
        self.totals = {}
    def checkIn(self, uid, station, t):
        self.checkins[uid] = (station, t)
    def checkOut(self, uid, station, t):
        start, t0 = self.checkins.pop(uid)
        key = (start, station)
        total, count = self.totals.get(key, (0, 0))
        self.totals[key] = (total + (t - t0), count + 1)
    def getAverageTime(self, start, end):
        total, count = self.totals[(start, end)]
        return total / count

def underground_system(ops):
    obj = UndergroundSystem()
    out = []
    for op in ops:
        r = getattr(obj, op[0])(*op[1:])
        out.append(r if op[0] == 'getAverageTime' else None)
    return out

Design Browser History MEDIUM

Problem: Start on a homepage. visit(url) clears forward history. back(steps) and forward(steps) move within history and return the current url. Return each back/forward result.
class BrowserHistory:
    def __init__(self, homepage):
        self.history = [homepage]
        self.cur = 0
    def visit(self, url):
        del self.history[self.cur + 1:]
        self.history.append(url)
        self.cur += 1
    def back(self, steps):
        self.cur = max(0, self.cur - steps)
        return self.history[self.cur]
    def forward(self, steps):
        self.cur = min(len(self.history) - 1, self.cur + steps)
        return self.history[self.cur]

def browser_history(homepage, ops):
    obj = BrowserHistory(homepage)
    out = []
    for op in ops:
        r = getattr(obj, op[0])(*op[1:])
        out.append(r if op[0] in ('back', 'forward') else None)
    return out

Online Stock Span MEDIUM

Problem: For each daily price, return the stock span: the number of consecutive days up to today with a price <= today's price.
def stock_spanner(prices):
    stack = []
    out = []
    for price in prices:
        span = 1
        while stack and stack[-1][0] <= price:
            span += stack.pop()[1]
        stack.append((price, span))
        out.append(span)
    return out

Design Front Middle Back Queue MEDIUM

Problem: Support pushFront, pushMiddle, pushBack, popFront, popMiddle, popBack. Pops return -1 if empty. Return each pop's result (None for void ops).
from collections import deque
class FrontMiddleBack:
    def __init__(self):
        self.left = deque()
        self.right = deque()
    def _balance(self):
        if len(self.left) > len(self.right):
            self.right.appendleft(self.left.pop())
        elif len(self.right) > len(self.left) + 1:
            self.left.append(self.right.popleft())
    def pushFront(self, val):
        self.left.appendleft(val); self._balance()
    def pushMiddle(self, val):
        if len(self.left) < len(self.right):
            self.left.append(val)
        else:
            self.right.appendleft(val)
        self._balance()
    def pushBack(self, val):
        self.right.append(val); self._balance()
    def popFront(self):
        if not self.left and not self.right:
            return -1
        val = self.left.popleft() if self.left else self.right.popleft()
        self._balance()
        return val
    def popMiddle(self):
        if not self.left and not self.right:
            return -1
        if len(self.left) == len(self.right):
            val = self.left.pop()
        else:
            val = self.right.popleft()
        self._balance()
        return val
    def popBack(self):
        if not self.right and not self.left:
            return -1
        val = self.right.pop() if self.right else self.left.pop()
        self._balance()
        return val

def front_middle_back_ops(ops):
    obj = FrontMiddleBack()
    out = []
    for op in ops:
        r = getattr(obj, op[0])(*op[1:])
        out.append(r if op[0].startswith('pop') else None)
    return out

My Calendar I MEDIUM

Problem: Book events [start, end). A booking succeeds only if it does not overlap an existing one. Return True/False for each booking.
def my_calendar(bookings):
    booked = []
    out = []
    for start, end in bookings:
        ok = all(end <= s or start >= e for s, e in booked)
        if ok:
            booked.append((start, end))
        out.append(ok)
    return out

Range Sum Query (Mutable) MEDIUM

Problem: Support update(i, val) and sumRange(i, j) with a Binary Indexed (Fenwick) Tree. Return each sumRange result (None for updates).
class NumArray:
    def __init__(self, nums):
        self.n = len(nums)
        self.tree = [0] * (self.n + 1)
        self.nums = [0] * self.n
        for i, v in enumerate(nums):
            self.update(i, v)
    def update(self, i, val):
        delta = val - self.nums[i]
        self.nums[i] = val
        i += 1
        while i <= self.n:
            self.tree[i] += delta
            i += i & (-i)
    def _prefix(self, i):
        s = 0
        while i > 0:
            s += self.tree[i]
            i -= i & (-i)
        return s
    def sumRange(self, i, j):
        return self._prefix(j + 1) - self._prefix(i)

def range_sum_mutable(nums, ops):
    obj = NumArray(nums)
    out = []
    for op in ops:
        r = getattr(obj, op[0])(*op[1:])
        out.append(r if op[0] == 'sumRange' else None)
    return out

Design Circular Deque MEDIUM

Problem: Implement a fixed-size circular double-ended queue: insertFront, insertLast, deleteFront, deleteLast, getFront, getRear, isEmpty, isFull. Return each op's result.
class MyCircularDeque:
    def __init__(self, k):
        self.q = [0] * k
        self.head = 0
        self.count = 0
        self.cap = k
    def insertFront(self, v):
        if self.count == self.cap:
            return False
        self.head = (self.head - 1) % self.cap
        self.q[self.head] = v
        self.count += 1
        return True
    def insertLast(self, v):
        if self.count == self.cap:
            return False
        self.q[(self.head + self.count) % self.cap] = v
        self.count += 1
        return True
    def deleteFront(self):
        if self.count == 0:
            return False
        self.head = (self.head + 1) % self.cap
        self.count -= 1
        return True
    def deleteLast(self):
        if self.count == 0:
            return False
        self.count -= 1
        return True
    def getFront(self):
        return -1 if self.count == 0 else self.q[self.head]
    def getRear(self):
        return -1 if self.count == 0 else self.q[(self.head + self.count - 1) % self.cap]
    def isEmpty(self):
        return self.count == 0
    def isFull(self):
        return self.count == self.cap

def circular_deque_ops(k, ops):
    obj = MyCircularDeque(k)
    out = []
    for op in ops:
        r = getattr(obj, op[0])(*op[1:])
        out.append(r)
    return out

LRU Cache HARD

Get/put in O(1), evicting the least-recently-used item. An OrderedDict does the heavy lifting.

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 most-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 least-recently-used

Knuth-Morris-Pratt (string search) HARD

Find a pattern in text in O(n+m) using a precomputed "failure" table.

class Solution:
    def kmp_search(self, text, pattern):
        lps = [0] * len(pattern)          # longest prefix = suffix table
        k = 0
        for i in range(1, len(pattern)):
            while k > 0 and pattern[i] != pattern[k]: k = lps[k-1]
            if pattern[i] == pattern[k]: k += 1
            lps[i] = k
        j = 0
        for i in range(len(text)):
            while j > 0 and text[i] != pattern[j]: j = lps[j-1]
            if text[i] == pattern[j]: j += 1
            if j == len(pattern): return i - j + 1   # start index of match
        return -1

LFU Cache HARD

Problem: Design a Least-Frequently-Used cache; ties are broken by least-recently-used. Support get (-1 if absent) and put in O(1). Return each get result.
from collections import defaultdict, OrderedDict
class LFUCache:
    def __init__(self, capacity):
        self.cap = capacity
        self.vals = {}
        self.freq = {}
        self.groups = defaultdict(OrderedDict)
        self.min_freq = 0
    def _bump(self, key):
        f = self.freq[key]
        del self.groups[f][key]
        if not self.groups[f]:
            del self.groups[f]
            if self.min_freq == f:
                self.min_freq += 1
        self.freq[key] = f + 1
        self.groups[f + 1][key] = None
    def get(self, key):
        if key not in self.vals:
            return -1
        self._bump(key)
        return self.vals[key]
    def put(self, key, value):
        if self.cap == 0:
            return
        if key in self.vals:
            self.vals[key] = value
            self._bump(key)
            return
        if len(self.vals) >= self.cap:
            evict, _ = self.groups[self.min_freq].popitem(last=False)
            del self.vals[evict]
            del self.freq[evict]
        self.vals[key] = value
        self.freq[key] = 1
        self.groups[1][key] = None
        self.min_freq = 1

def lfu_cache_ops(capacity, ops):
    obj = LFUCache(capacity)
    out = []
    for op in ops:
        r = getattr(obj, op[0])(*op[1:])
        out.append(r if op[0] == 'get' else None)
    return out

Find Median From Data Stream HARD

Problem: Support addNum(x) and findMedian(). Maintain a max-heap (low half) and min-heap (high half). Return each findMedian result (None for adds).
import heapq
class MedianFinder:
    def __init__(self):
        self.low = []
        self.high = []
    def addNum(self, num):
        heapq.heappush(self.low, -num)
        heapq.heappush(self.high, -heapq.heappop(self.low))
        if len(self.high) > len(self.low):
            heapq.heappush(self.low, -heapq.heappop(self.high))
    def findMedian(self):
        if len(self.low) > len(self.high):
            return float(-self.low[0])
        return (-self.low[0] + self.high[0]) / 2

def median_finder_ops(ops):
    obj = MedianFinder()
    out = []
    for op in ops:
        r = getattr(obj, op[0])(*op[1:])
        out.append(r if op[0] == 'findMedian' else None)
    return out

Add and Search Word (Wildcards) HARD

Problem: Design a dictionary with addWord and search, where search supports '.' matching any single letter. Return each search result (None for adds).
class WordDictionary:
    def __init__(self):
        self.root = {}
    def addWord(self, word):
        node = self.root
        for c in word:
            node = node.setdefault(c, {})
        node['$'] = True
    def search(self, word):
        def dfs(node, i):
            if i == len(word):
                return '$' in node
            c = word[i]
            if c == '.':
                return any(dfs(child, i + 1) for k, child in node.items() if k != '$')
            return c in node and dfs(node[c], i + 1)
        return dfs(self.root, 0)

def word_dictionary_ops(ops):
    obj = WordDictionary()
    out = []
    for op in ops:
        r = getattr(obj, op[0])(*op[1:])
        out.append(r if op[0] == 'search' else None)
    return out

My Calendar II (No Triple Booking) HARD

Problem: Book events [start, end). A booking is allowed unless it would cause a triple booking. Return True/False for each booking.
def my_calendar_ii(bookings):
    booked = []
    overlaps = []
    out = []
    for start, end in bookings:
        if any(start < e and s < end for s, e in overlaps):
            out.append(False)
            continue
        for s, e in booked:
            if start < e and s < end:
                overlaps.append((max(start, s), min(end, e)))
        booked.append((start, end))
        out.append(True)
    return out

My Calendar III (Max K-Booking) HARD

Problem: After each booking [start, end), return the maximum number of events overlapping at any single point in time, using a sweep-line count.
from collections import defaultdict
def my_calendar_iii(bookings):
    delta = defaultdict(int)
    out = []
    for start, end in bookings:
        delta[start] += 1
        delta[end] -= 1
        active = 0
        best = 0
        for t in sorted(delta):
            active += delta[t]
            best = max(best, active)
        out.append(best)
    return out

Snapshot Array HARD

Problem: Design an array of a given length that supports set(index, val), snap() (returns a snapshot id), and get(index, snap_id). Use per-index history + binary search. Return snap ids and get results (None for sets).
import bisect
class SnapshotArray:
    def __init__(self, length):
        self.snap_id = 0
        self.history = [[(-1, 0)] for _ in range(length)]
    def set(self, index, val):
        self.history[index].append((self.snap_id, val))
    def snap(self):
        self.snap_id += 1
        return self.snap_id - 1
    def get(self, index, snap_id):
        arr = self.history[index]
        i = bisect.bisect_right(arr, (snap_id, float('inf'))) - 1
        return arr[i][1]

def snapshot_array_ops(length, ops):
    obj = SnapshotArray(length)
    out = []
    for op in ops:
        r = getattr(obj, op[0])(*op[1:])
        out.append(r if op[0] in ('snap', 'get') else None)
    return out

Stock Price Fluctuation HARD

Problem: Records arrive as (timestamp, price) and timestamps may be corrected later. Support update, current (latest price), maximum, minimum. Return each query result (None for updates).
import heapq
class StockPrice:
    def __init__(self):
        self.prices = {}
        self.latest = 0
        self.max_heap = []
        self.min_heap = []
    def update(self, timestamp, price):
        self.prices[timestamp] = price
        self.latest = max(self.latest, timestamp)
        heapq.heappush(self.max_heap, (-price, timestamp))
        heapq.heappush(self.min_heap, (price, timestamp))
    def current(self):
        return self.prices[self.latest]
    def maximum(self):
        while -self.max_heap[0][0] != self.prices[self.max_heap[0][1]]:
            heapq.heappop(self.max_heap)
        return -self.max_heap[0][0]
    def minimum(self):
        while self.min_heap[0][0] != self.prices[self.min_heap[0][1]]:
            heapq.heappop(self.min_heap)
        return self.min_heap[0][0]

def stock_price_ops(ops):
    obj = StockPrice()
    out = []
    for op in ops:
        r = getattr(obj, op[0])(*op[1:])
        out.append(r if op[0] in ('current', 'maximum', 'minimum') else None)
    return out

Range Module HARD

Problem: Track ranges of numbers. Support addRange(l, r), queryRange(l, r) (are all numbers in [l,r) tracked?), and removeRange(l, r). Return each queryRange result (None for void ops).
import bisect
class RangeModule:
    def __init__(self):
        self.ranges = []
    def addRange(self, left, right):
        i = bisect.bisect_left(self.ranges, left)
        j = bisect.bisect_right(self.ranges, right)
        merged = []
        if i % 2 == 0:
            merged.append(left)
        if j % 2 == 0:
            merged.append(right)
        self.ranges[i:j] = merged
    def queryRange(self, left, right):
        i = bisect.bisect_right(self.ranges, left)
        j = bisect.bisect_left(self.ranges, right)
        return i == j and i % 2 == 1
    def removeRange(self, left, right):
        i = bisect.bisect_left(self.ranges, left)
        j = bisect.bisect_right(self.ranges, right)
        merged = []
        if i % 2 == 1:
            merged.append(left)
        if j % 2 == 1:
            merged.append(right)
        self.ranges[i:j] = merged

def range_module_ops(ops):
    obj = RangeModule()
    out = []
    for op in ops:
        r = getattr(obj, op[0])(*op[1:])
        out.append(r if op[0] == 'queryRange' else None)
    return out

Maximum Frequency Stack HARD

Problem: Design a stack where pop() removes and returns the most frequent element; ties are broken by the most recently pushed. Return each pop result (None for pushes).
from collections import defaultdict
class FreqStack:
    def __init__(self):
        self.freq = defaultdict(int)
        self.groups = defaultdict(list)
        self.max_freq = 0
    def push(self, val):
        self.freq[val] += 1
        f = self.freq[val]
        self.max_freq = max(self.max_freq, f)
        self.groups[f].append(val)
    def pop(self):
        val = self.groups[self.max_freq].pop()
        self.freq[val] -= 1
        if not self.groups[self.max_freq]:
            self.max_freq -= 1
        return val

def freq_stack_ops(ops):
    obj = FreqStack()
    out = []
    for op in ops:
        r = getattr(obj, op[0])(*op[1:])
        out.append(r if op[0] == 'pop' else None)
    return out

Authentication Manager HARD

Problem: Tokens expire ttl seconds after their last use. Support generate(id, t), renew(id, t) (only if unexpired), and countUnexpiredTokens(t). Return each count result (None for void ops).
class AuthenticationManager:
    def __init__(self, ttl):
        self.ttl = ttl
        self.tokens = {}
    def generate(self, token_id, current_time):
        self.tokens[token_id] = current_time + self.ttl
    def renew(self, token_id, current_time):
        if token_id in self.tokens and self.tokens[token_id] > current_time:
            self.tokens[token_id] = current_time + self.ttl
    def countUnexpiredTokens(self, current_time):
        return sum(1 for exp in self.tokens.values() if exp > current_time)

def authentication_manager_ops(ttl, ops):
    obj = AuthenticationManager(ttl)
    out = []
    for op in ops:
        r = getattr(obj, op[0])(*op[1:])
        out.append(r if op[0] == 'countUnexpiredTokens' else None)
    return out

Java

Binary Search (Java) EASY

Iterative binary search with the overflow-safe midpoint. O(log n) time, O(1) space.

public static int binarySearch(int[] arr, int target) {
    int lo = 0, hi = arr.length - 1;
    while (lo <= hi) {
        int mid = lo + (hi - lo) / 2;      // avoids (lo+hi) overflow
        if (arr[mid] == target) return mid;
        if (arr[mid] < target) lo = mid + 1;
        else                   hi = mid - 1;
    }
    return -1;                             // not found
}

Reverse an Array In Place (Java) EASY

Problem: Reverse an int array in place using two pointers.
    static void reverse(int[] a) {
        int lo = 0, hi = a.length - 1;
        while (lo < hi) {
            int tmp = a[lo]; a[lo] = a[hi]; a[hi] = tmp;
            lo++; hi--;
        }
    }

FizzBuzz (Java) EASY

Problem: Return the FizzBuzz sequence from 1 to n: multiples of 3 are 'Fizz', of 5 'Buzz', of both 'FizzBuzz'.
    static List<String> fizzBuzz(int n) {
        List<String> res = new ArrayList<>();
        for (int i = 1; i <= n; i++) {
            if (i % 15 == 0) res.add("FizzBuzz");
            else if (i % 3 == 0) res.add("Fizz");
            else if (i % 5 == 0) res.add("Buzz");
            else res.add(String.valueOf(i));
        }
        return res;
    }

Find Maximum in an Array (Java) EASY

Problem: Return the largest value in a non-empty int array.
    static int findMax(int[] a) {
        int max = a[0];
        for (int x : a) max = Math.max(max, x);
        return max;
    }

Sum of an Array (Java) EASY

Problem: Return the sum of all elements in an int array.
    static long arraySum(int[] a) {
        long sum = 0;
        for (int x : a) sum += x;
        return sum;
    }

Palindrome Check (Java) EASY

Problem: Return true if the string reads the same forwards and backwards.
    static boolean isPalindrome(String s) {
        int lo = 0, hi = s.length() - 1;
        while (lo < hi) {
            if (s.charAt(lo) != s.charAt(hi)) return false;
            lo++; hi--;
        }
        return true;
    }

Count Vowels (Java) EASY

Problem: Return the number of vowels (a, e, i, o, u) in the string.
    static int countVowels(String s) {
        int count = 0;
        String vowels = "aeiouAEIOU";
        for (char c : s.toCharArray())
            if (vowels.indexOf(c) >= 0) count++;
        return count;
    }

Factorial (Java) EASY

Problem: Compute n! iteratively using a long to hold the result.
    static long factorial(int n) {
        long result = 1;
        for (int i = 2; i <= n; i++) result *= i;
        return result;
    }

Greatest Common Divisor (Java) EASY

Problem: Compute gcd(a, b) with the iterative Euclidean algorithm.
    static int gcd(int a, int b) {
        while (b != 0) {
            int tmp = b;
            b = a % b;
            a = tmp;
        }
        return a;
    }

Linear Search (Java) EASY

Problem: Return the index of target in the array, or -1 if it is absent.
    static int linearSearch(int[] a, int target) {
        for (int i = 0; i < a.length; i++)
            if (a[i] == target) return i;
        return -1;
    }

Bubble Sort (Java) EASY

Problem: Sort an int array ascending using bubble sort with an early-exit optimization.
    static void bubbleSort(int[] a) {
        for (int i = 0; i < a.length - 1; i++) {
            boolean swapped = false;
            for (int j = 0; j < a.length - 1 - i; j++) {
                if (a[j] > a[j + 1]) {
                    int tmp = a[j]; a[j] = a[j + 1]; a[j + 1] = tmp;
                    swapped = true;
                }
            }
            if (!swapped) break;
        }
    }

Breadth-First Search (Java) MEDIUM

Queue-based BFS over an adjacency map. Mark nodes seen at enqueue time so cycles can't loop. O(V+E).

import java.util.*;

public static List<Integer> bfs(Map<Integer, List<Integer>> graph, int start) {
    List<Integer> order = new ArrayList<>();
    Set<Integer> seen  = new HashSet<>();
    Queue<Integer> queue = new LinkedList<>();
    queue.add(start); seen.add(start);

    while (!queue.isEmpty()) {
        int node = queue.poll();
        order.add(node);
        for (int next : graph.getOrDefault(node, List.of())) {
            if (!seen.contains(next)) {    // seen-before-enqueue stops cycles
                seen.add(next);
                queue.add(next);
            }
        }
    }
    return order;
}

Longest Common Subsequence (Java) MEDIUM

Bottom-up DP - the plagiarism-detector algorithm in Java. O(n·m) time and space.

public static int lcs(String a, String b) {
    int n = a.length(), m = b.length();
    int[][] dp = new int[n + 1][m + 1];
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            if (a.charAt(i - 1) == b.charAt(j - 1))
                dp[i][j] = 1 + dp[i - 1][j - 1];
            else
                dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
        }
    }
    return dp[n][m];
}

Two Sum (Java) MEDIUM

Problem: Return the indices of the two numbers that add up to target, using a HashMap in one pass.
    static int[] twoSum(int[] nums, int target) {
        Map<Integer, Integer> seen = new HashMap<>();
        for (int i = 0; i < nums.length; i++) {
            int need = target - nums[i];
            if (seen.containsKey(need)) return new int[]{seen.get(need), i};
            seen.put(nums[i], i);
        }
        return new int[]{-1, -1};
    }

Merge Two Sorted Arrays (Java) MEDIUM

Problem: Merge two sorted int arrays into a single sorted array.
    static int[] merge(int[] a, int[] b) {
        int[] res = new int[a.length + b.length];
        int i = 0, j = 0, k = 0;
        while (i < a.length && j < b.length)
            res[k++] = (a[i] <= b[j]) ? a[i++] : b[j++];
        while (i < a.length) res[k++] = a[i++];
        while (j < b.length) res[k++] = b[j++];
        return res;
    }

Valid Parentheses (Java) MEDIUM

Problem: Return true if the bracket string is balanced, using a Deque as a stack.
    static boolean isValid(String s) {
        Deque<Character> stack = new ArrayDeque<>();
        Map<Character, Character> pairs = Map.of(')', '(', ']', '[', '}', '{');
        for (char c : s.toCharArray()) {
            if (pairs.containsKey(c)) {
                if (stack.isEmpty() || stack.pop() != pairs.get(c)) return false;
            } else {
                stack.push(c);
            }
        }
        return stack.isEmpty();
    }

First Unique Character (Java) MEDIUM

Problem: Return the index of the first non-repeating character in the string, or -1 if none.
    static int firstUniqChar(String s) {
        Map<Character, Integer> count = new HashMap<>();
        for (char c : s.toCharArray())
            count.merge(c, 1, Integer::sum);
        for (int i = 0; i < s.length(); i++)
            if (count.get(s.charAt(i)) == 1) return i;
        return -1;
    }

Move Zeroes (Java) MEDIUM

Problem: Move all zeroes to the end of the array in place while keeping the order of the non-zero elements.
    static void moveZeroes(int[] nums) {
        int insert = 0;
        for (int n : nums)
            if (n != 0) nums[insert++] = n;
        while (insert < nums.length) nums[insert++] = 0;
    }

Rotate Array (Java) MEDIUM

Problem: Rotate the array to the right by k steps in place using the reversal trick.
    static void rotate(int[] nums, int k) {
        int n = nums.length;
        k %= n;
        reverse(nums, 0, n - 1);
        reverse(nums, 0, k - 1);
        reverse(nums, k, n - 1);
    }
    static void reverse(int[] a, int lo, int hi) {
        while (lo < hi) {
            int tmp = a[lo]; a[lo] = a[hi]; a[hi] = tmp;
            lo++; hi--;
        }
    }

Maximum Subarray - Kadane (Java) MEDIUM

Problem: Return the largest sum of any contiguous subarray using Kadane's algorithm.
    static int maxSubArray(int[] nums) {
        int best = nums[0], current = nums[0];
        for (int i = 1; i < nums.length; i++) {
            current = Math.max(nums[i], current + nums[i]);
            best = Math.max(best, current);
        }
        return best;
    }

Group Anagrams (Java) MEDIUM

Problem: Group the words that are anagrams of one another, keying a HashMap by the sorted characters.
    static List<List<String>> groupAnagrams(String[] strs) {
        Map<String, List<String>> map = new HashMap<>();
        for (String s : strs) {
            char[] chars = s.toCharArray();
            Arrays.sort(chars);
            String key = new String(chars);
            map.computeIfAbsent(key, k -> new ArrayList<>()).add(s);
        }
        return new ArrayList<>(map.values());
    }

Quicksort (Java) MEDIUM

Problem: Sort an int array ascending using in-place quicksort with the Lomuto partition scheme.
    static void quickSort(int[] a, int lo, int hi) {
        if (lo >= hi) return;
        int pivot = a[hi], i = lo;
        for (int j = lo; j < hi; j++) {
            if (a[j] < pivot) {
                int tmp = a[i]; a[i] = a[j]; a[j] = tmp;
                i++;
            }
        }
        int tmp = a[i]; a[i] = a[hi]; a[hi] = tmp;
        quickSort(a, lo, i - 1);
        quickSort(a, i + 1, hi);
    }

Merge Sort (Java) MEDIUM

Problem: Sort an int array ascending using recursive merge sort.
    static int[] mergeSort(int[] a) {
        if (a.length <= 1) return a;
        int mid = a.length / 2;
        int[] left = mergeSort(Arrays.copyOfRange(a, 0, mid));
        int[] right = mergeSort(Arrays.copyOfRange(a, mid, a.length));
        int[] res = new int[a.length];
        int i = 0, j = 0, k = 0;
        while (i < left.length && j < right.length)
            res[k++] = (left[i] <= right[j]) ? left[i++] : right[j++];
        while (i < left.length) res[k++] = left[i++];
        while (j < right.length) res[k++] = right[j++];
        return res;
    }

Edit Distance (Java) HARD

Levenshtein distance, bottom-up. O(n·m).

public static int editDistance(String a, String b) {
    int n = a.length(), m = b.length();
    int[][] dp = new int[n + 1][m + 1];
    for (int i = 0; i <= n; i++) dp[i][0] = i;   // delete all of a[:i]
    for (int j = 0; j <= m; j++) dp[0][j] = j;   // insert all of b[:j]

    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            if (a.charAt(i - 1) == b.charAt(j - 1))
                dp[i][j] = dp[i - 1][j - 1];
            else
                dp[i][j] = 1 + Math.min(dp[i - 1][j - 1],
                              Math.min(dp[i - 1][j], dp[i][j - 1]));
        }
    }
    return dp[n][m];
}

OOP Design - Cloud E-Reader (Java) HARD

The same design as the Python version, in idiomatic Java with a HashMap collection.

import java.util.*;

class Book {
    int id; String title; List<String> content; int lastPage = 0;
    Book(int id, String title, List<String> content) {
        this.id = id; this.title = title; this.content = content;
    }
    String displayPage() { return content.get(lastPage); }
    String turnPage() {
        if (lastPage < content.size() - 1) lastPage++;
        return displayPage();
    }
}

class Library {
    private Map<Integer, Book> collection = new HashMap<>();
    private Integer activeId = null;
    private int nextId = 0;

    int addBook(String title, List<String> content) {
        Book b = new Book(nextId++, title, content);
        collection.put(b.id, b);
        return b.id;
    }
    void removeBook(int id) {
        collection.remove(id);
        if (activeId != null && activeId == id) activeId = null;
    }
    void setActive(int id) { if (collection.containsKey(id)) activeId = id; }
    String displayPage() { return activeId == null ? null : collection.get(activeId).displayPage(); }
    String turnPage()    { return activeId == null ? null : collection.get(activeId).turnPage(); }
}

Dijkstra's Shortest Path (Java) HARD

Problem: Given a weighted adjacency matrix (0 = no edge), return the shortest distance from a source to every node using a PriorityQueue.
    static int[] dijkstra(int[][] graph, int src) {
        int n = graph.length;
        int[] dist = new int[n];
        Arrays.fill(dist, Integer.MAX_VALUE);
        dist[src] = 0;
        boolean[] done = new boolean[n];
        PriorityQueue<int[]> pq = new PriorityQueue<>((a, b) -> a[1] - b[1]);
        pq.add(new int[]{src, 0});
        while (!pq.isEmpty()) {
            int u = pq.poll()[0];
            if (done[u]) continue;
            done[u] = true;
            for (int v = 0; v < n; v++) {
                if (graph[u][v] > 0 && dist[u] + graph[u][v] < dist[v]) {
                    dist[v] = dist[u] + graph[u][v];
                    pq.add(new int[]{v, dist[v]});
                }
            }
        }
        return dist;
    }

LRU Cache (Java) HARD

Problem: Design a Least-Recently-Used cache with a fixed capacity using LinkedHashMap in access order.
    static class LRUCache {
        private final int cap;
        private final LinkedHashMap<Integer, Integer> map;
        LRUCache(int capacity) {
            cap = capacity;
            map = new LinkedHashMap<>(16, 0.75f, true) {
                protected boolean removeEldestEntry(Map.Entry<Integer, Integer> e) {
                    return size() > cap;
                }
            };
        }
        int get(int key) { return map.getOrDefault(key, -1); }
        void put(int key, int value) { map.put(key, value); }
    }

Trie (Prefix Tree) (Java) HARD

Problem: Implement a trie supporting insert, search (full word), and startsWith (prefix).
    static class Trie {
        private final Trie[] children = new Trie[26];
        private boolean isEnd;
        void insert(String word) {
            Trie node = this;
            for (char c : word.toCharArray()) {
                int i = c - 'a';
                if (node.children[i] == null) node.children[i] = new Trie();
                node = node.children[i];
            }
            node.isEnd = true;
        }
        boolean search(String word) {
            Trie node = find(word);
            return node != null && node.isEnd;
        }
        boolean startsWith(String prefix) { return find(prefix) != null; }
        private Trie find(String word) {
            Trie node = this;
            for (char c : word.toCharArray()) {
                int i = c - 'a';
                if (node.children[i] == null) return null;
                node = node.children[i];
            }
            return node;
        }
    }

Topological Sort - Kahn's (Java) HARD

Problem: Return a topological ordering of n nodes given directed edges, using Kahn's in-degree algorithm.
    static int[] topologicalSort(int n, int[][] edges) {
        List<List<Integer>> adj = new ArrayList<>();
        for (int i = 0; i < n; i++) adj.add(new ArrayList<>());
        int[] indegree = new int[n];
        for (int[] e : edges) {
            adj.get(e[0]).add(e[1]);
            indegree[e[1]]++;
        }
        Queue<Integer> queue = new LinkedList<>();
        for (int i = 0; i < n; i++) if (indegree[i] == 0) queue.add(i);
        int[] order = new int[n];
        int idx = 0;
        while (!queue.isEmpty()) {
            int u = queue.poll();
            order[idx++] = u;
            for (int v : adj.get(u))
                if (--indegree[v] == 0) queue.add(v);
        }
        return order;
    }

0/1 Knapsack (Java) HARD

Problem: Given item weights and values and a capacity, return the maximum value achievable using a 1-D DP.
    static int knapsack(int[] weights, int[] values, int capacity) {
        int[] dp = new int[capacity + 1];
        for (int i = 0; i < weights.length; i++)
            for (int w = capacity; w >= weights[i]; w--)
                dp[w] = Math.max(dp[w], dp[w - weights[i]] + values[i]);
        return dp[capacity];
    }

Longest Increasing Subsequence (Java) HARD

Problem: Return the length of the longest strictly increasing subsequence in O(n log n) using patience sorting.
    static int lengthOfLIS(int[] nums) {
        List<Integer> tails = new ArrayList<>();
        for (int x : nums) {
            int lo = 0, hi = tails.size();
            while (lo < hi) {
                int mid = (lo + hi) / 2;
                if (tails.get(mid) < x) lo = mid + 1;
                else hi = mid;
            }
            if (lo == tails.size()) tails.add(x);
            else tails.set(lo, x);
        }
        return tails.size();
    }

Median of Two Sorted Arrays (Java) HARD

Problem: Return the median of two sorted arrays in O(log(min(m,n))) using a binary-search partition.
    static double findMedianSortedArrays(int[] A, int[] B) {
        if (A.length > B.length) { int[] t = A; A = B; B = t; }
        int m = A.length, n = B.length, lo = 0, hi = m, half = (m + n + 1) / 2;
        while (lo <= hi) {
            int i = (lo + hi) / 2, j = half - i;
            int aL = i == 0 ? Integer.MIN_VALUE : A[i - 1];
            int aR = i == m ? Integer.MAX_VALUE : A[i];
            int bL = j == 0 ? Integer.MIN_VALUE : B[j - 1];
            int bR = j == n ? Integer.MAX_VALUE : B[j];
            if (aL <= bR && bL <= aR) {
                if (((m + n) & 1) == 1) return Math.max(aL, bL);
                return (Math.max(aL, bL) + Math.min(aR, bR)) / 2.0;
            } else if (aL > bR) hi = i - 1;
            else lo = i + 1;
        }
        return 0.0;
    }

N-Queens (Count) (Java) HARD

Problem: Count the number of distinct solutions to the n-queens puzzle using bitmask backtracking.
    static int totalNQueens(int n) {
        return solve(0, 0, 0, 0, n);
    }
    static int solve(int row, int cols, int d1, int d2, int n) {
        if (row == n) return 1;
        int count = 0;
        int available = ((1 << n) - 1) & ~(cols | d1 | d2);
        while (available != 0) {
            int p = available & (-available);
            available -= p;
            count += solve(row + 1, cols | p, (d1 | p) << 1, (d2 | p) >> 1, n);
        }
        return count;
    }

Coin Change (Java) HARD

Problem: Given coin denominations and a target amount, return the fewest coins needed to make the amount, or -1 if impossible.
    static int coinChange(int[] coins, int amount) {
        int[] dp = new int[amount + 1];
        Arrays.fill(dp, amount + 1);
        dp[0] = 0;
        for (int c : coins)
            for (int a = c; a <= amount; a++)
                dp[a] = Math.min(dp[a], dp[a - c] + 1);
        return dp[amount] > amount ? -1 : dp[amount];
    }

Union-Find / Disjoint Set (Java) HARD

Problem: Implement union-find with path compression and union by rank; count the connected components after a series of unions.
    static class UnionFind {
        private final int[] parent, rank;
        private int count;
        UnionFind(int n) {
            parent = new int[n];
            rank = new int[n];
            count = n;
            for (int i = 0; i < n; i++) parent[i] = i;
        }
        int find(int x) {
            while (parent[x] != x) {
                parent[x] = parent[parent[x]];
                x = parent[x];
            }
            return x;
        }
        void union(int a, int b) {
            int ra = find(a), rb = find(b);
            if (ra == rb) return;
            if (rank[ra] < rank[rb]) { int t = ra; ra = rb; rb = t; }
            parent[rb] = ra;
            if (rank[ra] == rank[rb]) rank[ra]++;
            count--;
        }
        int count() { return count; }
    }

Real-world & On-the-job

API + JSON - revenue by product EASY

Integration roles live on this: call a REST endpoint that returns a JSON list of orders and aggregate revenue per product, highest first. Standard library only - no external packages.

import json, urllib.request
from collections import defaultdict

def revenue_by_product(url):
    with urllib.request.urlopen(url, timeout=10) as resp:
        orders = json.load(resp)        # [{"product": "...", "price": 9.99, "qty": 2}, ...]

    totals = defaultdict(float)
    for o in orders:
        totals[o["product"]] += o["price"] * o["qty"]

    # highest-grossing first
    return sorted(totals.items(), key=lambda kv: kv[1], reverse=True)

SQL - group, filter & rank customers EASY

From Orders(id, customer_id, amount), list customers with more than 3 orders plus their total spend, biggest spenders first. Tests GROUP BY / HAVING / ORDER BY.

SELECT customer_id,
       COUNT(*)    AS order_count,
       SUM(amount) AS total_spent
FROM   Orders
GROUP BY customer_id
HAVING COUNT(*) > 3
ORDER BY total_spent DESC;

Unit testing / TDD EASY

Every listing wants tests, code reviews and Agile. Given is_valid_email, write tests that cover the happy path and the edge cases (missing @, empty, double @). Interviewers love when you test before you trust the code.

import unittest

def is_valid_email(s):
    return s.count("@") == 1 and "." in s.split("@")[1]

class TestEmail(unittest.TestCase):
    def test_valid(self):    self.assertTrue(is_valid_email("[email protected]"))
    def test_no_at(self):    self.assertFalse(is_valid_email("abc.com"))
    def test_empty(self):    self.assertFalse(is_valid_email(""))
    def test_two_ats(self):  self.assertFalse(is_valid_email("a@@b.com"))

if __name__ == "__main__":
    unittest.main()

Parse a Query String EASY

Problem: Parse a URL query string like 'a=1&b=2&c=3' into a dictionary of key/value pairs.
def parse_query_string(qs):
    result = {}
    if not qs:
        return result
    for pair in qs.split('&'):
        if '=' in pair:
            key, value = pair.split('=', 1)
            result[key] = value
        else:
            result[pair] = ''
    return result

Convert camelCase to snake_case EASY

Problem: Convert a camelCase identifier into snake_case (e.g. 'firstName' -> 'first_name').
def to_snake_case(s):
    result = []
    for c in s:
        if c.isupper():
            result.append('_')
            result.append(c.lower())
        else:
            result.append(c)
    return ''.join(result)

Convert snake_case to camelCase EASY

Problem: Convert a snake_case identifier into camelCase (e.g. 'first_name' -> 'firstName').
def to_camel_case(s):
    parts = s.split('_')
    return parts[0] + ''.join(word.capitalize() for word in parts[1:])

Format Cents as Currency EASY

Problem: Given an integer number of cents, return a formatted dollar string with thousands separators, e.g. 123456 -> '$1,234.56'.
def format_currency(cents):
    dollars = cents / 100
    return '${:,.2f}'.format(dollars)

Truncate Text With Ellipsis EASY

Problem: If the text is longer than max_len, cut it to max_len characters and append '...'; otherwise return it unchanged.
def truncate_text(text, max_len):
    if len(text) <= max_len:
        return text
    return text[:max_len] + '...'

Slugify a Title EASY

Problem: Turn a title into a URL slug: lowercase, spaces to hyphens, and drop characters that aren't letters, digits, or hyphens.
def slugify(title):
    out = []
    for c in title.lower():
        if c.isalnum():
            out.append(c)
        elif c in ' -_':
            out.append('-')
    slug = ''.join(out)
    while '--' in slug:
        slug = slug.replace('--', '-')
    return slug.strip('-')

Count Words in Text EASY

Problem: Return the number of whitespace-separated words in a block of text.
def count_words(text):
    return len(text.split())

Validate an Email Address EASY

Problem: Return True if the string looks like a basic email: exactly one '@', non-empty local and domain parts, and a dot in the domain.
def validate_email(email):
    if email.count('@') != 1:
        return False
    local, domain = email.split('@')
    if not local or not domain:
        return False
    return '.' in domain and not domain.startswith('.') and not domain.endswith('.')

Mask a Credit Card Number EASY

Problem: Return the card number with every digit hidden as '*' except the last four.
def mask_credit_card(number):
    digits = number.replace(' ', '').replace('-', '')
    if len(digits) <= 4:
        return digits
    return '*' * (len(digits) - 4) + digits[-4:]

Celsius to Fahrenheit EASY

Problem: Convert a Celsius temperature to Fahrenheit, rounded to one decimal place.
def celsius_to_fahrenheit(c):
    return round(c * 9 / 5 + 32, 1)

Plagiarism Detector - Longest Common Subsequence MEDIUM

To flag plagiarism between two stories, find the length of their longest shared subsequence of characters (same order, but gaps allowed). The naive "try every substring" approach is exponential; memoized recursion is O(n·m). Then run it over every pair of books to surface the most-likely match.

from functools import lru_cache

def longest_common_subsequence(a, b):
    @lru_cache(maxsize=None)
    def lcs(i, j):
        if i == len(a) or j == len(b):
            return 0
        if a[i] == b[j]:
            return 1 + lcs(i + 1, j + 1)            # match -> advance both
        return max(lcs(i + 1, j), lcs(i, j + 1))    # else skip one side
    return lcs(0, 0)

def most_likely_plagiarism(books):                  # books: list[(title, text)]
    best = (0, None, None)
    for i in range(len(books)):
        for j in range(i + 1, len(books)):
            score = longest_common_subsequence(books[i][1], books[j][1])
            if score > best[0]:
                best = (score, books[i][0], books[j][0])
    return best                                     # (shared_chars, titleA, titleB)

SQL - second-highest salary MEDIUM

SQL was the single most-requested hard skill across the listings. From Employee(id, name, salary), return the second-highest distinct salary (return NULL if there isn't one). Know both the sub-query and the window-function answer.

-- Classic sub-query
SELECT MAX(salary) AS second_highest
FROM   Employee
WHERE  salary < (SELECT MAX(salary) FROM Employee);

-- Modern, scales to "Nth highest" (window function)
SELECT DISTINCT salary AS second_highest
FROM (
    SELECT salary,
           DENSE_RANK() OVER (ORDER BY salary DESC) AS rnk
    FROM   Employee
) ranked
WHERE rnk = 2;

SQL - top earner per department MEDIUM

Join + correlated sub-query, a daily-driver pattern for data roles. List each department and the employee(s) earning the most within that department.

SELECT d.name AS department,
       e.name AS employee,
       e.salary
FROM   Employee  e
JOIN   Department d ON e.dept_id = d.id
WHERE  e.salary = (
    SELECT MAX(e2.salary)
    FROM   Employee e2
    WHERE  e2.dept_id = e.dept_id
)
ORDER BY d.name;

Parse a CSV Line With Quotes MEDIUM

Problem: Split a single CSV line into fields, respecting double-quoted fields that may contain commas.
def parse_csv_line(line):
    fields = []
    current = []
    in_quotes = False
    for c in line:
        if c == '"':
            in_quotes = not in_quotes
        elif c == ',' and not in_quotes:
            fields.append(''.join(current))
            current = []
        else:
            current.append(c)
    fields.append(''.join(current))
    return fields

Group Records by a Key MEDIUM

Problem: Given a list of dictionaries, group them into a dict of lists keyed by the value of the given field.
def group_by_key(records, key):
    groups = {}
    for record in records:
        groups.setdefault(record[key], []).append(record)
    return groups

Flatten a Nested Dictionary MEDIUM

Problem: Flatten a nested dictionary into a single level, joining nested keys with dots (e.g. {'a':{'b':1}} -> {'a.b':1}).
def flatten_dict(d, prefix=''):
    result = {}
    for key, value in d.items():
        full = prefix + key if not prefix else prefix + '.' + key
        if isinstance(value, dict):
            result.update(flatten_dict(value, full))
        else:
            result[full] = value
    return result

Deep Merge Two Dictionaries MEDIUM

Problem: Merge dictionary b into a recursively: nested dicts merge, and b's values win on conflicts. Return the merged result.
def deep_merge(a, b):
    result = dict(a)
    for key, value in b.items():
        if key in result and isinstance(result[key], dict) and isinstance(value, dict):
            result[key] = deep_merge(result[key], value)
        else:
            result[key] = value
    return result

Rate Limit Check MEDIUM

Problem: Given past request timestamps, a new timestamp, a limit, and a window, return True if allowing the new request stays within limit requests per window.
def rate_limit_ok(timestamps, now, limit, window):
    recent = [t for t in timestamps if t > now - window]
    return len(recent) < limit

Paginate a List MEDIUM

Problem: Return the slice of items for the given 1-based page number and page size.
def paginate(items, page, per_page):
    start = (page - 1) * per_page
    return items[start:start + per_page]

Parse a Duration String MEDIUM

Problem: Parse a duration like '1h30m15s' into a total number of seconds.
def parse_duration(s):
    units = {'h': 3600, 'm': 60, 's': 1}
    total = 0
    num = ''
    for c in s:
        if c.isdigit():
            num += c
        elif c in units:
            total += int(num) * units[c]
            num = ''
    return total

Deduplicate Preserving Order MEDIUM

Problem: Remove duplicate items from a list while keeping the order of first appearance.
def dedupe(items):
    seen = set()
    result = []
    for item in items:
        if item not in seen:
            seen.add(item)
            result.append(item)
    return result

Exponential Backoff Delays MEDIUM

Problem: Return the list of retry delays for the given number of attempts using exponential backoff (base * 2^i), capped at max_delay.
def backoff_delays(attempts, base, max_delay):
    delays = []
    for i in range(attempts):
        delay = min(base * (2 ** i), max_delay)
        delays.append(delay)
    return delays

Top N Items by Frequency MEDIUM

Problem: Return the n most frequent items as (item, count) pairs, ordered by descending count (ties by first appearance).
def top_n_by_count(items, n):
    from collections import Counter
    counts = Counter(items)
    return counts.most_common(n)

OOP System Design - Cloud E-Reader HARD

Design the core classes for an online cloud reading app (think Kindle, but for indie short stories). Requirements: a user has a library of books they can add to / remove from; they can set one book active; the app remembers where they left off in each book; and it shows one page at a time of the active book. Keep the Book and Library responsibilities separate so the storage format can change later.

class Book:
    """One story. content = list of page-strings; remembers the reader's spot."""
    def __init__(self, book_id, title, content):
        self.id = book_id
        self.title = title
        self.content = content          # list[str], one entry per page
        self.last_page = 0              # index into content

    def display_page(self):
        return self.content[self.last_page]

    def turn_page(self):
        if self.last_page < len(self.content) - 1:
            self.last_page += 1
        return self.display_page()


class Library:
    """A reader's collection of books + which one is open right now."""
    def __init__(self):
        self.collection = {}            # id -> Book  (dict = O(1) lookup)
        self.active_id = None
        self._next_id = 0               # simple unique-id generator

    def add_book(self, title, content):
        book = Book(self._next_id, title, content)
        self.collection[book.id] = book
        self._next_id += 1
        return book.id

    def remove_book(self, book_id):
        self.collection.pop(book_id, None)
        if self.active_id == book_id:
            self.active_id = None

    def set_active(self, book_id):
        if book_id in self.collection:
            self.active_id = book_id

    def display_page(self):
        if self.active_id is None: return None
        return self.collection[self.active_id].display_page()

    def turn_page(self):
        if self.active_id is None: return None
        return self.collection[self.active_id].turn_page()

# Follow-ups the interviewer will ask:
#  • Bigger font? Store content as ONE string + a Display class that computes
#    chars_per_page from font_size, then page n = content[n*cpp : (n+1)*cpp].
#  • Many users sharing books? Store each Book ONCE (e.g. a SQL table keyed by a
#    global id); keep per-user state (active_id, last_page) in a separate UserState.

Edit Distance (Levenshtein) HARD

The other DP every fuzzy-matching/plagiarism system uses: minimum single-character insert / delete / replace edits to turn string a into b. Bottom-up table, O(n·m).

def edit_distance(a, b):
    n, m = len(a), len(b)
    dp = [[0] * (m + 1) for _ in range(n + 1)]
    for i in range(n + 1): dp[i][0] = i        # delete all of a[:i]
    for j in range(m + 1): dp[0][j] = j        # insert all of b[:j]

    for i in range(1, n + 1):
        for j in range(1, m + 1):
            if a[i-1] == b[j-1]:
                dp[i][j] = dp[i-1][j-1]         # chars match -> free
            else:
                dp[i][j] = 1 + min(dp[i-1][j],      # delete
                                   dp[i][j-1],      # insert
                                   dp[i-1][j-1])    # replace
    return dp[n][m]

Compare Semantic Versions HARD

Problem: Compare two semantic version strings like '1.2.10' and '1.2.3'. Return -1, 0, or 1 (missing components count as 0).
def compare_versions(a, b):
    pa = [int(x) for x in a.split('.')]
    pb = [int(x) for x in b.split('.')]
    length = max(len(pa), len(pb))
    pa += [0] * (length - len(pa))
    pb += [0] * (length - len(pb))
    for x, y in zip(pa, pb):
        if x < y:
            return -1
        if x > y:
            return 1
    return 0

Parse CSV Into Records HARD

Problem: Given CSV text where the first row is the header, return a list of dictionaries mapping each column name to its value.
def csv_to_records_realworld(text):
    lines = [line for line in text.strip().split('\n') if line]
    if not lines:
        return []
    headers = lines[0].split(',')
    records = []
    for line in lines[1:]:
        values = line.split(',')
        records.append(dict(zip(headers, values)))
    return records

Get a Value by Dotted Path HARD

Problem: Given a nested structure of dicts and lists, return the value at a dotted path like 'user.roles.0.name', or None if any step is missing.
def get_by_path(obj, path):
    current = obj
    for part in path.split('.'):
        if isinstance(current, dict):
            if part not in current:
                return None
            current = current[part]
        elif isinstance(current, list):
            idx = int(part)
            if idx < 0 or idx >= len(current):
                return None
            current = current[idx]
        else:
            return None
    return current

Validate a Password HARD

Problem: Return a list of failed rules for a password: at least 8 chars, one uppercase, one lowercase, one digit, one special character. Empty list means valid.
def validate_password(pw):
    failures = []
    if len(pw) < 8:
        failures.append('too_short')
    if not any(c.isupper() for c in pw):
        failures.append('no_upper')
    if not any(c.islower() for c in pw):
        failures.append('no_lower')
    if not any(c.isdigit() for c in pw):
        failures.append('no_digit')
    if not any(not c.isalnum() for c in pw):
        failures.append('no_special')
    return failures

Diff Two Lists HARD

Problem: Compare an old and a new list and return a dict with the 'added' items (in new, not old) and 'removed' items (in old, not new), preserving order.
def diff_lists(old, new):
    old_set = set(old)
    new_set = set(new)
    added = [x for x in new if x not in old_set]
    removed = [x for x in old if x not in new_set]
    return {'added': added, 'removed': removed}

Parse an INI Config HARD

Problem: Parse simple INI text into a dict of sections, each a dict of key/value pairs. Ignore blank lines and ';' comments.
def parse_ini_realworld(text):
    config = {}
    section = None
    for line in text.split('\n'):
        line = line.strip()
        if not line or line.startswith(';'):
            continue
        if line.startswith('[') and line.endswith(']'):
            section = line[1:-1]
            config[section] = {}
        elif '=' in line and section is not None:
            key, value = line.split('=', 1)
            config[section][key.strip()] = value.strip()
    return config

Expand a Range Expression HARD

Problem: Expand a compact range string like '1-3,5,7-9' into the sorted list of integers it represents.
def expand_ranges(s):
    result = []
    for part in s.split(','):
        part = part.strip()
        if '-' in part:
            start, end = part.split('-')
            result.extend(range(int(start), int(end) + 1))
        else:
            result.append(int(part))
    return result

Format an Aligned Text Table HARD

Problem: Given rows of strings, return a single string where columns are left-aligned and padded to the width of the widest cell, rows joined by newlines.
def format_table(rows):
    if not rows:
        return ''
    cols = len(rows[0])
    widths = [max(len(row[c]) for row in rows) for c in range(cols)]
    lines = []
    for row in rows:
        lines.append('  '.join(cell.ljust(widths[c]) for c, cell in enumerate(row)))
    return '\n'.join(line.rstrip() for line in lines)

Normalize a File Path HARD

Problem: Simplify a Unix-style path, resolving '.' and '..' segments and collapsing repeated slashes. Return the canonical absolute path.
def normalize_path(path):
    parts = path.split('/')
    stack = []
    for part in parts:
        if part == '' or part == '.':
            continue
        if part == '..':
            if stack:
                stack.pop()
        else:
            stack.append(part)
    return '/' + '/'.join(stack)

Parse a Web Server Log Line HARD

Problem: Parse a Common Log Format line into a dict with ip, method, path, status, and size fields.
def parse_log_line(line):
    import re
    pattern = r'(\S+) \S+ \S+ \[[^\]]+\] "(\S+) (\S+) [^"]*" (\d+) (\d+)'
    m = re.match(pattern, line)
    if not m:
        return None
    return {
        'ip': m.group(1),
        'method': m.group(2),
        'path': m.group(3),
        'status': int(m.group(4)),
        'size': int(m.group(5)),
    }

🏢 Real-world & on-the-job problems

Pulled straight from what 2026 job listings actually ask for - across data, finance, cloud and full-stack roles the recurring hard skills are SQL, OOP/system design, dynamic programming, APIs/JSON and unit testing. These are worked end-to-end, the way a real 45-minute interview runs (design first, then an algorithm). The two headline problems below are the exact format of a full-length mock interview: an open-ended OOP design, then a DP algorithm.

Mixed & Hardest

Optimal Freelancing EASY

Max profit picking jobs (1 day each) by their deadlines within a 7-day week. Greedy by pay.

class Solution:
    def optimal_freelancing(self, jobs):
        LIMIT = 7
        jobs.sort(key=lambda j: j["payment"], reverse=True)
        taken = [False] * LIMIT; profit = 0
        for job in jobs:
            for day in range(min(job["deadline"], LIMIT) - 1, -1, -1):
                if not taken[day]:
                    taken[day] = True; profit += job["payment"]; break
        return profit

Knight Connection (two knights meet) MEDIUM

Min total moves for two knights to land on the same square. BFS from one; they move alternately.

from collections import deque

class Solution:
    def knight_connection(self, a, b):
        moves = [(2,1),(2,-1),(-2,1),(-2,-1),(1,2),(1,-2),(-1,2),(-1,-2)]
        q = deque([(a[0], a[1], 0)]); seen = {tuple(a)}
        while q:
            r, c, d = q.popleft()
            if [r, c] == b: return (d + 1) // 2
            for dr, dc in moves:
                nxt = (r + dr, c + dc)
                if nxt not in seen:
                    seen.add(nxt); q.append((nxt[0], nxt[1], d + 1))

Split Binary Tree MEDIUM

Can one cut split the tree into two equal-sum halves? Returns that half-sum or 0.

class Solution:
    def split_binary_tree(self, tree):
        def total(node):
            return 0 if not node else node.value + total(node.left) + total(node.right)
        whole = total(tree)
        if whole % 2: return 0
        found = [False]
        def helper(node):
            if not node: return 0
            s = node.value + helper(node.left) + helper(node.right)
            if s == whole // 2: found[0] = True
            return s
        helper(tree)
        return whole // 2 if found[0] else 0

Merging Linked Lists (find intersection) MEDIUM

Where two lists join. Swap each pointer to the other list's head when it ends - they meet at the join. O(n).

class Solution:
    def merging_linked_lists(self, l1, l2):
        a, b = l1, l2
        while a != b:
            a = a.next if a else l2
            b = b.next if b else l1
        return a            # the shared node (or None)

Min Heap Construction (from scratch) MEDIUM

class MinHeap:
    def __init__(self, array):
        self.heap = array
        for i in range((len(array) - 2) // 2, -1, -1):   # heapify, O(n)
            self.sift_down(i)
    def sift_down(self, i):
        n = len(self.heap)
        while 2*i + 1 < n:
            child = 2*i + 1
            if 2*i + 2 < n and self.heap[2*i+2] < self.heap[child]:
                child = 2*i + 2
            if self.heap[child] < self.heap[i]:
                self.heap[i], self.heap[child] = self.heap[child], self.heap[i]; i = child
            else: break
    def sift_up(self, i):
        while i > 0 and self.heap[i] < self.heap[(i-1)//2]:
            self.heap[i], self.heap[(i-1)//2] = self.heap[(i-1)//2], self.heap[i]; i = (i-1)//2
    def insert(self, v):
        self.heap.append(v); self.sift_up(len(self.heap) - 1)
    def remove(self):
        self.heap[0], self.heap[-1] = self.heap[-1], self.heap[0]
        v = self.heap.pop(); self.sift_down(0); return v

Juice Bottling MEDIUM

Split N liters into bottle sizes to maximize total price (like rod-cutting), returning the split. O(n²).

class Solution:
    def juice_bottling(self, prices):
        n = len(prices) - 1
        dp = [0] * len(prices); splits = [[] for _ in prices]
        for size in range(1, len(prices)):
            for liters in range(1, size + 1):
                if dp[size - liters] + prices[liters] > dp[size]:
                    dp[size] = dp[size - liters] + prices[liters]
                    splits[size] = splits[size - liters] + [liters]
        return splits[n]

Zero Sum Subarray MEDIUM

Does any subarray sum to 0? If a running sum repeats, the slice between is 0. O(n).

class Solution:
    def zero_sum_subarray(self, nums):
        seen = {0}; running = 0
        for n in nums:
            running += n
            if running in seen: return True
            seen.add(running)
        return False

Binary Tree Diameter MEDIUM

class Solution:
    def binary_tree_diameter(self, tree):
        diameter = [0]
        def height(node):
            if not node: return 0
            lh, rh = height(node.left), height(node.right)
            diameter[0] = max(diameter[0], lh + rh)
            return max(lh, rh) + 1
        height(tree)
        return diameter[0]

Minimum Passes Of Matrix MEDIUM

Each pass, positives convert their negative neighbors. How many passes to flip all? Multi-source BFS. O(w·h).

from collections import deque

class Solution:
    def minimum_passes_of_matrix(self, matrix):
        q = deque((r, c) for r in range(len(matrix))
                  for c in range(len(matrix[0])) if matrix[r][c] > 0)
        passes = 0
        while q:
            for _ in range(len(q)):
                r, c = q.popleft()
                for dr, dc in ((1,0),(-1,0),(0,1),(0,-1)):
                    nr, nc = r+dr, c+dc
                    if 0 <= nr < len(matrix) and 0 <= nc < len(matrix[0]) and matrix[nr][nc] < 0:
                        matrix[nr][nc] *= -1; q.append((nr, nc))
            passes += 1
        return -1 if any(v < 0 for row in matrix for v in row) else max(passes - 1, 0)

Best Digits (remove k to maximize) MEDIUM

class Solution:
    def best_digits(self, number, k):
        stack = []
        for d in number:
            while k > 0 and stack and stack[-1] < d:
                stack.pop(); k -= 1
            stack.append(d)
        return "".join(stack[:len(stack) - k]) if k else "".join(stack)

Colliding Asteroids MEDIUM

Positive = moving right, negative = left. Use a stack; resolve collisions. O(n).

class Solution:
    def colliding_asteroids(self, asteroids):
        stack = []
        for a in asteroids:
            alive = True
            while alive and a < 0 and stack and stack[-1] > 0:
                if stack[-1] < -a: stack.pop()
                elif stack[-1] == -a: stack.pop(); alive = False
                else: alive = False
            if alive: stack.append(a)
        return stack

Minimum Characters For Words MEDIUM

from collections import Counter

class Solution:
    def minimum_characters_for_words(self, words):
        max_counts = {}
        for word in words:
            for ch, cnt in Counter(word).items():
                max_counts[ch] = max(max_counts.get(ch, 0), cnt)
        out = []
        for ch, cnt in max_counts.items(): out += [ch] * cnt
        return out

Longest Balanced Substring HARD

Longest run of valid () parentheses. Stack of indices. O(n).

class Solution:
    def longest_balanced(self, s):
        stack = [-1]; longest = 0
        for i, ch in enumerate(s):
            if ch == "(":
                stack.append(i)
            else:
                stack.pop()
                if not stack: stack.append(i)
                else: longest = max(longest, i - stack[-1])
        return longest

Right Smaller Than HARD

For each element, how many to its right are smaller. Insert into a sorted list from the right. O(n²) (O(n log n) with a BIT).

import bisect

class Solution:
    def right_smaller_than(self, arr):
        res = [0] * len(arr); sorted_seen = []
        for i in range(len(arr) - 1, -1, -1):
            pos = bisect.bisect_left(sorted_seen, arr[i])
            res[i] = pos
            sorted_seen.insert(pos, arr[i])
        return res

Maximize Expression HARD

Maximize a[i]−a[j]+a[k]−a[l] for i<j<k<l. Build four running-best arrays. O(n).

class Solution:
    def maximize_expression(self, arr):
        n = len(arr)
        if n < 4: return 0
        maxA = [arr[0]] * n
        for i in range(1, n): maxA[i] = max(maxA[i-1], arr[i])
        maxAB = [float("-inf")] * n
        for i in range(1, n): maxAB[i] = max(maxAB[i-1], maxA[i-1] - arr[i])
        maxABC = [float("-inf")] * n
        for i in range(2, n): maxABC[i] = max(maxABC[i-1], maxAB[i-1] + arr[i])
        maxABCD = [float("-inf")] * n
        for i in range(3, n): maxABCD[i] = max(maxABCD[i-1], maxABC[i-1] - arr[i])
        return maxABCD[n-1]

Repair BST (two nodes swapped) HARD

An in-order walk of a BST is sorted - the two out-of-order nodes are the swapped pair. O(n).

class Solution:
    def repair_bst(self, tree):
        first = second = prev = None
        def inorder(node):
            nonlocal first, second, prev
            if not node: return
            inorder(node.left)
            if prev and prev.value > node.value:
                if not first: first = prev
                second = node
            prev = node
            inorder(node.right)
        inorder(tree)
        first.value, second.value = second.value, first.value
        return tree

Validate Three Nodes HARD

Is the middle node a descendant of one outer node and an ancestor of the other? O(h).

class Solution:
    def validate_three_nodes(self, one, two, three):
        def is_descendant(node, target):
            while node and node != target:
                node = node.left if target.value < node.value else node.right
            return node == target
        if is_descendant(two, one):  return is_descendant(three, two)
        if is_descendant(two, three): return is_descendant(one, two)
        return False

Line Through Points HARD

Max points on a single straight line. Group by reduced slope (use gcd to normalize). O(n²).

from math import gcd

class Solution:
    def line_through_points(self, points):
        best = 1
        for i in range(len(points)):
            slopes = {}
            for j in range(i + 1, len(points)):
                dx = points[j][0] - points[i][0]
                dy = points[j][1] - points[i][1]
                g = gcd(dx, dy) or 1
                slope = (dx // g, dy // g)
                slopes[slope] = slopes.get(slope, 1) + 1
                best = max(best, slopes[slope])
        return best

Stable Internships (Gale-Shapley) HARD

Match interns ↔ teams so no pair would both rather swap. The classic stable-matching algorithm.

class Solution:
    def stable_internships(self, interns, teams):
        chosen = {}                       # team -> intern
        free = list(range(len(interns)))
        next_choice = [0] * len(interns)
        while free:
            intern = free.pop(0)
            team = interns[intern][next_choice[intern]]
            next_choice[intern] += 1
            if team not in chosen:
                chosen[team] = intern
            else:
                current = chosen[team]; pref = teams[team]
                if pref.index(intern) < pref.index(current):
                    chosen[team] = intern; free.append(current)
                else:
                    free.append(intern)
        return [[team, intern] for team, intern in chosen.items()]

Shortest Unique Prefixes HARD

Shortest prefix of each word that no other word shares. Trie with counts. O(total chars).

class Solution:
    def shortest_unique_prefixes(self, strings):
        trie = {}
        for s in strings:                 # build trie, counting visits
            node = trie
            for ch in s:
                node = node.setdefault(ch, {"count": 0})
                node["count"] += 1
        res = []
        for s in strings:
            node = trie; prefix = ""
            for ch in s:
                node = node[ch]; prefix += ch
                if node["count"] == 1: break   # unique from here
            res.append(prefix)
        return res

Square of Zeroes HARD

Is there a square whose border is all 0s? Precompute consecutive zeros right/down, then check each square. O(n³).

class Solution:
    def square_of_zeroes(self, matrix):
        n = len(matrix)
        info = [[[0, 0] for _ in range(n)] for _ in range(n)]   # [right, down]
        for r in range(n - 1, -1, -1):
            for c in range(n - 1, -1, -1):
                if matrix[r][c] == 0:
                    info[r][c][0] = 1 + (info[r][c+1][0] if c+1 < n else 0)
                    info[r][c][1] = 1 + (info[r+1][c][1] if r+1 < n else 0)
        for r in range(n):
            for c in range(n):
                for size in range(2, n - max(r, c) + 1):
                    br, bc = r + size - 1, c + size - 1
                    if (info[r][c][0] >= size and info[r][c][1] >= size and
                            info[r][bc][1] >= size and info[br][c][0] >= size):
                        return True
        return False

Optimal Assembly Line HARD

Min possible max-station-time when splitting ordered steps across k stations. Binary-search the answer. O(n log(sum)).

class Solution:
    def optimal_assembly_line(self, durations, num_stations):
        def feasible(max_time):
            used, current = 1, 0
            for d in durations:
                if d > max_time: return False
                if current + d > max_time: used += 1; current = d
                else: current += d
            return used <= num_stations
        lo, hi, best = max(durations), sum(durations), sum(durations)
        while lo <= hi:
            mid = (lo + hi) // 2
            if feasible(mid): best = mid; hi = mid - 1
            else: lo = mid + 1
        return best

Pattern Matcher HARD

Given a pattern of x's & y's, find strings for x and y that rebuild s. Try every length of x. O(n²).

class Solution:
    def pattern_matcher(self, pattern, s):
        if len(pattern) > len(s): return []
        swapped = pattern[0] != "x"
        p = ["x" if c == "y" else "y" for c in pattern] if swapped else list(pattern)
        cx, cy = p.count("x"), p.count("y")
        first_y = p.index("y") if cy else None
        if cy:
            for lx in range(1, len(s) // cx + 1):
                rem = len(s) - lx * cx
                if rem % cy: continue
                ly = rem // cy
                yi = first_y * lx
                x, y = s[:lx], s[yi:yi + ly]
                if "".join(x if c == "x" else y for c in p) == s:
                    return [y, x] if swapped else [x, y]
        else:
            if len(s) % cx: return []
            x = s[: len(s) // cx]
            if x * cx == s:
                return ["", x] if swapped else [x, ""]
        return []

Right Sibling Tree HARD

Rewire every node's right to point to its sibling on the same level. Recurse left before mutating right.

class Solution:
    def right_sibling_tree(self, root):
        def mutate(node, parent, is_left):
            if node is None: return
            left, right = node.left, node.right
            mutate(left, node, True)
            if parent is None: node.right = None
            elif is_left: node.right = parent.right
            else: node.right = parent.right.left if parent.right else None
            mutate(right, node, False)
        mutate(root, None, False)
        return root

Airport Connections HARD

Fewest new routes so every airport is reachable from the start. Score unreachable airports by how many other unreachable ones they unlock; add greedily.

class Solution:
    def airport_connections(self, airports, routes, start):
        graph = {a: [] for a in airports}
        for src, dst in routes: graph[src].append(dst)
        reachable = set()
        def dfs(node):
            if node in reachable: return
            reachable.add(node)
            for nb in graph[node]: dfs(nb)
        dfs(start)
        def reach_count(node, seen):
            if node in seen: return 0
            seen.add(node)
            total = 0 if node in reachable else 1
            for nb in graph[node]: total += reach_count(nb, seen)
            return total
        scored = sorted(((a, reach_count(a, set())) for a in airports if a not in reachable),
                        key=lambda x: -x[1])
        connections = 0
        for airport, _ in scored:
            if airport in reachable: continue
            connections += 1
            dfs(airport)
        return connections

Largest Park HARD

Biggest rectangle of empty land (0s) in a grid. Row-by-row histogram + largest-rectangle. O(rows·cols).

class Solution:
    def largest_park(self, land):
        cols = len(land[0]); heights = [0] * cols; best = 0
        for row in land:
            for c in range(cols):
                heights[c] = 0 if row[c] == 1 else heights[c] + 1
            stack = []                              # largest rectangle in histogram
            for i in range(cols + 1):
                h = heights[i] if i < cols else 0
                while stack and heights[stack[-1]] >= h:
                    height = heights[stack.pop()]
                    width = i if not stack else i - stack[-1] - 1
                    best = max(best, height * width)
                stack.append(i)
        return best

Sum BSTs (count valid BST subtrees) HARD

class Solution:
    def count_bsts(self, tree):
        count = [0]
        def helper(node):                # returns (is_bst, min, max)
            if not node: return (True, float("inf"), float("-inf"))
            lb, lmin, lmax = helper(node.left)
            rb, rmin, rmax = helper(node.right)
            is_bst = lb and rb and lmax < node.value < rmin
            if is_bst: count[0] += 1
            return (is_bst, min(lmin, node.value), max(rmax, node.value))
        helper(tree)
        return count[0]

Doubly Linked List (construction) HARD

class DLL:
    def __init__(self): self.head = self.tail = None
    def set_head(self, node):
        if not self.head: self.head = self.tail = node; return
        self.insert_before(self.head, node)
    def insert_before(self, node, new):
        self.remove(new)
        new.prev, new.next = node.prev, node
        if node.prev is None: self.head = new
        else: node.prev.next = new
        node.prev = new
    def remove(self, node):
        if node is self.head: self.head = node.next
        if node is self.tail: self.tail = node.prev
        if node.prev: node.prev.next = node.next
        if node.next: node.next.prev = node.prev
        node.prev = node.next = None

Blackjack Probability HARD

Chance the dealer busts (goes over the target). Dealer draws (cards 1-10) until within 4 of target. Memoized.

class Solution:
    def blackjack_probability(self, target, starting):
        cache = {}
        def helper(current):
            if current > target: return 1.0          # busted
            if current + 4 >= target: return 0.0      # dealer stands, safe
            if current in cache: return cache[current]
            cache[current] = sum(helper(current + d) for d in range(1, 11)) / 10
            return cache[current]
        return round(helper(starting), 3)

Two-Edge-Connected Graph HARD

Is the graph connected with no "bridge" edges (every edge lies on a cycle)? DFS arrival/low times (Tarjan). O(V+E).

class Solution:
    def two_edge_connected(self, edges):
        n = len(edges)
        if n == 0: return True
        arrival = [-1] * n
        def dfs(node, prev, time):
            arrival[node] = time; lowest = time
            for nb in edges[node]:
                if arrival[nb] == -1:
                    low = dfs(nb, node, time + 1)
                    if low == -1: return -1            # bridge below
                    if low <= arrival[node] and node != 0 is False: pass
                    lowest = min(lowest, low)
                elif nb != prev:
                    lowest = min(lowest, arrival[nb])
            if lowest == arrival[node] and node != 0:  # edge to parent is a bridge
                return -1
            return lowest
        if dfs(0, -1, 0) == -1: return False
        return all(a != -1 for a in arrival)           # also fully connected

Count Squares HARD

How many squares can be formed from a set of points? Check each pair as a diagonal. O(n²).

class Solution:
    def count_squares(self, points):
        pts = {(x, y) for x, y in points}; count = 0
        for x1, y1 in points:
            for x2, y2 in points:
                if (x1, y1) == (x2, y2): continue
                mx, my = (x1 + x2) / 2, (y1 + y2) / 2
                dx, dy = x1 - mx, y1 - my
                if (mx - dy, my + dx) in pts and (mx + dy, my - dx) in pts:
                    count += 1
        return count // 4                       # each square counted 4×

Largest Island HARD

Flip one water cell to land - what's the biggest land block possible? Label regions, then test each water cell. O(w·h).

class Solution:
    def largest_island(self, grid):
        n, m = len(grid), len(grid[0])
        rid = [[-1]*m for _ in range(n)]; sizes = {}; cur = 0
        def fill(r, c, cur):
            stack = [(r, c)]; size = 0
            while stack:
                i, j = stack.pop()
                if 0<=i<n and 0<=j<m and grid[i][j]==0 and rid[i][j]==-1:
                    rid[i][j] = cur; size += 1
                    stack += [(i+1,j),(i-1,j),(i,j+1),(i,j-1)]
            return size
        for r in range(n):
            for c in range(m):
                if grid[r][c]==0 and rid[r][c]==-1:
                    sizes[cur] = fill(r, c, cur); cur += 1
        best = max(sizes.values(), default=0)
        for r in range(n):
            for c in range(m):
                if grid[r][c]==1:
                    ids = {rid[r+dr][c+dc] for dr, dc in ((1,0),(-1,0),(0,1),(0,-1))
                           if 0<=r+dr<n and 0<=c+dc<m and grid[r+dr][c+dc]==0}
                    best = max(best, 1 + sum(sizes[i] for i in ids))
        return best

Sort K-Sorted Array HARD

Every element is at most k spots from its sorted position. A size-(k+1) min-heap. O(n log k).

import heapq

class Solution:
    def sort_k_sorted(self, array, k):
        heap = array[:k+1]; heapq.heapify(heap); idx = 0
        for i in range(k+1, len(array)):
            array[idx] = heapq.heappop(heap); idx += 1
            heapq.heappush(heap, array[i])
        while heap:
            array[idx] = heapq.heappop(heap); idx += 1
        return array

Merge K Sorted Arrays HARD

import heapq

class Solution:
    def merge_sorted_arrays(self, arrays):
        heap = [(arr[0], i, 0) for i, arr in enumerate(arrays) if arr]
        heapq.heapify(heap); res = []
        while heap:
            val, a, e = heapq.heappop(heap); res.append(val)
            if e + 1 < len(arrays[a]):
                heapq.heappush(heap, (arrays[a][e+1], a, e+1))
        return res

Shift Linked List HARD

Rotate a list by k. Make it circular, then break it at the right spot. O(n).

class Solution:
    def shift_linked_list(self, head, k):
        length = 1; tail = head
        while tail.next: tail = tail.next; length += 1
        k %= length
        if k == 0: return head
        tail.next = head                          # circular
        new_tail = head
        for _ in range(length - k - 1): new_tail = new_tail.next
        new_head = new_tail.next; new_tail.next = None
        return new_head

Lowest Common Manager HARD

LCA on an org chart (n-ary tree, no parent links). Count how many of the two appear in each subtree. O(n).

class Solution:
    def lowest_common_manager(self, top, one, two):
        def helper(manager):
            count = 0
            for report in manager.direct_reports:
                found, lca = helper(report)
                if lca: return 0, lca
                count += found
            if manager in (one, two): count += 1
            return count, (manager if count == 2 else None)
        return helper(top)[1]

Ambiguous Measurements HARD

Can imprecise measuring cups produce a target range? Memoized recursion.

class Solution:
    def ambiguous_measurements(self, cups, low, high):
        cache = {}
        def can(low, high):
            if low <= 0 and high <= 0: return False
            if (low, high) in cache: return cache[(low, high)]
            result = False
            for cl, ch in cups:
                if low <= cl and ch <= high:
                    result = True; break
                if can(max(0, low - cl), max(0, high - ch)):
                    result = True; break
            cache[(low, high)] = result
            return result
        return can(low, high)

Shorten Path HARD

Simplify a Unix-style path (handle ., .., extra slashes). Stack. O(n).

class Solution:
    def shorten_path(self, path):
        is_abs = path[0] == "/"
        tokens = [t for t in path.split("/") if t and t != "."]
        stack = [""] if is_abs else []
        for t in tokens:
            if t == "..":
                if not stack or stack[-1] == "..":
                    if not is_abs: stack.append(t)
                elif stack[-1] != "":
                    stack.pop()
            else:
                stack.append(t)
        if stack == [""]: return "/"
        return "/".join(stack)

Rearrange Linked List (around a value) HARD

Reorder so everything < k comes before == k before > k. Build three sublists, then stitch. O(n).

class Solution:
    def rearrange_linked_list(self, head, k):
        parts = {"less": [None, None], "equal": [None, None], "greater": [None, None]}
        node = head
        while node:
            nxt = node.next; node.next = None
            b = "less" if node.value < k else "greater" if node.value > k else "equal"
            h, t = parts[b]
            if not h: parts[b] = [node, node]
            else: t.next = node; parts[b][1] = node
            node = nxt
        new_head = prev_tail = None
        for h, t in (parts["less"], parts["equal"], parts["greater"]):
            if h is None: continue
            if new_head is None: new_head = h
            if prev_tail: prev_tail.next = h
            prev_tail = t
        return new_head

Laptop Rentals HARD

Min laptops for overlapping rental times. Sort starts & ends, sweep with two pointers. O(n log n).

class Solution:
    def laptop_rentals(self, times):
        if not times: return 0
        starts = sorted(t[0] for t in times)
        ends = sorted(t[1] for t in times)
        used = max_used = s = e = 0
        while s < len(starts):
            if starts[s] < ends[e]:
                used += 1; s += 1; max_used = max(max_used, used)
            else:
                used -= 1; e += 1
        return max_used

Strings Made Up Of Strings HARD

Which big strings can be built by concatenating the smaller ones (word-break)? DP per string. O(n·L²).

class Solution:
    def strings_made_up_of_strings(self, strings, substrings):
        subs = set(substrings)
        max_len = max((len(s) for s in substrings), default=0)
        out = []
        for s in strings:
            n = len(s); dp = [False] * (n + 1); dp[0] = True
            for i in range(1, n + 1):
                for j in range(max(0, i - max_len), i):
                    if dp[j] and s[j:i] in subs:
                        dp[i] = True; break
            if dp[n]: out.append(s)
        return out

Longest Most Frequent Prefix HARD

Longest prefix shared by the most strings. Build a counting trie, then follow the most-visited path. O(total chars).

class Solution:
    def longest_most_frequent_prefix(self, strings):
        trie = {}
        for s in strings:
            node = trie
            for ch in s:
                node = node.setdefault(ch, {"_count": 0})
                node["_count"] += 1
        best_count = len(strings)
        node = trie; prefix = ""
        while True:
            nxt = next(((ch, c) for ch, c in node.items()
                        if ch != "_count" and c["_count"] == best_count), None)
            if not nxt: break
            prefix += nxt[0]; node = nxt[1]
        return prefix

Waterfall Streams HARD

Water poured at a source flows down, splitting 50/50 around walls (1s). Return how much reaches each bottom slot. Row-by-row simulation; carry % as negatives.

class Solution:
    def waterfall_streams(self, array, source):
        rows = [r[:] for r in array]
        rows[0][source] = -1                      # -1 == 100% water
        for r in range(len(rows) - 1):
            for c in range(len(rows[r])):
                cur = rows[r][c]
                if cur >= 0: continue              # no water here
                if rows[r+1][c] == 0:              # falls straight down
                    rows[r+1][c] += cur; continue
                split = cur / 2                    # blocked → split left/right
                left = c
                while left - 1 >= 0:
                    left -= 1
                    if rows[r][left] == 1: break
                    if rows[r+1][left] == 0: rows[r+1][left] += split; break
                right = c
                while right + 1 < len(rows[r]):
                    right += 1
                    if rows[r][right] == 1: break
                    if rows[r+1][right] == 0: rows[r+1][right] += split; break
        return [(-v) * 100 if v < 0 else 0 for v in rows[-1]]
🎯 Interview tip: out loud, (1) restate the problem, (2) give a brute-force idea + its Big-O, (3) improve it with a pattern, (4) code it, (5) test on an example and edge cases (empty input, one element). Talking through it matters as much as the answer.

Top 150

🏆 Top 150 Interview Questions

The 150 questions companies ask most, grouped by topic. Below are concise, original Python solutions for each. (Some overlap the classics above; included here so this set is self-contained.)

Array / String

Merge Sorted Array EASY

Merge nums2 into nums1 in place - fill from the back to avoid overwriting. O(m+n).

class Solution:
    def merge(self, nums1, m, nums2, n):
        i, j, k = m - 1, n - 1, m + n - 1
        while j >= 0:
            if i >= 0 and nums1[i] > nums2[j]:
                nums1[k] = nums1[i]; i -= 1
            else:
                nums1[k] = nums2[j]; j -= 1
            k -= 1

Remove Element EASY

class Solution:
    def remove_element(self, nums, val):
        k = 0
        for n in nums:
            if n != val: nums[k] = n; k += 1
        return k

Remove Duplicates from Sorted Array EASY

class Solution:
    def remove_duplicates(self, nums):
        if not nums: return 0
        k = 1
        for i in range(1, len(nums)):
            if nums[i] != nums[k-1]:
                nums[k] = nums[i]; k += 1
        return k

Remove Duplicates from Sorted Array II (keep 2) MEDIUM

class Solution:
    def remove_duplicates_ii(self, nums):
        k = 0
        for n in nums:
            if k < 2 or n != nums[k-2]:
                nums[k] = n; k += 1
        return k

Majority Element EASY

class Solution:
    def majority_element(self, nums):
        count = 0; cand = None
        for n in nums:
            if count == 0: cand = n
            count += 1 if n == cand else -1
        return cand

Rotate Array MEDIUM

class Solution:
    def rotate(self, nums, k):
        k %= len(nums)
        nums[:] = nums[-k:] + nums[:-k]

Best Time to Buy and Sell Stock EASY

class Solution:
    def max_profit(self, prices):
        min_price = float("inf"); profit = 0
        for p in prices:
            min_price = min(min_price, p)
            profit = max(profit, p - min_price)
        return profit

Best Time to Buy and Sell Stock II MEDIUM

Unlimited trades → grab every upward step. O(n).

class Solution:
    def max_profit_ii(self, prices):
        return sum(max(0, prices[i] - prices[i-1]) for i in range(1, len(prices)))

Jump Game MEDIUM

class Solution:
    def can_jump(self, nums):
        reach = 0
        for i, n in enumerate(nums):
            if i > reach: return False
            reach = max(reach, i + n)
        return True

Jump Game II (min jumps) MEDIUM

class Solution:
    def jump(self, nums):
        jumps = end = farthest = 0
        for i in range(len(nums) - 1):
            farthest = max(farthest, i + nums[i])
            if i == end:
                jumps += 1; end = farthest
        return jumps

H-Index MEDIUM

class Solution:
    def h_index(self, citations):
        citations.sort(reverse=True)
        h = 0
        for i, c in enumerate(citations):
            if c >= i + 1: h = i + 1
            else: break
        return h

Insert Delete GetRandom O(1) MEDIUM

Array for O(1) random + dict of value→index; on remove, swap with the last element.

import random
class RandomizedSet:
    def __init__(self):
        self.data = []; self.idx = {}
    def insert(self, val):
        if val in self.idx: return False
        self.idx[val] = len(self.data); self.data.append(val); return True
    def remove(self, val):
        if val not in self.idx: return False
        i = self.idx[val]; last = self.data[-1]
        self.data[i] = last; self.idx[last] = i
        self.data.pop(); del self.idx[val]; return True
    def getRandom(self):
        return random.choice(self.data)

Product of Array Except Self MEDIUM

Prefix products left-to-right, then multiply by suffix products right-to-left. No division. O(n).

class Solution:
    def product_except_self(self, nums):
        n = len(nums); res = [1] * n
        left = 1
        for i in range(n):
            res[i] = left; left *= nums[i]
        right = 1
        for i in range(n - 1, -1, -1):
            res[i] *= right; right *= nums[i]
        return res

Gas Station MEDIUM

class Solution:
    def can_complete_circuit(self, gas, cost):
        if sum(gas) < sum(cost): return -1
        total = start = 0
        for i in range(len(gas)):
            total += gas[i] - cost[i]
            if total < 0: total = 0; start = i + 1
        return start

Candy HARD

Two passes: reward left-to-right for rising ratings, then right-to-left. O(n).

class Solution:
    def candy(self, ratings):
        n = len(ratings); c = [1] * n
        for i in range(1, n):
            if ratings[i] > ratings[i-1]: c[i] = c[i-1] + 1
        for i in range(n - 2, -1, -1):
            if ratings[i] > ratings[i+1]: c[i] = max(c[i], c[i+1] + 1)
        return sum(c)

Trapping Rain Water HARD

class Solution:
    def trap(self, height):
        left, right = 0, len(height) - 1
        left_max = right_max = water = 0
        while left < right:
            if height[left] < height[right]:
                left_max = max(left_max, height[left])
                water += left_max - height[left]; left += 1
            else:
                right_max = max(right_max, height[right])
                water += right_max - height[right]; right -= 1
        return water

Roman to Integer EASY

class Solution:
    def roman_to_int(self, s):
        v = {"I":1,"V":5,"X":10,"L":50,"C":100,"D":500,"M":1000}
        total = 0
        for i in range(len(s)):
            if i + 1 < len(s) and v[s[i]] < v[s[i+1]]: total -= v[s[i]]
            else: total += v[s[i]]
        return total

Integer to Roman MEDIUM

class Solution:
    def int_to_roman(self, num):
        vals = [(1000,"M"),(900,"CM"),(500,"D"),(400,"CD"),(100,"C"),(90,"XC"),
                (50,"L"),(40,"XL"),(10,"X"),(9,"IX"),(5,"V"),(4,"IV"),(1,"I")]
        res = []
        for v, sym in vals:
            while num >= v: res.append(sym); num -= v
        return "".join(res)

Length of Last Word EASY

class Solution:
    def length_of_last_word(self, s):
        parts = s.split()
        return len(parts[-1]) if parts else 0

Longest Common Prefix EASY

class Solution:
    def longest_common_prefix(self, strs):
        if not strs: return ""
        prefix = strs[0]
        for s in strs[1:]:
            while not s.startswith(prefix):
                prefix = prefix[:-1]
                if not prefix: return ""
        return prefix

Reverse Words in a String MEDIUM

class Solution:
    def reverse_words(self, s):
        return " ".join(s.split()[::-1])

Zigzag Conversion MEDIUM

class Solution:
    def convert(self, s, num_rows):
        if num_rows == 1: return s
        rows = [""] * num_rows
        r, step = 0, 1
        for c in s:
            rows[r] += c
            if r == 0: step = 1
            elif r == num_rows - 1: step = -1
            r += step
        return "".join(rows)

Find the Index of First Occurrence EASY

class Solution:
    def str_str(self, haystack, needle):
        n, m = len(haystack), len(needle)
        for i in range(n - m + 1):
            if haystack[i:i+m] == needle: return i
        return -1

Text Justification HARD

Greedily pack words per line, then distribute spaces evenly (extra spaces go to the left gaps); last line is left-justified.

class Solution:
    def full_justify(self, words, max_width):
        res = []; line = []; length = 0
        for w in words:
            if length + len(line) + len(w) > max_width:
                slots = max(1, len(line) - 1)
                for i in range(max_width - length):
                    line[i % slots] += " "
                res.append("".join(line)); line = []; length = 0
            line.append(w); length += len(w)
        res.append(" ".join(line).ljust(max_width))
        return res

Two Pointers

Valid Palindrome EASY

class Solution:
    def is_palindrome(self, s):
        cleaned = [c.lower() for c in s if c.isalnum()]
        return cleaned == cleaned[::-1]

Is Subsequence EASY

class Solution:
    def is_subsequence(self, s, t):
        i = 0
        for c in t:
            if i < len(s) and s[i] == c: i += 1
        return i == len(s)

Two Sum II - Input Array Is Sorted MEDIUM

class Solution:
    def two_sum_ii(self, numbers, target):
        lo, hi = 0, len(numbers) - 1
        while lo < hi:
            s = numbers[lo] + numbers[hi]
            if s == target: return [lo + 1, hi + 1]
            if s < target: lo += 1
            else: hi -= 1

Container With Most Water MEDIUM

class Solution:
    def max_area(self, height):
        lo, hi, best = 0, len(height) - 1, 0
        while lo < hi:
            best = max(best, min(height[lo], height[hi]) * (hi - lo))
            if height[lo] < height[hi]: lo += 1
            else: hi -= 1
        return best

3Sum MEDIUM

Sort, fix one number, then two-pointer the rest. Skip duplicates. O(n²).

class Solution:
    def three_sum(self, nums):
        nums.sort(); res = []
        for i in range(len(nums) - 2):
            if i > 0 and nums[i] == nums[i-1]: continue
            lo, hi = i + 1, len(nums) - 1
            while lo < hi:
                s = nums[i] + nums[lo] + nums[hi]
                if s < 0: lo += 1
                elif s > 0: hi -= 1
                else:
                    res.append([nums[i], nums[lo], nums[hi]])
                    lo += 1; hi -= 1
                    while lo < hi and nums[lo] == nums[lo-1]: lo += 1
                    while lo < hi and nums[hi] == nums[hi+1]: hi -= 1
        return res

Sliding Window

Minimum Size Subarray Sum MEDIUM

class Solution:
    def min_subarray_len(self, target, nums):
        left = total = 0; best = float("inf")
        for right in range(len(nums)):
            total += nums[right]
            while total >= target:
                best = min(best, right - left + 1)
                total -= nums[left]; left += 1
        return best if best != float("inf") else 0

Longest Substring Without Repeating Characters MEDIUM

class Solution:
    def length_of_longest_substring(self, s):
        seen = {}; left = best = 0
        for right, c in enumerate(s):
            if c in seen and seen[c] >= left:
                left = seen[c] + 1
            seen[c] = right
            best = max(best, right - left + 1)
        return best

Substring with Concatenation of All Words HARD

from collections import Counter

class Solution:
    def find_substring(self, s, words):
        if not words: return []
        wl, n = len(words[0]), len(words); total = wl * n
        need = Counter(words); res = []
        for i in range(len(s) - total + 1):
            seen = Counter()
            for j in range(i, i + total, wl):
                word = s[j:j+wl]
                if word not in need: break
                seen[word] += 1
                if seen[word] > need[word]: break
            else:
                res.append(i)
        return res

Minimum Window Substring HARD

Expand right to cover all needed chars, then shrink left while still valid. O(n).

from collections import Counter

class Solution:
    def min_window(self, s, t):
        need = Counter(t); missing = len(t)
        left = start = 0; end = float("inf")
        for right, c in enumerate(s):
            if need[c] > 0: missing -= 1
            need[c] -= 1
            while missing == 0:
                if right - left < end - start: start, end = left, right
                need[s[left]] += 1
                if need[s[left]] > 0: missing += 1
                left += 1
        return s[start:end+1] if end != float("inf") else ""

Matrix

Valid Sudoku MEDIUM

class Solution:
    def is_valid_sudoku(self, board):
        seen = set()
        for r in range(9):
            for c in range(9):
                v = board[r][c]
                if v == ".": continue
                for key in ((v, "row", r), (v, "col", c), (v, "box", r//3, c//3)):
                    if key in seen: return False
                    seen.add(key)
        return True

Spiral Matrix MEDIUM

Pop the top row, rotate the rest counter-clockwise, repeat. O(m·n).

class Solution:
    def spiral_order(self, matrix):
        res = []
        while matrix:
            res += matrix.pop(0)
            matrix = [list(row) for row in zip(*matrix)][::-1]
        return res

Rotate Image (90° in place) MEDIUM

class Solution:
    def rotate_image(self, matrix):
        matrix.reverse()                       # flip vertically
        for i in range(len(matrix)):           # then transpose
            for j in range(i):
                matrix[i][j], matrix[j][i] = matrix[j][i], matrix[i][j]

Set Matrix Zeroes MEDIUM

class Solution:
    def set_zeroes(self, matrix):
        rows, cols = set(), set()
        for r in range(len(matrix)):
            for c in range(len(matrix[0])):
                if matrix[r][c] == 0: rows.add(r); cols.add(c)
        for r in range(len(matrix)):
            for c in range(len(matrix[0])):
                if r in rows or c in cols: matrix[r][c] = 0

Game of Life MEDIUM

Encode next state in bit 2 so neighbors still read the old state (bit 1), then shift. O(m·n), O(1) space.

class Solution:
    def game_of_life(self, board):
        rows, cols = len(board), len(board[0])
        for r in range(rows):
            for c in range(cols):
                live = 0
                for dr in (-1, 0, 1):
                    for dc in (-1, 0, 1):
                        if (dr or dc) and 0 <= r+dr < rows and 0 <= c+dc < cols:
                            live += board[r+dr][c+dc] & 1
                if board[r][c] & 1:
                    if live in (2, 3): board[r][c] |= 2
                elif live == 3: board[r][c] |= 2
        for r in range(rows):
            for c in range(cols):
                board[r][c] >>= 1

Hashmap

Ransom Note EASY

from collections import Counter

class Solution:
    def can_construct(self, ransom_note, magazine):
        return not (Counter(ransom_note) - Counter(magazine))

Isomorphic Strings EASY

class Solution:
    def is_isomorphic(self, s, t):
        return len(set(s)) == len(set(t)) == len(set(zip(s, t)))

Word Pattern EASY

class Solution:
    def word_pattern(self, pattern, s):
        words = s.split()
        if len(pattern) != len(words): return False
        return len(set(pattern)) == len(set(words)) == len(set(zip(pattern, words)))

Valid Anagram EASY

from collections import Counter

class Solution:
    def is_anagram(self, s, t):
        return Counter(s) == Counter(t)

Group Anagrams MEDIUM

from collections import defaultdict

class Solution:
    def group_anagrams(self, strs):
        groups = defaultdict(list)
        for s in strs:
            groups[tuple(sorted(s))].append(s)
        return list(groups.values())

Two Sum EASY

class Solution:
    def two_sum(self, nums, target):
        seen = {}
        for i, n in enumerate(nums):
            if target - n in seen: return [seen[target - n], i]
            seen[n] = i

Happy Number EASY

class Solution:
    def is_happy(self, n):
        seen = set()
        while n != 1 and n not in seen:
            seen.add(n)
            n = sum(int(d) ** 2 for d in str(n))
        return n == 1

Contains Duplicate II EASY

class Solution:
    def contains_nearby_duplicate(self, nums, k):
        last = {}
        for i, n in enumerate(nums):
            if n in last and i - last[n] <= k: return True
            last[n] = i
        return False

Longest Consecutive Sequence MEDIUM

Put all in a set; only start counting from numbers with no left neighbor. O(n).

class Solution:
    def longest_consecutive(self, nums):
        s = set(nums); best = 0
        for n in s:
            if n - 1 not in s:
                length = 1
                while n + length in s: length += 1
                best = max(best, length)
        return best

Intervals

Summary Ranges EASY

class Solution:
    def summary_ranges(self, nums):
        res = []; i = 0
        while i < len(nums):
            start = nums[i]
            while i + 1 < len(nums) and nums[i+1] == nums[i] + 1: i += 1
            res.append(str(start) if start == nums[i] else f"{start}->{nums[i]}")
            i += 1
        return res

Merge Intervals MEDIUM

class Solution:
    def merge_intervals(self, intervals):
        intervals.sort()
        res = [intervals[0]]
        for s, e in intervals[1:]:
            if s <= res[-1][1]: res[-1][1] = max(res[-1][1], e)
            else: res.append([s, e])
        return res

Insert Interval MEDIUM

class Solution:
    def insert_interval(self, intervals, new):
        res = []; i = 0; n = len(intervals)
        while i < n and intervals[i][1] < new[0]:
            res.append(intervals[i]); i += 1
        while i < n and intervals[i][0] <= new[1]:
            new = [min(new[0], intervals[i][0]), max(new[1], intervals[i][1])]; i += 1
        res.append(new)
        while i < n: res.append(intervals[i]); i += 1
        return res

Min Number of Arrows to Burst Balloons MEDIUM

Sort by end, shoot at each non-overlapping end. Greedy. O(n log n).

class Solution:
    def find_min_arrow_shots(self, points):
        points.sort(key=lambda p: p[1])
        arrows = 1; end = points[0][1]
        for s, e in points[1:]:
            if s > end: arrows += 1; end = e
        return arrows

Stack

Valid Parentheses EASY

class Solution:
    def is_valid(self, s):
        pairs = {")": "(", "]": "[", "}": "{"}
        stack = []
        for c in s:
            if c in pairs:
                if not stack or stack.pop() != pairs[c]: return False
            else:
                stack.append(c)
        return not stack

Simplify Path MEDIUM

class Solution:
    def simplify_path(self, path):
        stack = []
        for part in path.split("/"):
            if part in ("", "."): continue
            if part == "..":
                if stack: stack.pop()
            else: stack.append(part)
        return "/" + "/".join(stack)

Min Stack MEDIUM

class MinStack:
    def __init__(self): self.stack = []
    def push(self, val):
        m = min(val, self.stack[-1][1]) if self.stack else val
        self.stack.append((val, m))
    def pop(self): self.stack.pop()
    def top(self): return self.stack[-1][0]
    def getMin(self): return self.stack[-1][1]

Evaluate Reverse Polish Notation MEDIUM

class Solution:
    def eval_rpn(self, tokens):
        stack = []
        ops = {"+": lambda a, b: a + b, "-": lambda a, b: a - b,
               "*": lambda a, b: a * b, "/": lambda a, b: int(a / b)}
        for t in tokens:
            if t in ops:
                b = stack.pop(); a = stack.pop(); stack.append(ops[t](a, b))
            else:
                stack.append(int(t))
        return stack[0]

Basic Calculator HARD

Handle + - ( ). Push the running result & sign on (, fold back on ). O(n).

class Solution:
    def calculate(self, s):
        stack = []; result = 0; num = 0; sign = 1
        for c in s:
            if c.isdigit():
                num = num * 10 + int(c)
            elif c in "+-":
                result += sign * num; num = 0
                sign = 1 if c == "+" else -1
            elif c == "(":
                stack.append(result); stack.append(sign)
                result = 0; sign = 1
            elif c == ")":
                result += sign * num; num = 0
                result = result * stack.pop() + stack.pop()   # sign, then prev result
        return result + sign * num

Linked List

Linked List Cycle EASY

class Solution:
    def has_cycle(self, head):
        slow = fast = head
        while fast and fast.next:
            slow = slow.next; fast = fast.next.next
            if slow == fast: return True
        return False

Add Two Numbers MEDIUM

class Solution:
    def add_two_numbers(self, l1, l2):
        dummy = ListNode(0); cur = dummy; carry = 0
        while l1 or l2 or carry:
            total = carry
            if l1: total += l1.val; l1 = l1.next
            if l2: total += l2.val; l2 = l2.next
            carry, digit = divmod(total, 10)
            cur.next = ListNode(digit); cur = cur.next
        return dummy.next

Merge Two Sorted Lists EASY

class Solution:
    def merge_two_lists(self, l1, l2):
        dummy = ListNode(0); tail = dummy
        while l1 and l2:
            if l1.val <= l2.val: tail.next = l1; l1 = l1.next
            else: tail.next = l2; l2 = l2.next
            tail = tail.next
        tail.next = l1 or l2
        return dummy.next

Copy List with Random Pointer MEDIUM

Map each old node to its clone, then wire up next/random. O(n).

class Solution:
    def copy_random_list(self, head):
        if not head: return None
        clone = {}
        cur = head
        while cur:
            clone[cur] = Node(cur.val); cur = cur.next
        cur = head
        while cur:
            clone[cur].next = clone.get(cur.next)
            clone[cur].random = clone.get(cur.random)
            cur = cur.next
        return clone[head]

Reverse Linked List II MEDIUM

class Solution:
    def reverse_between(self, head, left, right):
        dummy = ListNode(0, head); prev = dummy
        for _ in range(left - 1): prev = prev.next
        cur = prev.next
        for _ in range(right - left):
            nxt = cur.next
            cur.next = nxt.next
            nxt.next = prev.next
            prev.next = nxt
        return dummy.next

Reverse Nodes in k-Group HARD

class Solution:
    def reverse_k_group(self, head, k):
        node = head
        for _ in range(k):                      # need k nodes
            if not node: return head
            node = node.next
        prev = None; cur = head
        for _ in range(k):
            nxt = cur.next; cur.next = prev; prev = cur; cur = nxt
        head.next = self.reverse_k_group(cur, k)
        return prev

Remove Nth Node From End of List MEDIUM

class Solution:
    def remove_nth_from_end(self, head, n):
        dummy = ListNode(0, head); fast = slow = dummy
        for _ in range(n): fast = fast.next
        while fast.next:
            fast = fast.next; slow = slow.next
        slow.next = slow.next.next
        return dummy.next

Remove Duplicates from Sorted List II MEDIUM

class Solution:
    def delete_duplicates_ii(self, head):
        dummy = ListNode(0, head); prev = dummy; cur = head
        while cur:
            if cur.next and cur.val == cur.next.val:
                while cur.next and cur.val == cur.next.val: cur = cur.next
                prev.next = cur.next
            else:
                prev = prev.next
            cur = cur.next
        return dummy.next

Rotate List MEDIUM

class Solution:
    def rotate_right(self, head, k):
        if not head or not head.next: return head
        n = 1; tail = head
        while tail.next: tail = tail.next; n += 1
        k %= n
        if k == 0: return head
        tail.next = head                        # circular
        new_tail = head
        for _ in range(n - k - 1): new_tail = new_tail.next
        new_head = new_tail.next; new_tail.next = None
        return new_head

Partition List MEDIUM

class Solution:
    def partition(self, head, x):
        less = ListNode(0); greater = ListNode(0)
        lt, gt = less, greater
        while head:
            if head.val < x: lt.next = head; lt = lt.next
            else: gt.next = head; gt = gt.next
            head = head.next
        gt.next = None; lt.next = greater.next
        return less.next

LRU Cache MEDIUM

An OrderedDict gives O(1) get/put with recency tracking (move_to_end).

from collections import OrderedDict
class LRUCache:
    def __init__(self, capacity):
        self.cache = OrderedDict(); self.cap = capacity
    def get(self, key):
        if key not in self.cache: return -1
        self.cache.move_to_end(key)
        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.cap:
            self.cache.popitem(last=False)

Binary Tree - General

Maximum Depth of Binary Tree EASY

class Solution:
    def max_depth(self, root):
        if not root: return 0
        return 1 + max(self.max_depth(root.left), self.max_depth(root.right))

Same Tree EASY

class Solution:
    def is_same_tree(self, p, q):
        if not p and not q: return True
        if not p or not q or p.val != q.val: return False
        return self.is_same_tree(p.left, q.left) and self.is_same_tree(p.right, q.right)

Invert Binary Tree EASY

class Solution:
    def invert_tree(self, root):
        if root:
            root.left, root.right = self.invert_tree(root.right), self.invert_tree(root.left)
        return root

Symmetric Tree EASY

class Solution:
    def is_symmetric(self, root):
        def mirror(a, b):
            if not a and not b: return True
            if not a or not b or a.val != b.val: return False
            return mirror(a.left, b.right) and mirror(a.right, b.left)
        return mirror(root, root)

Construct Tree from Preorder & Inorder MEDIUM

First preorder value is the root; its index in inorder splits left/right. O(n).

class Solution:
    def build_tree(self, preorder, inorder):
        idx = {v: i for i, v in enumerate(inorder)}
        pre = [0]
        def build(lo, hi):
            if lo > hi: return None
            val = preorder[pre[0]]; pre[0] += 1
            node = TreeNode(val); mid = idx[val]
            node.left = build(lo, mid - 1)
            node.right = build(mid + 1, hi)
            return node
        return build(0, len(inorder) - 1)

Construct Tree from Inorder & Postorder MEDIUM

class Solution:
    def build_tree_post(self, inorder, postorder):
        idx = {v: i for i, v in enumerate(inorder)}
        post = [len(postorder) - 1]
        def build(lo, hi):
            if lo > hi: return None
            val = postorder[post[0]]; post[0] -= 1
            node = TreeNode(val); mid = idx[val]
            node.right = build(mid + 1, hi)     # right before left (postorder reversed)
            node.left = build(lo, mid - 1)
            return node
        return build(0, len(inorder) - 1)

Populating Next Right Pointers II MEDIUM

Use the already-linked current level to build the next level's next chain. O(n), O(1) extra.

class Solution:
    def connect(self, root):
        head = root
        while head:
            dummy = Node(0); tail = dummy; cur = head
            while cur:
                if cur.left: tail.next = cur.left; tail = tail.next
                if cur.right: tail.next = cur.right; tail = tail.next
                cur = cur.next
            head = dummy.next
        return root

Flatten Binary Tree to Linked List MEDIUM

class Solution:
    def flatten(self, root):
        node = root
        while node:
            if node.left:
                rightmost = node.left
                while rightmost.right: rightmost = rightmost.right
                rightmost.right = node.right
                node.right = node.left; node.left = None
            node = node.right

Path Sum EASY

class Solution:
    def has_path_sum(self, root, target):
        if not root: return False
        if not root.left and not root.right: return root.val == target
        rem = target - root.val
        return self.has_path_sum(root.left, rem) or self.has_path_sum(root.right, rem)

Sum Root to Leaf Numbers MEDIUM

class Solution:
    def sum_numbers(self, root):
        def dfs(node, cur):
            if not node: return 0
            cur = cur * 10 + node.val
            if not node.left and not node.right: return cur
            return dfs(node.left, cur) + dfs(node.right, cur)
        return dfs(root, 0)

Binary Tree Maximum Path Sum HARD

class Solution:
    def max_path_sum(self, root):
        best = [float("-inf")]
        def gain(node):
            if not node: return 0
            left = max(gain(node.left), 0)
            right = max(gain(node.right), 0)
            best[0] = max(best[0], node.val + left + right)
            return node.val + max(left, right)
        gain(root)
        return best[0]

Binary Search Tree Iterator MEDIUM

class BSTIterator:
    def __init__(self, root):
        self.stack = []
        self._push_left(root)
    def _push_left(self, node):
        while node:
            self.stack.append(node); node = node.left
    def next(self):
        node = self.stack.pop()
        self._push_left(node.right)
        return node.val
    def hasNext(self):
        return bool(self.stack)

Count Complete Tree Nodes EASY

If left and right heights match, it's a perfect subtree → 2^h − 1. Otherwise recurse. O(log²n).

class Solution:
    def count_nodes(self, root):
        if not root: return 0
        lh = rh = 0; l = r = root
        while l: lh += 1; l = l.left
        while r: rh += 1; r = r.right
        if lh == rh: return (1 << lh) - 1
        return 1 + self.count_nodes(root.left) + self.count_nodes(root.right)

Lowest Common Ancestor of a Binary Tree MEDIUM

class Solution:
    def lowest_common_ancestor(self, root, p, q):
        if not root or root == p or root == q: return root
        left = self.lowest_common_ancestor(root.left, p, q)
        right = self.lowest_common_ancestor(root.right, p, q)
        if left and right: return root
        return left or right

Binary Tree - BFS

Binary Tree Right Side View MEDIUM

from collections import deque

class Solution:
    def right_side_view(self, root):
        if not root: return []
        res = []; queue = deque([root])
        while queue:
            n = len(queue)
            for i in range(n):
                node = queue.popleft()
                if i == n - 1: res.append(node.val)
                if node.left: queue.append(node.left)
                if node.right: queue.append(node.right)
        return res

Average of Levels in Binary Tree EASY

from collections import deque

class Solution:
    def average_of_levels(self, root):
        res = []; queue = deque([root])
        while queue:
            n = len(queue); total = 0
            for _ in range(n):
                node = queue.popleft(); total += node.val
                if node.left: queue.append(node.left)
                if node.right: queue.append(node.right)
            res.append(total / n)
        return res

Binary Tree Level Order Traversal MEDIUM

from collections import deque

class Solution:
    def level_order(self, root):
        if not root: return []
        res = []; queue = deque([root])
        while queue:
            level = []
            for _ in range(len(queue)):
                node = queue.popleft(); level.append(node.val)
                if node.left: queue.append(node.left)
                if node.right: queue.append(node.right)
            res.append(level)
        return res

Binary Tree Zigzag Level Order Traversal MEDIUM

from collections import deque

class Solution:
    def zigzag_level_order(self, root):
        if not root: return []
        res = []; queue = deque([root]); ltr = True
        while queue:
            level = deque()
            for _ in range(len(queue)):
                node = queue.popleft()
                if ltr: level.append(node.val)
                else: level.appendleft(node.val)
                if node.left: queue.append(node.left)
                if node.right: queue.append(node.right)
            res.append(list(level)); ltr = not ltr
        return res

Binary Search Tree

Minimum Absolute Difference in BST EASY

In-order visits values sorted → the min gap is between adjacent values. O(n).

class Solution:
    def get_minimum_difference(self, root):
        prev = [None]; best = [float("inf")]
        def inorder(node):
            if not node: return
            inorder(node.left)
            if prev[0] is not None:
                best[0] = min(best[0], node.val - prev[0])
            prev[0] = node.val
            inorder(node.right)
        inorder(root)
        return best[0]

Kth Smallest Element in a BST MEDIUM

class Solution:
    def kth_smallest(self, root, k):
        stack = []; node = root
        while stack or node:
            while node:
                stack.append(node); node = node.left
            node = stack.pop(); k -= 1
            if k == 0: return node.val
            node = node.right

Validate Binary Search Tree MEDIUM

class Solution:
    def is_valid_bst(self, root):
        def valid(node, low, high):
            if not node: return True
            if not (low < node.val < high): return False
            return valid(node.left, low, node.val) and valid(node.right, node.val, high)
        return valid(root, float("-inf"), float("inf"))

Graph - General

Number of Islands MEDIUM

class Solution:
    def num_islands(self, grid):
        if not grid: return 0
        count = 0
        def sink(r, c):
            if 0 <= r < len(grid) and 0 <= c < len(grid[0]) and grid[r][c] == "1":
                grid[r][c] = "0"
                sink(r+1, c); sink(r-1, c); sink(r, c+1); sink(r, c-1)
        for r in range(len(grid)):
            for c in range(len(grid[0])):
                if grid[r][c] == "1":
                    count += 1; sink(r, c)
        return count

Surrounded Regions MEDIUM

Any 'O' connected to the border survives - mark those first, flip the rest. O(m·n).

class Solution:
    def surrounded(self, board):
        if not board: return
        rows, cols = len(board), len(board[0])
        def mark(r, c):
            if 0 <= r < rows and 0 <= c < cols and board[r][c] == "O":
                board[r][c] = "#"
                mark(r+1, c); mark(r-1, c); mark(r, c+1); mark(r, c-1)
        for r in range(rows): mark(r, 0); mark(r, cols-1)
        for c in range(cols): mark(0, c); mark(rows-1, c)
        for r in range(rows):
            for c in range(cols):
                board[r][c] = "O" if board[r][c] == "#" else "X"

Clone Graph MEDIUM

class Solution:
    def clone_graph(self, node):
        if not node: return None
        clones = {}
        def dfs(n):
            if n in clones: return clones[n]
            copy = Node(n.val); clones[n] = copy
            for nb in n.neighbors:
                copy.neighbors.append(dfs(nb))
            return copy
        return dfs(node)

Evaluate Division MEDIUM

Build a weighted graph (a/b = v, b/a = 1/v); each query is a DFS multiplying edge weights.

from collections import defaultdict

class Solution:
    def calc_equation(self, equations, values, queries):
        graph = defaultdict(dict)
        for (a, b), v in zip(equations, values):
            graph[a][b] = v; graph[b][a] = 1 / v
        def dfs(src, dst, seen):
            if src not in graph or dst not in graph: return -1.0
            if src == dst: return 1.0
            seen.add(src)
            for nb, w in graph[src].items():
                if nb not in seen:
                    res = dfs(nb, dst, seen)
                    if res != -1.0: return w * res
            return -1.0
        return [dfs(a, b, set()) for a, b in queries]

Course Schedule (can finish?) MEDIUM

Detect a cycle in the prereq graph via 3-color DFS. O(V+E).

from collections import defaultdict

class Solution:
    def can_finish(self, num_courses, prerequisites):
        graph = defaultdict(list)
        for a, b in prerequisites: graph[b].append(a)
        state = [0] * num_courses        # 0=unseen, 1=visiting, 2=done
        def dfs(node):
            if state[node] == 1: return False
            if state[node] == 2: return True
            state[node] = 1
            for nb in graph[node]:
                if not dfs(nb): return False
            state[node] = 2
            return True
        return all(dfs(i) for i in range(num_courses))

Course Schedule II (order) MEDIUM

Kahn's topological sort (BFS on in-degrees). O(V+E).

from collections import defaultdict, deque

class Solution:
    def find_order(self, num_courses, prerequisites):
        graph = defaultdict(list); indeg = [0] * num_courses
        for a, b in prerequisites:
            graph[b].append(a); indeg[a] += 1
        queue = deque(i for i in range(num_courses) if indeg[i] == 0)
        order = []
        while queue:
            node = queue.popleft(); order.append(node)
            for nb in graph[node]:
                indeg[nb] -= 1
                if indeg[nb] == 0: queue.append(nb)
        return order if len(order) == num_courses else []

Graph - BFS

Snakes and Ladders MEDIUM

from collections import deque

class Solution:
    def snakes_and_ladders(self, board):
        n = len(board)
        def cell(s):
            r, c = divmod(s - 1, n)
            if r % 2: c = n - 1 - c
            return board[n - 1 - r][c]
        queue = deque([(1, 0)]); seen = {1}
        while queue:
            s, moves = queue.popleft()
            if s == n * n: return moves
            for nxt in range(s + 1, min(s + 6, n * n) + 1):
                dest = cell(nxt)
                if dest != -1: nxt = dest
                if nxt not in seen:
                    seen.add(nxt); queue.append((nxt, moves + 1))
        return -1

Minimum Genetic Mutation MEDIUM

from collections import deque

class Solution:
    def min_mutation(self, start, end, bank):
        bank = set(bank)
        queue = deque([(start, 0)]); seen = {start}
        while queue:
            gene, steps = queue.popleft()
            if gene == end: return steps
            for i in range(len(gene)):
                for ch in "ACGT":
                    mut = gene[:i] + ch + gene[i+1:]
                    if mut in bank and mut not in seen:
                        seen.add(mut); queue.append((mut, steps + 1))
        return -1

Word Ladder HARD

from collections import deque

class Solution:
    def ladder_length(self, begin, end, word_list):
        words = set(word_list)
        if end not in words: return 0
        queue = deque([(begin, 1)]); seen = {begin}
        while queue:
            word, length = queue.popleft()
            if word == end: return length
            for i in range(len(word)):
                for ch in "abcdefghijklmnopqrstuvwxyz":
                    nxt = word[:i] + ch + word[i+1:]
                    if nxt in words and nxt not in seen:
                        seen.add(nxt); queue.append((nxt, length + 1))
        return 0

Trie

Implement Trie (Prefix Tree) MEDIUM

class Trie:
    def __init__(self): self.root = {}
    def insert(self, word):
        node = self.root
        for c in word: node = node.setdefault(c, {})
        node["$"] = True
    def search(self, word):
        node = self.root
        for c in word:
            if c not in node: return False
            node = node[c]
        return "$" in node
    def startsWith(self, prefix):
        node = self.root
        for c in prefix:
            if c not in node: return False
            node = node[c]
        return True

Design Add and Search Words (wildcards) MEDIUM

class WordDictionary:
    def __init__(self): self.root = {}
    def addWord(self, word):
        node = self.root
        for c in word: node = node.setdefault(c, {})
        node["$"] = True
    def search(self, word):
        def dfs(node, i):
            if i == len(word): return "$" in node
            c = word[i]
            if c == ".":
                return any(dfs(child, i+1) for k, child in node.items() if k != "$")
            return c in node and dfs(node[c], i+1)
        return dfs(self.root, 0)

Word Search II HARD

Put all words in a trie, then DFS the board once, pruning by trie paths.

class Solution:
    def find_words(self, board, words):
        trie = {}
        for w in words:
            node = trie
            for c in w: node = node.setdefault(c, {})
            node["$"] = w
        res = []; rows, cols = len(board), len(board[0])
        def dfs(r, c, node):
            ch = board[r][c]
            if ch not in node: return
            nxt = node[ch]
            if "$" in nxt: res.append(nxt.pop("$"))
            board[r][c] = "#"
            for dr, dc in ((1,0),(-1,0),(0,1),(0,-1)):
                nr, nc = r+dr, c+dc
                if 0 <= nr < rows and 0 <= nc < cols and board[nr][nc] != "#":
                    dfs(nr, nc, nxt)
            board[r][c] = ch
        for r in range(rows):
            for c in range(cols):
                dfs(r, c, trie)
        return res

Backtracking

Letter Combinations of a Phone Number MEDIUM

class Solution:
    def letter_combinations(self, digits):
        if not digits: return []
        m = {"2":"abc","3":"def","4":"ghi","5":"jkl",
             "6":"mno","7":"pqrs","8":"tuv","9":"wxyz"}
        res = [""]
        for d in digits:
            res = [prefix + c for prefix in res for c in m[d]]
        return res

Combinations MEDIUM

class Solution:
    def combine(self, n, k):
        res = []
        def backtrack(start, path):
            if len(path) == k:
                res.append(path[:]); return
            for i in range(start, n + 1):
                path.append(i)
                backtrack(i + 1, path)
                path.pop()
        backtrack(1, [])
        return res

Permutations MEDIUM

class Solution:
    def permute(self, nums):
        res = []
        def backtrack(path, remaining):
            if not remaining:
                res.append(path[:]); return
            for i in range(len(remaining)):
                backtrack(path + [remaining[i]], remaining[:i] + remaining[i+1:])
        backtrack([], nums)
        return res

Combination Sum MEDIUM

class Solution:
    def combination_sum(self, candidates, target):
        res = []
        def backtrack(start, path, remaining):
            if remaining == 0:
                res.append(path[:]); return
            for i in range(start, len(candidates)):
                if candidates[i] <= remaining:
                    path.append(candidates[i])
                    backtrack(i, path, remaining - candidates[i])   # i: reuse allowed
                    path.pop()
        backtrack(0, [], target)
        return res

N-Queens II (count) HARD

class Solution:
    def total_n_queens(self, n):
        cols = set(); diag1 = set(); diag2 = set()
        def place(row):
            if row == n: return 1
            count = 0
            for col in range(n):
                if col in cols or (row+col) in diag1 or (row-col) in diag2: continue
                cols.add(col); diag1.add(row+col); diag2.add(row-col)
                count += place(row + 1)
                cols.discard(col); diag1.discard(row+col); diag2.discard(row-col)
            return count
        return place(0)

Generate Parentheses MEDIUM

class Solution:
    def generate_parenthesis(self, n):
        res = []
        def backtrack(s, open_n, close_n):
            if len(s) == 2 * n:
                res.append(s); return
            if open_n < n: backtrack(s + "(", open_n + 1, close_n)
            if close_n < open_n: backtrack(s + ")", open_n, close_n + 1)
        backtrack("", 0, 0)
        return res

Word Search MEDIUM

class Solution:
    def exist(self, board, word):
        rows, cols = len(board), len(board[0])
        def dfs(r, c, i):
            if i == len(word): return True
            if not (0 <= r < rows and 0 <= c < cols) or board[r][c] != word[i]:
                return False
            board[r][c] = "#"
            found = (dfs(r+1, c, i+1) or dfs(r-1, c, i+1) or
                     dfs(r, c+1, i+1) or dfs(r, c-1, i+1))
            board[r][c] = word[i]
            return found
        return any(dfs(r, c, 0) for r in range(rows) for c in range(cols))

Divide & Conquer

Convert Sorted Array to BST EASY

class Solution:
    def sorted_array_to_bst(self, nums):
        def build(lo, hi):
            if lo > hi: return None
            mid = (lo + hi) // 2
            node = TreeNode(nums[mid])
            node.left = build(lo, mid - 1)
            node.right = build(mid + 1, hi)
            return node
        return build(0, len(nums) - 1)

Sort List (merge sort) MEDIUM

class Solution:
    def sort_list(self, head):
        if not head or not head.next: return head
        slow, fast = head, head.next
        while fast and fast.next:
            slow = slow.next; fast = fast.next.next
        mid = slow.next; slow.next = None
        left, right = self.sort_list(head), self.sort_list(mid)
        dummy = tail = ListNode(0)
        while left and right:
            if left.val <= right.val: tail.next = left; left = left.next
            else: tail.next = right; right = right.next
            tail = tail.next
        tail.next = left or right
        return dummy.next

Construct Quad Tree MEDIUM

class Solution:
    def construct(self, grid):
        def build(r, c, size):
            if size == 1:
                return Node(grid[r][c] == 1, True, None, None, None, None)
            half = size // 2
            tl = build(r, c, half);        tr = build(r, c + half, half)
            bl = build(r + half, c, half); br = build(r + half, c + half, half)
            if (tl.isLeaf and tr.isLeaf and bl.isLeaf and br.isLeaf
                    and tl.val == tr.val == bl.val == br.val):
                return Node(tl.val, True, None, None, None, None)
            return Node(True, False, tl, tr, bl, br)
        return build(0, 0, len(grid))

Merge k Sorted Lists HARD

import heapq

class Solution:
    def merge_k_lists(self, lists):
        heap = []
        for i, node in enumerate(lists):
            if node: heapq.heappush(heap, (node.val, i, node))
        dummy = tail = ListNode(0)
        while heap:
            val, i, node = heapq.heappop(heap)
            tail.next = node; tail = node
            if node.next:
                heapq.heappush(heap, (node.next.val, i, node.next))
        return dummy.next

Kadane's Algorithm

Maximum Subarray MEDIUM

class Solution:
    def max_sub_array(self, nums):
        best = cur = nums[0]
        for n in nums[1:]:
            cur = max(n, cur + n)
            best = max(best, cur)
        return best

Maximum Sum Circular Subarray MEDIUM

Answer is either a normal Kadane max, or total − (minimum subarray), whichever's bigger. Guard the all-negative case.

class Solution:
    def max_subarray_circular(self, nums):
        total = 0
        cur_max = best_max = nums[0]
        cur_min = best_min = nums[0]
        for n in nums:
            cur_max = max(n, cur_max + n); best_max = max(best_max, cur_max)
            cur_min = min(n, cur_min + n); best_min = min(best_min, cur_min)
            total += n
        if best_max < 0: return best_max
        return max(best_max, total - best_min)

Binary Search

Search Insert Position EASY

class Solution:
    def search_insert(self, nums, target):
        lo, hi = 0, len(nums)
        while lo < hi:
            mid = (lo + hi) // 2
            if nums[mid] < target: lo = mid + 1
            else: hi = mid
        return lo

Search a 2D Matrix MEDIUM

Treat the matrix as one sorted array of length rows·cols. O(log(mn)).

class Solution:
    def search_matrix(self, matrix, target):
        rows, cols = len(matrix), len(matrix[0])
        lo, hi = 0, rows * cols - 1
        while lo <= hi:
            mid = (lo + hi) // 2
            val = matrix[mid // cols][mid % cols]
            if val == target: return True
            if val < target: lo = mid + 1
            else: hi = mid - 1
        return False

Find Peak Element MEDIUM

class Solution:
    def find_peak_element(self, nums):
        lo, hi = 0, len(nums) - 1
        while lo < hi:
            mid = (lo + hi) // 2
            if nums[mid] < nums[mid + 1]: lo = mid + 1
            else: hi = mid
        return lo

Search in Rotated Sorted Array MEDIUM

One half is always sorted - check which, then decide which side to keep. O(log n).

class Solution:
    def search_rotated(self, 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

Find First and Last Position MEDIUM

import bisect

class Solution:
    def search_range(self, nums, target):
        left = bisect.bisect_left(nums, target)
        if left == len(nums) or nums[left] != target: return [-1, -1]
        right = bisect.bisect_right(nums, target) - 1
        return [left, right]

Find Minimum in Rotated Sorted Array MEDIUM

class Solution:
    def find_min(self, nums):
        lo, hi = 0, len(nums) - 1
        while lo < hi:
            mid = (lo + hi) // 2
            if nums[mid] > nums[hi]: lo = mid + 1
            else: hi = mid
        return nums[lo]

Median of Two Sorted Arrays HARD

Binary-search a partition of the smaller array so left halves ≤ right halves. O(log(min(m,n))).

class Solution:
    def find_median_sorted_arrays(self, a, b):
        if len(a) > len(b): a, b = b, a
        m, n = len(a), len(b); half = (m + n + 1) // 2
        lo, hi = 0, m
        while lo <= hi:
            i = (lo + hi) // 2; j = half - i
            a_left  = a[i-1] if i > 0 else float("-inf")
            a_right = a[i]   if i < m else float("inf")
            b_left  = b[j-1] if j > 0 else float("-inf")
            b_right = b[j]   if j < n else float("inf")
            if a_left <= b_right and b_left <= a_right:
                if (m + n) % 2: return max(a_left, b_left)
                return (max(a_left, b_left) + min(a_right, b_right)) / 2
            elif a_left > b_right: hi = i - 1
            else: lo = i + 1

Heap

Kth Largest Element in an Array MEDIUM

import heapq

class Solution:
    def find_kth_largest(self, nums, k):
        return heapq.nlargest(k, nums)[-1]

IPO HARD

Greedy: among all affordable projects, always take the most profitable. Max-heap of profits. O(n log n).

import heapq

class Solution:
    def find_maximized_capital(self, k, w, profits, capital):
        projects = sorted(zip(capital, profits))
        heap = []; i = 0
        for _ in range(k):
            while i < len(projects) and projects[i][0] <= w:
                heapq.heappush(heap, -projects[i][1]); i += 1
            if not heap: break
            w -= heapq.heappop(heap)
        return w

Find K Pairs with Smallest Sums MEDIUM

import heapq

class Solution:
    def k_smallest_pairs(self, nums1, nums2, k):
        if not nums1 or not nums2: return []
        heap = []; res = []
        for i in range(min(k, len(nums1))):
            heapq.heappush(heap, (nums1[i] + nums2[0], i, 0))
        while heap and len(res) < k:
            _, i, j = heapq.heappop(heap)
            res.append([nums1[i], nums2[j]])
            if j + 1 < len(nums2):
                heapq.heappush(heap, (nums1[i] + nums2[j+1], i, j+1))
        return res

Find Median from Data Stream HARD

Two heaps: a max-heap for the lower half, a min-heap for the upper. Median is the top(s). O(log n) per add.

import heapq
class MedianFinder:
    def __init__(self):
        self.small = []   # max-heap (store negatives)
        self.large = []   # min-heap
    def addNum(self, num):
        heapq.heappush(self.small, -num)
        heapq.heappush(self.large, -heapq.heappop(self.small))
        if len(self.large) > len(self.small):
            heapq.heappush(self.small, -heapq.heappop(self.large))
    def findMedian(self):
        if len(self.small) > len(self.large): return -self.small[0]
        return (-self.small[0] + self.large[0]) / 2

Bit Manipulation

Add Binary EASY

class Solution:
    def add_binary(self, a, b):
        return bin(int(a, 2) + int(b, 2))[2:]

Reverse Bits EASY

class Solution:
    def reverse_bits(self, n):
        result = 0
        for _ in range(32):
            result = (result << 1) | (n & 1)
            n >>= 1
        return result

Number of 1 Bits EASY

class Solution:
    def hamming_weight(self, n):
        count = 0
        while n:
            n &= n - 1            # clears the lowest set bit
            count += 1
        return count

Single Number EASY

class Solution:
    def single_number(self, nums):
        result = 0
        for n in nums: result ^= n     # pairs cancel via XOR
        return result

Single Number II (others appear 3×) MEDIUM

class Solution:
    def single_number_ii(self, nums):
        ones = twos = 0
        for n in nums:
            ones = (ones ^ n) & ~twos
            twos = (twos ^ n) & ~ones
        return ones

Bitwise AND of Numbers Range MEDIUM

The result is the common binary prefix of left & right. Shift both right until equal. O(log n).

class Solution:
    def range_bitwise_and(self, left, right):
        shift = 0
        while left < right:
            left >>= 1; right >>= 1; shift += 1
        return left << shift

Math

Palindrome Number EASY

class Solution:
    def is_palindrome_number(self, x):
        if x < 0: return False
        return str(x) == str(x)[::-1]

Plus One EASY

class Solution:
    def plus_one(self, digits):
        for i in range(len(digits) - 1, -1, -1):
            if digits[i] < 9:
                digits[i] += 1; return digits
            digits[i] = 0
        return [1] + digits

Factorial Trailing Zeroes MEDIUM

Count factors of 5 in n! (each pairs with a 2 to make a trailing zero). O(log n).

class Solution:
    def trailing_zeroes(self, n):
        count = 0
        while n:
            n //= 5; count += n
        return count

Sqrt(x) EASY

class Solution:
    def my_sqrt(self, x):
        lo, hi = 0, x
        while lo <= hi:
            mid = (lo + hi) // 2
            if mid * mid <= x: lo = mid + 1
            else: hi = mid - 1
        return hi

Pow(x, n) - fast exponentiation MEDIUM

class Solution:
    def my_pow(self, x, n):
        if n < 0: x = 1 / x; n = -n
        result = 1
        while n:
            if n & 1: result *= x
            x *= x; n >>= 1
        return result

Max Points on a Line HARD

from math import gcd

class Solution:
    def max_points(self, points):
        if len(points) <= 2: return len(points)
        best = 0
        for i in range(len(points)):
            slopes = {}
            for j in range(i + 1, len(points)):
                dx = points[j][0] - points[i][0]
                dy = points[j][1] - points[i][1]
                g = gcd(dx, dy) or 1
                slope = (dx // g, dy // g)
                slopes[slope] = slopes.get(slope, 1) + 1
                best = max(best, slopes[slope])
        return best

1-D Dynamic Programming

Climbing Stairs EASY

class Solution:
    def climb_stairs(self, n):
        a, b = 1, 1
        for _ in range(n):
            a, b = b, a + b
        return a

House Robber MEDIUM

class Solution:
    def rob(self, nums):
        prev = curr = 0
        for n in nums:
            prev, curr = curr, max(curr, prev + n)
        return curr

Word Break MEDIUM

class Solution:
    def word_break(self, s, word_dict):
        words = set(word_dict); n = len(s)
        dp = [False] * (n + 1); dp[0] = True
        for i in range(1, n + 1):
            for j in range(i):
                if dp[j] and s[j:i] in words:
                    dp[i] = True; break
        return dp[n]

Coin Change MEDIUM

class Solution:
    def coin_change(self, coins, amount):
        dp = [0] + [float("inf")] * amount
        for a in range(1, amount + 1):
            for c in coins:
                if c <= a:
                    dp[a] = min(dp[a], dp[a - c] + 1)
        return dp[amount] if dp[amount] != float("inf") else -1

Longest Increasing Subsequence MEDIUM

Patience sorting: keep the smallest tail for each length. O(n log n).

import bisect

class Solution:
    def length_of_lis(self, nums):
        tails = []
        for n in nums:
            i = bisect.bisect_left(tails, n)
            if i == len(tails): tails.append(n)
            else: tails[i] = n
        return len(tails)

Multidimensional DP

Triangle MEDIUM

class Solution:
    def minimum_total(self, triangle):
        dp = triangle[-1][:]
        for row in range(len(triangle) - 2, -1, -1):
            for i in range(len(triangle[row])):
                dp[i] = triangle[row][i] + min(dp[i], dp[i + 1])
        return dp[0]

Minimum Path Sum MEDIUM

class Solution:
    def min_path_sum(self, grid):
        rows, cols = len(grid), len(grid[0])
        for r in range(rows):
            for c in range(cols):
                if r == 0 and c == 0: continue
                up = grid[r-1][c] if r > 0 else float("inf")
                left = grid[r][c-1] if c > 0 else float("inf")
                grid[r][c] += min(up, left)
        return grid[-1][-1]

Unique Paths II (obstacles) MEDIUM

class Solution:
    def unique_paths_with_obstacles(self, grid):
        cols = len(grid[0])
        dp = [0] * cols; dp[0] = 1
        for row in grid:
            for c in range(cols):
                if row[c] == 1: dp[c] = 0
                elif c > 0: dp[c] += dp[c - 1]
        return dp[-1]

Longest Palindromic Substring MEDIUM

Expand around each center (odd and even length). O(n²).

class Solution:
    def longest_palindrome(self, s):
        start = end = 0
        def expand(l, r):
            while l >= 0 and r < len(s) and s[l] == s[r]:
                l -= 1; r += 1
            return l + 1, r - 1
        for i in range(len(s)):
            for l, r in (expand(i, i), expand(i, i + 1)):
                if r - l > end - start: start, end = l, r
        return s[start:end + 1]

Interleaving String MEDIUM

class Solution:
    def is_interleave(self, s1, s2, s3):
        if len(s1) + len(s2) != len(s3): return False
        dp = [False] * (len(s2) + 1); dp[0] = True
        for j in range(1, len(s2) + 1):
            dp[j] = dp[j-1] and s2[j-1] == s3[j-1]
        for i in range(1, len(s1) + 1):
            dp[0] = dp[0] and s1[i-1] == s3[i-1]
            for j in range(1, len(s2) + 1):
                dp[j] = ((dp[j] and s1[i-1] == s3[i+j-1]) or
                         (dp[j-1] and s2[j-1] == s3[i+j-1]))
        return dp[-1]

Edit Distance MEDIUM

class Solution:
    def min_distance(self, word1, word2):
        m, n = len(word1), len(word2)
        dp = [[0] * (n + 1) for _ in range(m + 1)]
        for i in range(m + 1): dp[i][0] = i
        for j in range(n + 1): dp[0][j] = j
        for i in range(1, m + 1):
            for j in range(1, n + 1):
                if word1[i-1] == word2[j-1]:
                    dp[i][j] = dp[i-1][j-1]
                else:
                    dp[i][j] = 1 + min(dp[i-1][j], dp[i][j-1], dp[i-1][j-1])
        return dp[m][n]

Best Time to Buy and Sell Stock III (2 txns) HARD

class Solution:
    def max_profit_iii(self, prices):
        buy1 = buy2 = float("-inf"); sell1 = sell2 = 0
        for p in prices:
            buy1 = max(buy1, -p)
            sell1 = max(sell1, buy1 + p)
            buy2 = max(buy2, sell1 - p)
            sell2 = max(sell2, buy2 + p)
        return sell2

Best Time to Buy and Sell Stock IV (k txns) HARD

class Solution:
    def max_profit_iv(self, k, prices):
        if not prices: return 0
        if k >= len(prices) // 2:               # unlimited → grab every rise
            return sum(max(0, prices[i] - prices[i-1]) for i in range(1, len(prices)))
        buy = [float("-inf")] * (k + 1); sell = [0] * (k + 1)
        for p in prices:
            for j in range(1, k + 1):
                buy[j] = max(buy[j], sell[j-1] - p)
                sell[j] = max(sell[j], buy[j] + p)
        return sell[k]

Maximal Square MEDIUM

dp[r][c] = side of the largest all-1 square ending at (r,c) = 1 + min of its top/left/diagonal. O(m·n).

class Solution:
    def maximal_square(self, matrix):
        if not matrix: return 0
        rows, cols = len(matrix), len(matrix[0])
        dp = [[0] * (cols + 1) for _ in range(rows + 1)]
        best = 0
        for r in range(1, rows + 1):
            for c in range(1, cols + 1):
                if matrix[r-1][c-1] == "1":
                    dp[r][c] = 1 + min(dp[r-1][c], dp[r][c-1], dp[r-1][c-1])
                    best = max(best, dp[r][c])
        return best * best

🗣️ Behavioral, projects & interview flow

Coding is only half the interview. A typical CS interview runs intro → resume/projects → coding → behavioral → your questions. These parts are free here because they win (or lose) you offers just as often as the algorithm does.

1. Intro & rapport2. Resume / projects3. Coding / technical4. Behavioral5. Your questions for them
Use STAR for every behavioral answer

Situation → Task → Action → Result. Set the scene in a sentence, say what you were responsible for, explain what you specifically did, then finish with a concrete (ideally measurable) result. Aim for ~90 seconds.

Resume & project questions
"Walk me through a project you built."
Pick one project and tell a story, not a feature list: the problem it solved, your key technical decisions and why, one hard bug or trade-off you hit, and the outcome (users, performance, what you'd improve). Interviewers care far more about your reasoning than the tech stack. Know your own code cold - they will drill into anything you claim.
Behavioral questions (with sample answers)
Tell me about a difficult team project.
S/T: On a class capstone our 4-person team fell behind because two members disagreed on the database design. A: I set up a 30-minute call, had each person whiteboard their approach, and we listed trade-offs in a shared doc; we chose the simpler schema and split the rest into clear tickets. R: We shipped on time and the grader praised our clean data model. Lesson: surfacing a disagreement early saves the whole team time.
Tell me about a conflict with a teammate.
Stay blameless and outcome-focused: "A teammate kept pushing directly to main, which broke our build. Instead of calling him out publicly, I asked to pair for ten minutes, showed how a quick PR + CI check avoids it, and proposed we protect the branch. He was on board, and our broken-build rate dropped to near zero." Show empathy + a process fix, never personal criticism.
Tell me about a time you failed or made a mistake.
Pick a real, low-stakes mistake and emphasize the lesson: "I once deployed a change without testing an edge case and it crashed for empty input. I rolled back, added a unit test for that case, and now I write the test first. It taught me that 'it works on my machine' isn't done." Owning it + a concrete habit change is exactly what they want.
How do you handle pressure / a tight deadline?
"I break the work into must-haves vs nice-to-haves, communicate early if scope is at risk, and protect a working version at every step. On a hackathon I cut a fancy feature to guarantee the core demo ran - we placed because it actually worked." Show prioritization and proactive communication, not heroics.
Tell me about learning a new technology quickly.
"I'd never used React, but a project needed it. I did the official tutorial, built a tiny throwaway app to cement the concepts, then leaned on docs + small experiments rather than copying blindly. Within a week I was productive." Show a repeatable learning process, not just 'I'm a fast learner.'
Explain a complex technical concept to a non-technical person.
Use an analogy and drop the jargon: "I explained an API as a waiter - you don't go into the kitchen, you give the waiter your order and they bring the food back. The API is the messenger between your app and the server." The skill being tested is empathy + clear communication.
How do you juggle multiple tasks?
"I list everything, rank by deadline and impact, time-box focused blocks, and re-check priorities daily. If two things truly collide, I ask my lead which matters more rather than guessing." Concrete system > "I'm good at multitasking."
Why do you want to work here?
Tie their mission/product to something specific about you: "You work on developer tools, and I've spent two years building side projects to make my own workflow faster - I want to do that for millions of engineers." Always research the company first; generic answers read as low effort.
How do you stay current with new tech and trends?
Show an active habit, not a list of podcasts: "I follow release notes and a couple of engineering blogs, but mostly I learn by building - when something looks useful I spin up a small project to try it. Recently I taught myself X that way." Demonstrating you learn by doing beats name-dropping resources.
When did you apply your skills to a business problem?
Connect code to measurable impact: "Support was drowning in repetitive tickets, so I wrote a script that auto-categorized them and surfaced the top issues. It cut triage time about 40% and let the team fix root causes instead of symptoms." Always link the tech to a business outcome.
Smart questions to ask THEM (have 2-3 ready)

"What does success look like in the first 90 days?" · "How does the team handle code review and testing?" · "What's the biggest technical challenge the team is facing right now?" · "How do you support growth for early-career engineers?" Asking nothing signals low interest - always ask at least two.

🎓 CS fundamentals - the questions interviewers actually ask

Crisp, correct answers to the conceptual questions that come up constantly (languages, OOP, memory/OS, databases, architecture & process). Free to read - tap any question.

Languages & execution
Compiler vs interpreter?
A compiler translates the whole program to machine code ahead of time, producing a standalone executable - fast to run, slower to build (C++, Go, Rust). An interpreter executes the source line-by-line at runtime - slower but flexible and easy to debug (Python). Hybrids (Java, C#) compile to bytecode, then a VM interprets/JIT-compiles it.
Statically vs dynamically typed languages?
Static typing checks types at compile time (Java, C++) - catches type errors early, more boilerplate. Dynamic typing checks at runtime (Python, JavaScript) - faster to write, but type errors surface later.
Object-oriented vs functional programming?
OOP organizes code around objects bundling state + behavior, emphasizing encapsulation, inheritance and polymorphism. Functional programming organizes around pure functions and immutable data, avoiding shared state and side effects. Most modern languages support both.
Character stream vs byte stream?
A byte stream reads/writes raw 8-bit bytes - use for binary data (images, files). A character stream reads/writes 16-bit characters with an encoding - better for text because it handles Unicode correctly. In Java: InputStream/OutputStream vs Reader/Writer.
Compiled vs interpreted language?
About a language's usual execution model. A compiled language is turned into native machine code before running (C, C++, Go) → fast execution, platform-specific binaries. An interpreted language is executed by an interpreter at runtime (Python, JavaScript, Ruby) → portable and quick to iterate, but slower. The line blurs: Java/C# compile to bytecode, then a VM runs (and JIT-compiles) it.
Object-oriented programming
What is a class? What is a superclass?
A class is a blueprint defining the data (fields) and behavior (methods) of a type of object - it encapsulates state and promotes reuse. A superclass is a parent/base class that another class (a subclass) inherits from; the subclass gets all the superclass's fields and methods and can add or override its own.
Overriding vs overloading?
Overriding: a subclass replaces an inherited method with its own implementation - same signature, resolved at runtime (polymorphism). Overloading: several methods share a name but have different parameter lists in the same class - resolved at compile time.
Pros and cons of multiple inheritance?
Pro: a class can combine behavior from several parents. Con: the "diamond problem" - ambiguity when two parents define the same method - plus extra complexity. That's why Java/C# forbid it for classes and use interfaces instead.
The SOLID principles?
Five OO design principles for maintainable code: Single-responsibility (one reason to change), Open/closed (open to extension, closed to modification), Liskov substitution (subtypes must be usable as their base), Interface segregation (many small interfaces > one fat one), Dependency inversion (depend on abstractions, not concretions).
Default vs conversion constructor?
A default constructor takes no arguments and sets default field values. A conversion constructor takes a single argument of another type and builds an object from it, enabling (often implicit) type conversion.
Memory & operating systems
What is garbage collection? How is memory managed?
Garbage collection automatically reclaims memory that's no longer reachable, so you don't free() manually (Java, Python, C#). The GC tracks references and frees unreachable objects; the trade-off is occasional pauses and less control. In manual languages (C/C++) you allocate (malloc/new) and must deallocate (free/delete) yourself, or you leak memory.
Primary vs secondary memory?
Primary (RAM) is fast, volatile main memory the CPU works from directly - contents are lost on power-off. Secondary (SSD, HDD, USB) is slower, non-volatile, far larger, and stores data permanently.
What is virtual memory?
An OS technique that gives each process its own large, contiguous address space by mapping it to physical RAM plus disk (paging). It lets programs use more memory than physically exists and isolates processes from one another.
What is a deadlock? How do you prevent it?
Two or more processes each hold a resource and wait for one the other holds, so none can proceed. Prevent it by breaking a Coffman condition - e.g. acquire locks in a consistent global order, use timeouts, or avoid hold-and-wait.
What is an operating system?
Software that manages hardware and resources and lets applications run - handling process scheduling, memory management, file systems and I/O. Common ones: Windows (compatibility), macOS (UX), Linux (servers, customizability).
Databases
What is normalization and why does it matter?
Organizing tables to cut redundancy and avoid update anomalies by splitting data into related tables (1NF → 2NF → 3NF…). It keeps data consistent and storage efficient; you JOIN tables when you need the combined view.
What is a transaction (ACID)?
A unit of work that must fully complete or fully roll back. ACID = Atomicity, Consistency, Isolation, Durability - guaranteeing reliable processing despite crashes or concurrent access.
Primary key vs foreign key?
A primary key uniquely identifies each row in a table. A foreign key is a column that references another table's primary key, enforcing a relationship and referential integrity between them.
What is a distributed database?
A database spread across multiple machines/locations that appears as one logical database. It improves scalability and availability but must handle replication, consistency and partition tolerance (the CAP trade-off).
Architecture, web & process
Monolith vs microservices?
A monolith is one deployable unit holding all features - simple to start, harder to scale and deploy piecemeal. Microservices split the app into small, independently deployable services talking over APIs - scalable and resilient, but more operational complexity.
What are CI and CD?
Continuous Integration = automatically build + test every change so issues surface early. Continuous Delivery/Deployment = automatically release those validated changes to staging/production, reducing risky manual releases.
What is DevOps?
A culture + practices uniting development and operations to ship faster and more reliably - automation, CI/CD, infrastructure-as-code, and continuous monitoring.
Waterfall vs Agile (and what is Scrum)?
Waterfall is sequential (requirements → design → build → test → release) - predictable, but inflexible to change. Agile is iterative: small increments in short cycles with continuous feedback, so you adapt and ship value sooner. Scrum is an Agile framework with 1-4-week sprints, daily standups, a prioritized backlog, and roles (Product Owner, Scrum Master, Dev Team).
Client-side vs server-side? SPA vs traditional?
Client-side code runs in the browser (HTML/CSS/JS) for interactivity; server-side runs on the server (Python/Java/Node) for data, auth and business logic. A SPA loads one page and updates content via JS/API calls without full reloads (app-like); a traditional multi-page app fetches a fresh page from the server per navigation.
Responsive design? Load balancing vs fault tolerance?
Responsive design adapts pages to any screen via fluid grids, flexible images and CSS media queries. Load balancing spreads requests across servers to avoid overload; fault tolerance keeps the system working when a component fails (redundancy/failover) - related but distinct goals.
Explain the Software Development Life Cycle (SDLC).
The process for building software end-to-end: requirements → design → implementation → testing → deployment → maintenance. Models include Waterfall (sequential) and Agile (iterative). It gives teams a structured, repeatable way to deliver quality software predictably.
Centralized vs decentralized system?
A centralized system has one controlling node/server everything depends on - simple, but a single point of failure. A decentralized system spreads control across many nodes with no single authority - more resilient and scalable, but harder to coordinate (e.g. peer-to-peer networks, blockchains).
Consensus algorithms in distributed systems?
They let many nodes agree on a single value/state despite failures or network delays. Protocols like Paxos and Raft elect a leader and replicate a log so all nodes stay consistent; blockchains use Proof-of-Work/Stake. They juggle consistency, availability and partition tolerance - the CAP theorem says you can't fully guarantee all three at once.
Algorithm concepts
Explain the idea behind dynamic programming.
Break a problem into overlapping sub-problems with optimal substructure, solve each once and store the result (memoization top-down, or tabulation bottom-up) to avoid recomputation. It turns exponential brute force into polynomial time - e.g. Fibonacci, knapsack, edit distance, LCS. (See the worked DP problems above.)
Time/space complexity of common data structures?
Array: index O(1), search O(n). Linked list: insert/delete at a known node O(1), search O(n). Balanced BST: search/insert/delete O(log n). Hash map: average O(1). Graph traversal (BFS/DFS): O(V+E). (Full table at the top of this page.)
How would you implement BFS (memory, queues, cycles)?
Use a queue: enqueue the start, then repeatedly dequeue a node, record it, and enqueue its unseen neighbors. Mark nodes seen at enqueue time (not dequeue) so a cyclic graph never re-adds a node. Time O(V+E), space O(V). See the Java implementation below.
Shortest path in a weighted graph?
For non-negative weights use Dijkstra's algorithm: keep a min-heap of (distance, node), repeatedly pop the closest unvisited node and relax its edges (update neighbor distances). O((V+E) log V). For graphs with negative weights use Bellman-Ford; for an unweighted graph plain BFS already gives the shortest path. (Dijkstra skeleton is in the Code templates section.)
~190 classic problems + the top 150 interview questions - original Python, Big-O & difficulty tags, the 25 pattern templates, complexity tables, and the 7-stage interview playbook. One of the most complete free interview references anywhere. Bookmark it and good luck! 🍀