The Complete Guide

Blind 75: Master LeetCode

Conquer the 75 most important LeetCode problems. Master data structures, algorithms, and problem-solving patterns that will prepare you for any coding interview.

Array Problems

Master array manipulation, sliding windows, and two-pointer techniques.

Two Sum

What You'll Learn

Hash maps for O(1) lookups and efficient pair finding.

Real-World Context

"Find two numbers in an array that add up to a target sum."

Breaking It Down

  • Create a hash map to store numbers and their indices
  • Iterate through the array
  • For each number, check if target - current exists in map
  • Return indices if found, otherwise add current to map
Python 3
def twoSum(nums, target):
    num_map = {}
    for i, num in enumerate(nums):
        complement = target - num
        if complement in num_map:
            return [num_map[complement], i]
        num_map[num] = i
    return []

Line-by-Line Walkthrough

Using a hash map allows us to check for complements in constant time. This approach handles duplicates correctly and runs in linear time with linear space.

Complexity Analysis

  • Time: O(N)
  • Space: O(N)
  • Single pass through array with hash map storage

Common Mistakes

  • Using nested loops resulting in O(N²) time
    Use hash map for O(1) complement checks
  • Not handling duplicate values properly
    Store indices in map, not just presence

Try It Yourself

Three Sum - Find three numbers that sum to target

Best Time to Buy and Sell Stock

What You'll Learn

Single pass tracking of minimum and maximum profit.

Real-World Context

"Find the maximum profit from buying and selling a stock once."

Breaking It Down

  • Initialize minimum price and maximum profit
  • Iterate through prices
  • Update minimum price if current is lower
  • Update maximum profit if current profit is higher
  • Return maximum profit
Python 3
def maxProfit(prices):
    if not prices:
        return 0
    min_price = float('inf')
    max_profit = 0
    for price in prices:
        min_price = min(min_price, price)
        max_profit = max(max_profit, price - min_price)
    return max_profit

Line-by-Line Walkthrough

By tracking the minimum price seen so far and calculating profit at each step, we find the optimal buy and sell points in one pass.

Complexity Analysis

  • Time: O(N)
  • Space: O(1)
  • Single pass with constant extra space

Common Mistakes

  • Trying all pairs of buy/sell dates
    Track minimum price and update profit continuously

Try It Yourself

Best Time to Buy and Sell Stock II - Multiple transactions allowed

Contains Duplicate

What You'll Learn

Using hash sets for efficient duplicate detection.

Real-World Context

"Determine if an array contains any duplicate values."

Breaking It Down

  • Create a hash set to store seen numbers
  • Iterate through the array
  • If number is already in set, return true
  • Otherwise add to set
  • Return false if no duplicates found
Python 3
def containsDuplicate(nums):
    seen = set()
    for num in nums:
        if num in seen:
            return True
        seen.add(num)
    return False

Line-by-Line Walkthrough

Hash sets provide O(1) average time complexity for insertions and lookups, making this approach efficient for large arrays.

Complexity Analysis

  • Time: O(N)
  • Space: O(N)
  • Single pass with hash set storage

Common Mistakes

  • Sorting the array and checking adjacent elements
    Use hash set for O(N) time instead of O(N log N)

Try It Yourself

Contains Duplicate II - Check for duplicates within k distance

Product of Array Except Self

What You'll Learn

Prefix and suffix products to avoid division and handle zeros.

Real-World Context

"Return an array where each element is the product of all elements except itself."

Breaking It Down

  • Create result array initialized to 1
  • Compute prefix products from left to right
  • Compute suffix products from right to left
  • Multiply prefix and suffix for each position
  • Handle zeros by tracking zero count
Python 3
def productExceptSelf(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

Line-by-Line Walkthrough

By computing prefix and suffix products separately, we avoid using division and handle zeros correctly. This approach runs in linear time with constant extra space (excluding output).

Complexity Analysis

  • Time: O(N)
  • Space: O(1)
  • Two passes with output array as extra space

Common Mistakes

  • Using division by total product
    Use prefix/suffix products to handle zeros

Try It Yourself

Maximum Product Subarray - Find subarray with maximum product

Maximum Subarray

What You'll Learn

Kadane's algorithm for maximum subarray sum.

Real-World Context

"Find the contiguous subarray with the largest sum."

Breaking It Down

  • Initialize current sum and maximum sum to first element
  • Iterate through the array from second element
  • Update current sum: max(current + num, num)
  • Update maximum sum if current is larger
  • Return maximum sum
Python 3
def maxSubArray(nums):
    if not nums:
        return 0
    max_current = max_global = nums[0]
    for num in nums[1:]:
        max_current = max(num, max_current + num)
        if max_current > max_global:
            max_global = max_current
    return max_global

Line-by-Line Walkthrough

Kadane's algorithm keeps track of the maximum sum ending at each position. When the current sum becomes negative, we start a new subarray.

Complexity Analysis

  • Time: O(N)
  • Space: O(1)
  • Single pass with constant space

Common Mistakes

  • Using brute force O(N²) approach
    Use Kadane's algorithm for O(N) time

Try It Yourself

Maximum Product Subarray - Similar but for product

Maximum Product Subarray

What You'll Learn

Tracking both maximum and minimum products for negative numbers.

Real-World Context

"Find the contiguous subarray with the largest product."

Breaking It Down

  • Initialize max_so_far, min_so_far, and result to first element
  • Iterate through the array
  • If current is negative, swap max and min
  • Update max_so_far and min_so_far
  • Update result with max_so_far
  • Return result
Python 3
def maxProduct(nums):
    if not nums:
        return 0
    max_so_far = min_so_far = result = nums[0]
    for num in nums[1:]:
        if num < 0:
            max_so_far, min_so_far = min_so_far, max_so_far
        max_so_far = max(num, max_so_far * num)
        min_so_far = min(num, min_so_far * num)
        result = max(result, max_so_far)
    return result

Line-by-Line Walkthrough

Because of negative numbers, we need to track both the maximum and minimum product ending at each position. Negative numbers can turn a minimum into a maximum when multiplied.

Complexity Analysis

  • Time: O(N)
  • Space: O(1)
  • Single pass with constant space

Common Mistakes

  • Not handling negative numbers properly
    Track both max and min products

Try It Yourself

Maximum Subarray - Sum instead of product

Find Minimum in Rotated Sorted Array

What You'll Learn

Binary search in rotated arrays.

Real-World Context

"Find the minimum element in a rotated sorted array."

Breaking It Down

  • Use binary search with left and right pointers
  • If array is not rotated, return first element
  • Check middle element
  • If mid > right, minimum is in right half
  • If mid < right, minimum is in left half
  • Return the minimum found
Python 3
def findMin(nums):
    left, right = 0, len(nums) - 1
    while left < right:
        mid = (left + right) // 2
        if nums[mid] > nums[right]:
            left = mid + 1
        else:
            right = mid
    return nums[left]

Line-by-Line Walkthrough

The rotation point is where the minimum is. We use binary search to find where the array is no longer sorted.

Complexity Analysis

  • Time: O(log N)
  • Space: O(1)
  • Binary search with constant space

Common Mistakes

  • Linear search
    Use binary search for log N time

Try It Yourself

Search in Rotated Sorted Array - Find specific element

Search in Rotated Sorted Array

What You'll Learn

Modified binary search for rotated arrays.

Real-World Context

"Search for a target in a rotated sorted array."

Breaking It Down

  • Use binary search
  • Determine which half is sorted
  • Check if target is in the sorted half
  • Adjust pointers accordingly
  • Return index or -1 if not found
Python 3
def search(nums, target):
    left, right = 0, len(nums) - 1
    while left <= right:
        mid = (left + right) // 2
        if nums[mid] == target:
            return mid
        if nums[left] <= nums[mid]:
            if nums[left] <= target < nums[mid]:
                right = mid - 1
            else:
                left = mid + 1
        else:
            if nums[mid] < target <= nums[right]:
                left = mid + 1
            else:
                right = mid - 1
    return -1

Line-by-Line Walkthrough

We determine which half is sorted and check if the target could be in that half, then search accordingly.

Complexity Analysis

  • Time: O(log N)
  • Space: O(1)
  • Modified binary search

Common Mistakes

  • Not identifying the sorted half
    Check nums[left] <= nums[mid] to determine sorted half

Try It Yourself

Find Minimum in Rotated Sorted Array - Find minimum instead of search

3Sum

What You'll Learn

Two pointers with sorting for efficient triplet finding.

Real-World Context

"Find all unique triplets that sum to zero."

Breaking It Down

  • Sort the array
  • For each element, use two pointers on the rest
  • Skip duplicates to avoid duplicate triplets
  • Move pointers based on sum comparison
  • Add valid triplets to result
Python 3
def threeSum(nums):
    nums.sort()
    result = []
    for i in range(len(nums) - 2):
        if i > 0 and nums[i] == nums[i-1]:
            continue
        left, right = i + 1, len(nums) - 1
        while left < right:
            total = nums[i] + nums[left] + nums[right]
            if total < 0:
                left += 1
            elif total > 0:
                right -= 1
            else:
                result.append([nums[i], nums[left], nums[right]])
                while left < right and nums[left] == nums[left+1]:
                    left += 1
                while left < right and nums[right] == nums[right-1]:
                    right -= 1
                left += 1
                right -= 1
    return result

Line-by-Line Walkthrough

Sorting allows us to use two pointers efficiently. We fix one element and find pairs that sum to its negative.

Complexity Analysis

  • Time: O(N²)
  • Space: O(1)
  • Sorting is O(N log N), two pointers is O(N²)

Common Mistakes

  • Not skipping duplicates
    Skip duplicate elements to avoid duplicate triplets

Try It Yourself

4Sum - Find four numbers that sum to target

Container With Most Water

What You'll Learn

Two pointers for maximum area calculation.

Real-World Context

"Find two lines that form a container with the most water."

Breaking It Down

  • Use two pointers at start and end
  • Calculate area as min(height[left], height[right]) * (right - left)
  • Move the pointer with smaller height inward
  • Track maximum area
  • Return maximum area
Python 3
def maxArea(height):
    left, right = 0, len(height) - 1
    max_area = 0
    while left < right:
        width = right - left
        h = min(height[left], height[right])
        area = width * h
        max_area = max(max_area, area)
        if height[left] < height[right]:
            left += 1
        else:
            right -= 1
    return max_area

Line-by-Line Walkthrough

Moving the shorter line inward gives a chance for a taller line, potentially increasing the area.

Complexity Analysis

  • Time: O(N)
  • Space: O(1)
  • Single pass with two pointers

Common Mistakes

  • Brute force all pairs
    Use two pointers to achieve O(N) time

Try It Yourself

Trapping Rain Water - Calculate trapped water

Binary Problems

Bit manipulation, bitwise operations, and binary representations.

Sum of Two Integers

What You'll Learn

Bit manipulation for addition without + or -.

Real-World Context

"Calculate sum of two integers using bitwise operations."

Breaking It Down

  • Use XOR for sum without carry
  • Use AND and left shift for carry
  • Repeat until no carry
  • Return the result
Python 3
def getSum(a, b):
    while b != 0:
        carry = a & b
        a = a ^ b
        b = carry << 1
    return a

Line-by-Line Walkthrough

This simulates binary addition: XOR gives sum bits, AND gives carry bits.

Complexity Analysis

  • Time: O(1)
  • Space: O(1)
  • Fixed number of bits

Common Mistakes

  • Using + operator
    Use bitwise operations

Try It Yourself

Subtract two numbers using bitwise operations

Number of 1 Bits

What You'll Learn

Bit manipulation and counting set bits efficiently.

Real-World Context

"Count the number of 1 bits in the binary representation of a number."

Breaking It Down

  • Initialize counter to 0
  • While number is not zero
  • Check if least significant bit is 1 using n & 1
  • Add to counter and right shift n by 1
  • Return counter
Python 3
def hammingWeight(n):
    count = 0
    while n:
        count += n & 1
        n >>= 1
    return count

Line-by-Line Walkthrough

By checking each bit individually through right shifts, we count all set bits. This works for both positive and unsigned integers.

Complexity Analysis

  • Time: O(1)
  • Space: O(1)
  • Fixed 32-bit operations regardless of input

Common Mistakes

  • Using string conversion and counting '1's
    Use bitwise operations for efficiency

Try It Yourself

Counting Bits - Count bits for all numbers up to n

Counting Bits

What You'll Learn

Dynamic programming for bit counting.

Real-World Context

"Count the number of 1 bits for all numbers from 0 to n."

Breaking It Down

  • Initialize dp array of size n+1
  • For each number i from 1 to n
  • dp[i] = dp[i >> 1] + (i & 1)
  • Return the dp array
Python 3
def countBits(n):
    dp = [0] * (n + 1)
    for i in range(1, n + 1):
        dp[i] = dp[i >> 1] + (i & 1)
    return dp

Line-by-Line Walkthrough

Each number's bit count is the bit count of its half plus the least significant bit.

Complexity Analysis

  • Time: O(N)
  • Space: O(N)
  • Linear pass building dp array

Common Mistakes

  • Counting bits for each number separately
    Use DP to reuse previous calculations

Try It Yourself

Number of 1 Bits - Count for single number

Missing Number

What You'll Learn

XOR for finding missing element in sequence.

Real-World Context

"Find the missing number in array of 0 to n."

Breaking It Down

  • XOR all numbers from 0 to n
  • XOR all numbers in the array
  • XOR the two results to find missing number
  • Return the missing number
Python 3
def missingNumber(nums):
    n = len(nums)
    xor_all = 0
    for i in range(n + 1):
        xor_all ^= i
    for num in nums:
        xor_all ^= num
    return xor_all

Line-by-Line Walkthrough

XOR of a number with itself is 0, so all present numbers cancel out, leaving the missing one.

Complexity Analysis

  • Time: O(N)
  • Space: O(1)
  • Single pass with XOR

Common Mistakes

  • Using sum and subtracting
    Use XOR to avoid overflow issues

Try It Yourself

Find the duplicate number - Similar but with duplicate

Reverse Bits

What You'll Learn

Bit manipulation for reversing bit order.

Real-World Context

"Reverse the bits of a 32-bit unsigned integer."

Breaking It Down

  • Initialize result to 0
  • For 32 bits
  • Shift result left by 1
  • Add least significant bit of n to result
  • Shift n right by 1
  • Return result
Python 3
def reverseBits(n):
    result = 0
    for _ in range(32):
        result = (result << 1) | (n & 1)
        n >>= 1
    return result

Line-by-Line Walkthrough

We build the reversed number bit by bit, taking the LSB of n and putting it as MSB of result.

Complexity Analysis

  • Time: O(1)
  • Space: O(1)
  • Fixed 32 operations

Common Mistakes

  • Not handling 32 bits properly
    Loop exactly 32 times

Try It Yourself

Number of 1 Bits - Count set bits instead of reverse

Dynamic Programming

Breaking problems into subproblems and optimal substructure.

Climbing Stairs

What You'll Learn

Bottom-up DP for counting ways with recurrence relations.

Real-World Context

"Find the number of ways to climb n stairs, taking 1 or 2 steps at a time."

Breaking It Down

  • Handle base cases: 0 stairs = 1 way, 1 stair = 1 way
  • Initialize dp array or variables for previous two steps
  • For each step from 2 to n, ways = ways(n-1) + ways(n-2)
  • Return the nth step ways
Python 3
def climbStairs(n):
    if n <= 1:
        return 1
    prev1, prev2 = 1, 1
    for i in range(2, n + 1):
        current = prev1 + prev2
        prev2 = prev1
        prev1 = current
    return prev1

Line-by-Line Walkthrough

This is the Fibonacci sequence where each step depends on the previous two. Using iterative DP avoids recursion stack overflow.

Complexity Analysis

  • Time: O(N)
  • Space: O(1)
  • Linear pass with constant space using two variables

Common Mistakes

  • Using full recursion without memoization
    Use iterative approach or memoization to avoid exponential time

Try It Yourself

House Robber - Maximize robbery with adjacent house constraint

Coin Change

What You'll Learn

DP for minimum coins to make amount.

Real-World Context

"Find the minimum number of coins needed to make a given amount."

Breaking It Down

  • Initialize dp array with infinity, dp[0] = 0
  • For each coin, for each amount from coin to target
  • dp[amount] = min(dp[amount], dp[amount - coin] + 1)
  • Return dp[target] if not infinity, else -1
Python 3
def coinChange(coins, amount):
    dp = [float('inf')] * (amount + 1)
    dp[0] = 0
    for coin in coins:
        for i in range(coin, amount + 1):
            dp[i] = min(dp[i], dp[i - coin] + 1)
    return dp[amount] if dp[amount] != float('inf') else -1

Line-by-Line Walkthrough

We build up the minimum coins for each amount using previously computed values.

Complexity Analysis

  • Time: O(amount * coins)
  • Space: O(amount)
  • Nested loops over coins and amounts

Common Mistakes

  • Greedy approach without checking all possibilities
    Use DP to consider all combinations

Try It Yourself

Coin Change 2 - Number of ways to make amount

Longest Increasing Subsequence

What You'll Learn

DP for longest increasing subsequence.

Real-World Context

"Find the length of the longest increasing subsequence."

Breaking It Down

  • Initialize dp array with 1s
  • For each element, check all previous elements
  • If nums[i] > nums[j], dp[i] = max(dp[i], dp[j] + 1)
  • Return the maximum in dp
Python 3
def lengthOfLIS(nums):
    if not nums:
        return 0
    dp = [1] * len(nums)
    for i in range(1, len(nums)):
        for j in range(i):
            if nums[i] > nums[j]:
                dp[i] = max(dp[i], dp[j] + 1)
    return max(dp)

Line-by-Line Walkthrough

For each element, we look at all previous elements and extend the longest subsequence ending before it.

Complexity Analysis

  • Time: O(N²)
  • Space: O(N)
  • Nested loops for each pair

Common Mistakes

  • Not considering all previous elements
    Check every previous element for extension

Try It Yourself

Longest Common Subsequence - Between two strings

Longest Common Subsequence

What You'll Learn

2D DP for string comparison.

Real-World Context

"Find the length of the longest common subsequence between two strings."

Breaking It Down

  • Create 2D dp table of size (m+1) x (n+1)
  • If characters match, dp[i][j] = dp[i-1][j-1] + 1
  • Else, dp[i][j] = max(dp[i-1][j], dp[i][j-1])
  • Return dp[m][n]
Python 3
def longestCommonSubsequence(text1, text2):
    m, n = len(text1), len(text2)
    dp = [[0] * (n + 1) for _ in range(m + 1)]
    for i in range(1, m + 1):
        for j in range(1, n + 1):
            if text1[i-1] == text2[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[m][n]

Line-by-Line Walkthrough

The DP table represents the LCS length for substrings. We build it bottom-up.

Complexity Analysis

  • Time: O(M*N)
  • Space: O(M*N)
  • 2D table for both strings

Common Mistakes

  • Not using DP, trying recursive approach
    Use 2D DP for efficiency

Try It Yourself

Edit Distance - Minimum operations to transform one string to another

Word Break

What You'll Learn

DP for string segmentation.

Real-World Context

"Determine if a string can be segmented into words from a dictionary."

Breaking It Down

  • Initialize dp array of size len(s) + 1, dp[0] = True
  • For each position i, if dp[i] is True
  • Check substrings from i to j
  • If substring in wordDict, set dp[j] = True
  • Return dp[len(s)]
Python 3
def wordBreak(s, wordDict):
    wordSet = set(wordDict)
    dp = [False] * (len(s) + 1)
    dp[0] = True
    for i in range(len(s) + 1):
        if dp[i]:
            for j in range(i + 1, len(s) + 1):
                if s[i:j] in wordSet:
                    dp[j] = True
    return dp[len(s)]

Line-by-Line Walkthrough

We use DP to mark positions where we can reach with dictionary words.

Complexity Analysis

  • Time: O(N²)
  • Space: O(N)
  • Nested loops over string positions

Common Mistakes

  • Not using DP, trying all combinations
    Use DP to avoid redundant checks

Try It Yourself

Word Break II - Return all possible sentences

Combination Sum

What You'll Learn

Backtracking for combination problems.

Real-World Context

"Find all unique combinations that sum to target."

Breaking It Down

  • Sort the candidates
  • Use backtracking function
  • For each candidate, add to current combination
  • Recurse with remaining target
  • Backtrack by removing the candidate
  • Skip duplicates
Python 3
def combinationSum(candidates, target):
    def backtrack(start, target, path):
        if target == 0:
            result.append(path[:])
            return
        for i in range(start, len(candidates)):
            if candidates[i] > target:
                break
            path.append(candidates[i])
            backtrack(i, target - candidates[i], path)
            path.pop()
    candidates.sort()
    result = []
    backtrack(0, target, [])
    return result

Line-by-Line Walkthrough

Backtracking explores all possible combinations, pruning when sum exceeds target.

Complexity Analysis

  • Time: O(2^N)
  • Space: O(N)
  • Exponential in worst case, but pruning helps

Common Mistakes

  • Not sorting and skipping duplicates
    Sort candidates and start from current index

Try It Yourself

Combination Sum II - Each number used at most once

House Robber

What You'll Learn

DP for maximum sum with constraints.

Real-World Context

"Maximize the amount of money robbed with no adjacent houses."

Breaking It Down

  • Initialize dp[0] = nums[0], dp[1] = max(nums[0], nums[1])
  • For i from 2 to n-1, dp[i] = max(dp[i-1], dp[i-2] + nums[i])
  • Return dp[n-1]
Python 3
def rob(nums):
    if not nums:
        return 0
    if len(nums) == 1:
        return nums[0]
    dp = [0] * len(nums)
    dp[0] = nums[0]
    dp[1] = max(nums[0], nums[1])
    for i in range(2, len(nums)):
        dp[i] = max(dp[i-1], dp[i-2] + nums[i])
    return dp[-1]

Line-by-Line Walkthrough

We choose for each house whether to rob it or not, based on previous decisions.

Complexity Analysis

  • Time: O(N)
  • Space: O(N)
  • Linear pass with dp array

Common Mistakes

  • Not considering the constraint
    Use DP to track maximum at each step

Try It Yourself

House Robber II - Circular houses

House Robber II

What You'll Learn

DP with circular constraint.

Real-World Context

"Maximize robbery in a circle of houses."

Breaking It Down

  • Handle edge cases
  • Compute max for houses 0 to n-2
  • Compute max for houses 1 to n-1
  • Return the maximum of the two
Python 3
def rob(nums):
    def rob_linear(nums):
        if not nums:
            return 0
        prev1, prev2 = 0, 0
        for num in nums:
            current = max(prev1, prev2 + num)
            prev2 = prev1
            prev1 = current
        return prev1
    if len(nums) == 1:
        return nums[0]
    return max(rob_linear(nums[:-1]), rob_linear(nums[1:]))

Line-by-Line Walkthrough

Since it's circular, we can't rob both first and last. So we compute max excluding first and excluding last.

Complexity Analysis

  • Time: O(N)
  • Space: O(1)
  • Two linear passes

Common Mistakes

  • Not handling the circular constraint
    Compute max for two separate ranges

Try It Yourself

House Robber - Linear houses

Decode Ways

What You'll Learn

DP for counting ways to decode string.

Real-World Context

"Number of ways to decode a string of digits."

Breaking It Down

  • Initialize dp[0] = 1, dp[1] = 1 if s[0] != '0'
  • For i from 2 to len(s)
  • If s[i-1] != '0', dp[i] += dp[i-1]
  • If s[i-2:i] is valid two-digit, dp[i] += dp[i-2]
  • Return dp[len(s)]
Python 3
def numDecodings(s):
    if not s or s[0] == '0':
        return 0
    dp = [0] * (len(s) + 1)
    dp[0] = 1
    dp[1] = 1
    for i in range(2, len(s) + 1):
        if s[i-1] != '0':
            dp[i] += dp[i-1]
        two_digit = int(s[i-2:i])
        if 10 <= two_digit <= 26:
            dp[i] += dp[i-2]
    return dp[len(s)]

Line-by-Line Walkthrough

Each position can be reached from previous one or two positions if valid.

Complexity Analysis

  • Time: O(N)
  • Space: O(N)
  • Linear pass with dp array

Common Mistakes

  • Not checking for leading zeros
    Handle '0' cases carefully

Try It Yourself

Decode Ways II - With '*' wildcards

Unique Paths

What You'll Learn

2D DP for grid path counting.

Real-World Context

"Number of unique paths from top-left to bottom-right in grid."

Breaking It Down

  • Initialize dp grid with 1s for first row and column
  • For each cell, dp[i][j] = dp[i-1][j] + dp[i][j-1]
  • Return dp[m-1][n-1]
Python 3
def uniquePaths(m, n):
    dp = [[1] * n for _ in range(m)]
    for i in range(1, m):
        for j in range(1, n):
            dp[i][j] = dp[i-1][j] + dp[i][j-1]
    return dp[m-1][n-1]

Line-by-Line Walkthrough

Each cell's paths come from top and left cells.

Complexity Analysis

  • Time: O(M*N)
  • Space: O(M*N)
  • Fill the grid

Common Mistakes

  • Using recursion without memoization
    Use DP to avoid recomputation

Try It Yourself

Unique Paths II - With obstacles

Jump Game

What You'll Learn

Greedy for jump reachability.

Real-World Context

"Determine if you can reach the last index."

Breaking It Down

  • Initialize farthest = 0
  • For each index i up to farthest
  • Update farthest = max(farthest, i + nums[i])
  • If farthest >= len-1, return true
  • Return false
Python 3
def canJump(nums):
    farthest = 0
    for i in range(len(nums)):
        if i > farthest:
            return False
        farthest = max(farthest, i + nums[i])
        if farthest >= len(nums) - 1:
            return True
    return True

Line-by-Line Walkthrough

We keep track of the farthest we can reach so far.

Complexity Analysis

  • Time: O(N)
  • Space: O(1)
  • Single pass

Common Mistakes

  • Using DP for each position
    Use greedy to track maximum reach

Try It Yourself

Jump Game II - Minimum jumps to reach end

Graph Problems

Graph traversal, cycles, and connectivity.

Clone Graph

What You'll Learn

DFS or BFS for graph cloning.

Real-World Context

"Create a deep copy of a connected undirected graph."

Breaking It Down

  • Use DFS or BFS
  • Maintain a map from original to cloned nodes
  • For each neighbor, clone if not already cloned
  • Return the cloned start node
Python 3
def cloneGraph(node):
    if not node:
        return None
    cloned = {}
    def dfs(node):
        if node in cloned:
            return cloned[node]
        clone = Node(node.val)
        cloned[node] = clone
        for neighbor in node.neighbors:
            clone.neighbors.append(dfs(neighbor))
        return clone
    return dfs(node)

Line-by-Line Walkthrough

We use a map to keep track of cloned nodes to avoid infinite recursion.

Complexity Analysis

  • Time: O(N)
  • Space: O(N)
  • Visit each node and edge once

Common Mistakes

  • Not using a map, causing infinite loops
    Use map to track cloned nodes

Try It Yourself

Copy List with Random Pointer - Similar with random pointers

Course Schedule

What You'll Learn

Topological sort with DFS or Kahn's algorithm.

Real-World Context

"Determine if you can finish all courses given prerequisites."

Breaking It Down

  • Build adjacency list and indegree array
  • Use queue for Kahn's algorithm
  • Add nodes with indegree 0 to queue
  • Process queue, reduce indegrees
  • If all nodes processed, no cycle
Python 3
def canFinish(numCourses, prerequisites):
    from collections import deque
    adj = [[] for _ in range(numCourses)]
    indegree = [0] * numCourses
    for course, prereq in prerequisites:
        adj[prereq].append(course)
        indegree[course] += 1
    queue = deque([i for i in range(numCourses) if indegree[i] == 0])
    count = 0
    while queue:
        course = queue.popleft()
        count += 1
        for neighbor in adj[course]:
            indegree[neighbor] -= 1
            if indegree[neighbor] == 0:
                queue.append(neighbor)
    return count == numCourses

Line-by-Line Walkthrough

If there's a cycle, not all courses can be finished. Topological sort detects this.

Complexity Analysis

  • Time: O(V+E)
  • Space: O(V+E)
  • Adjacency list and queue

Common Mistakes

  • Not detecting cycles
    Use topological sort

Try It Yourself

Course Schedule II - Return the order

Pacific Atlantic Water Flow

What You'll Learn

Multi-source BFS or DFS for flow problems.

Real-World Context

"Find cells that can flow to both oceans."

Breaking It Down

  • Use DFS or BFS from Pacific and Atlantic borders
  • Mark cells reachable to each ocean
  • Find intersection of both sets
Python 3
def pacificAtlantic(matrix):
    if not matrix or not matrix[0]:
        return []
    m, n = len(matrix), len(matrix[0])
    pacific = set()
    atlantic = set()
    def dfs(i, j, visited, prev_height):
        if (i, j) in visited or i < 0 or i >= m or j < 0 or j >= n or matrix[i][j] < prev_height:
            return
        visited.add((i, j))
        dfs(i+1, j, visited, matrix[i][j])
        dfs(i-1, j, visited, matrix[i][j])
        dfs(i, j+1, visited, matrix[i][j])
        dfs(i, j-1, visited, matrix[i][j])
    for i in range(m):
        dfs(i, 0, pacific, matrix[i][0])
        dfs(i, n-1, atlantic, matrix[i][n-1])
    for j in range(n):
        dfs(0, j, pacific, matrix[0][j])
        dfs(m-1, j, atlantic, matrix[m-1][j])
    return list(pacific & atlantic)

Line-by-Line Walkthrough

We start from the borders and flow to higher or equal heights.

Complexity Analysis

  • Time: O(M*N)
  • Space: O(M*N)
  • Visit each cell multiple times

Common Mistakes

  • Not starting from borders
    Multi-source from ocean borders

Try It Yourself

Surrounded Regions - Mark regions not surrounded

Number of Islands

What You'll Learn

DFS or BFS for connected components.

Real-World Context

"Count the number of islands in a grid."

Breaking It Down

  • Iterate through grid
  • When find '1', increment count and DFS/BFS to mark visited
  • Mark all connected '1's as visited
  • Return count
Python 3
def numIslands(grid):
    if not grid or not grid[0]:
        return 0
    m, n = len(grid), len(grid[0])
    count = 0
    def dfs(i, j):
        if i < 0 or i >= m or j < 0 or j >= n or grid[i][j] == '0':
            return
        grid[i][j] = '0'
        dfs(i+1, j)
        dfs(i-1, j)
        dfs(i, j+1)
        dfs(i, j-1)
    for i in range(m):
        for j in range(n):
            if grid[i][j] == '1':
                count += 1
                dfs(i, j)
    return count

Line-by-Line Walkthrough

Each DFS/BFS marks one island and all its connected land.

Complexity Analysis

  • Time: O(M*N)
  • Space: O(M*N)
  • Visit each cell once

Common Mistakes

  • Not marking visited
    Mark cells as visited during traversal

Try It Yourself

Max Area of Island - Find largest island area

Longest Consecutive Sequence

What You'll Learn

Hash set for efficient lookups.

Real-World Context

"Find the longest consecutive sequence in array."

Breaking It Down

  • Put all numbers in a hash set
  • For each number, if it's the start of a sequence
  • Count consecutive numbers
  • Update max length
Python 3
def longestConsecutive(nums):
    num_set = set(nums)
    max_length = 0
    for num in nums:
        if num - 1 not in num_set:
            current = num
            length = 1
            while current + 1 in num_set:
                current += 1
                length += 1
            max_length = max(max_length, length)
    return max_length

Line-by-Line Walkthrough

We only start counting from numbers that are the beginning of sequences.

Complexity Analysis

  • Time: O(N)
  • Space: O(N)
  • Hash set operations

Common Mistakes

  • Sorting the array
    Use hash set for O(1) lookups

Try It Yourself

Consecutive Numbers Sum - Number of ways to write as sum of consecutives

Alien Dictionary

What You'll Learn

Topological sort for ordering.

Real-World Context

"Derive the order of letters from alien dictionary."

Breaking It Down

  • Build graph from adjacent words
  • Use topological sort
  • Return the order or detect cycle
Python 3
def alienOrder(words):
    from collections import defaultdict, deque
    adj = defaultdict(list)
    indegree = {c: 0 for word in words for c in word}
    for i in range(len(words) - 1):
        w1, w2 = words[i], words[i+1]
        for j in range(min(len(w1), len(w2))):
            if w1[j] != w2[j]:
                if w2[j] not in adj[w1[j]]:
                    adj[w1[j]].append(w2[j])
                    indegree[w2[j]] += 1
                break
        else:
            if len(w1) > len(w2):
                return ""
    queue = deque([c for c in indegree if indegree[c] == 0])
    result = []
    while queue:
        c = queue.popleft()
        result.append(c)
        for neighbor in adj[c]:
            indegree[neighbor] -= 1
            if indegree[neighbor] == 0:
                queue.append(neighbor)
    return "".join(result) if len(result) == len(indegree) else ""

Line-by-Line Walkthrough

We build edges between letters that appear in order in adjacent words.

Complexity Analysis

  • Time: O(N)
  • Space: O(1)
  • Limited alphabet size

Common Mistakes

  • Not handling invalid cases
    Check for cycles and invalid orders

Try It Yourself

Course Schedule - Similar topological sort

Interval Problems

Sorting and merging intervals.

Insert Interval

What You'll Learn

Insert and merge intervals.

Real-World Context

"Insert a new interval into sorted intervals and merge overlapping."

Breaking It Down

  • Find position to insert
  • Insert the interval
  • Merge overlapping intervals
Python 3
def insert(intervals, newInterval):
    result = []
    i = 0
    while i < len(intervals) and intervals[i][1] < newInterval[0]:
        result.append(intervals[i])
        i += 1
    while i < len(intervals) and intervals[i][0] <= newInterval[1]:
        newInterval[0] = min(newInterval[0], intervals[i][0])
        newInterval[1] = max(newInterval[1], intervals[i][1])
        i += 1
    result.append(newInterval)
    while i < len(intervals):
        result.append(intervals[i])
        i += 1
    return result

Line-by-Line Walkthrough

We insert the new interval and merge with overlapping ones.

Complexity Analysis

  • Time: O(N)
  • Space: O(N)
  • Single pass through intervals

Common Mistakes

  • Not merging properly
    Update the new interval bounds

Try It Yourself

Merge Intervals - Merge all overlapping

Merge Intervals

What You'll Learn

Sort and merge overlapping intervals.

Real-World Context

"Merge all overlapping intervals."

Breaking It Down

  • Sort intervals by start time
  • Initialize result with first interval
  • For each interval, if overlaps with last in result, merge
  • Else append to result
Python 3
def merge(intervals):
    if not intervals:
        return []
    intervals.sort(key=lambda x: x[0])
    result = [intervals[0]]
    for interval in intervals[1:]:
        if interval[0] <= result[-1][1]:
            result[-1][1] = max(result[-1][1], interval[1])
        else:
            result.append(interval)
    return result

Line-by-Line Walkthrough

Sorting allows us to merge adjacent overlapping intervals.

Complexity Analysis

  • Time: O(N log N)
  • Space: O(N)
  • Sorting dominates

Common Mistakes

  • Not sorting first
    Sort by start time

Try It Yourself

Insert Interval - Insert one interval

Non-overlapping Intervals

What You'll Learn

Greedy for minimum removals.

Real-World Context

"Find minimum number of intervals to remove to make non-overlapping."

Breaking It Down

  • Sort by end time
  • Initialize count and end
  • For each interval, if overlaps, increment count
  • Else update end
Python 3
def eraseOverlapIntervals(intervals):
    if not intervals:
        return 0
    intervals.sort(key=lambda x: x[1])
    count = 0
    end = intervals[0][1]
    for interval in intervals[1:]:
        if interval[0] < end:
            count += 1
        else:
            end = interval[1]
    return count

Line-by-Line Walkthrough

By sorting by end time, we greedily keep intervals that end earliest.

Complexity Analysis

  • Time: O(N log N)
  • Space: O(1)
  • Sorting

Common Mistakes

  • Sorting by start time
    Sort by end time for greedy choice

Try It Yourself

Meeting Rooms - Check if can attend all meetings

Meeting Rooms II

What You'll Learn

Priority queue for minimum rooms.

Real-World Context

"Find minimum number of meeting rooms required."

Breaking It Down

  • Sort meetings by start time
  • Use min-heap for end times
  • For each meeting, if heap top <= start, pop
  • Push current end, update max rooms
Python 3
def minMeetingRooms(intervals):
    if not intervals:
        return 0
    intervals.sort(key=lambda x: x[0])
    import heapq
    heap = []
    max_rooms = 0
    for interval in intervals:
        while heap and heap[0] <= interval[0]:
            heapq.heappop(heap)
        heapq.heappush(heap, interval[1])
        max_rooms = max(max_rooms, len(heap))
    return max_rooms

Line-by-Line Walkthrough

The heap keeps track of the earliest ending meeting in each room.

Complexity Analysis

  • Time: O(N log N)
  • Space: O(N)
  • Sorting and heap operations

Common Mistakes

  • Not using heap
    Use min-heap for end times

Try It Yourself

Meeting Rooms - Check if one room suffices

Linked List Problems

Pointer manipulation and traversal.

Reverse a Linked List

What You'll Learn

Iterative and recursive reversal.

Real-World Context

"Reverse a singly linked list."

Breaking It Down

  • Initialize prev = None, current = head
  • While current, next = current.next
  • current.next = prev
  • prev = current, current = next
  • Return prev
Python 3
def reverseList(head):
    prev = None
    current = head
    while current:
        next_node = current.next
        current.next = prev
        prev = current
        current = next_node
    return prev

Line-by-Line Walkthrough

We reverse the links one by one, keeping track of previous node.

Complexity Analysis

  • Time: O(N)
  • Space: O(1)
  • Single pass

Common Mistakes

  • Losing the next pointer
    Store next before changing links

Try It Yourself

Reverse Linked List II - Reverse portion of list

Detect Cycle in a Linked List

What You'll Learn

Floyd's cycle detection algorithm.

Real-World Context

"Determine if linked list has a cycle."

Breaking It Down

  • Use slow and fast pointers
  • Slow moves one step, fast two steps
  • If they meet, cycle exists
  • Return true if meet, false if fast reaches end
Python 3
def hasCycle(head):
    if not head or not head.next:
        return False
    slow = head
    fast = head.next
    while fast and fast.next:
        if slow == fast:
            return True
        slow = slow.next
        fast = fast.next.next
    return False

Line-by-Line Walkthrough

If there's a cycle, fast will eventually catch up to slow.

Complexity Analysis

  • Time: O(N)
  • Space: O(1)
  • Linear traversal

Common Mistakes

  • Using hash set
    Use two pointers for O(1) space

Try It Yourself

Linked List Cycle II - Find cycle start

Merge Two Sorted Lists

What You'll Learn

Merging two sorted lists.

Real-World Context

"Merge two sorted linked lists."

Breaking It Down

  • Use dummy node
  • Compare heads, append smaller
  • Move pointers
  • Append remaining list
Python 3
def mergeTwoLists(list1, list2):
    dummy = ListNode()
    current = dummy
    while list1 and list2:
        if list1.val < list2.val:
            current.next = list1
            list1 = list1.next
        else:
            current.next = list2
            list2 = list2.next
        current = current.next
    current.next = list1 if list1 else list2
    return dummy.next

Line-by-Line Walkthrough

We build the merged list by always taking the smaller head.

Complexity Analysis

  • Time: O(N + M)
  • Space: O(1)
  • Single pass through both lists

Common Mistakes

  • Not handling remaining nodes
    Append the remaining list at the end

Try It Yourself

Merge K Sorted Lists - Merge multiple lists

Merge K Sorted Lists

What You'll Learn

Priority queue for merging k lists.

Real-World Context

"Merge k sorted linked lists."

Breaking It Down

  • Use min-heap with first node of each list
  • While heap not empty, pop smallest
  • Add its next to heap
  • Append to result
Python 3
def mergeKLists(lists):
    import heapq
    heap = []
    for i, lst in enumerate(lists):
        if lst:
            heapq.heappush(heap, (lst.val, i, lst))
    dummy = ListNode()
    current = dummy
    while heap:
        val, i, node = heapq.heappop(heap)
        current.next = node
        current = current.next
        if node.next:
            heapq.heappush(heap, (node.next.val, i, node.next))
    return dummy.next

Line-by-Line Walkthrough

The heap always gives us the smallest current node.

Complexity Analysis

  • Time: O(N log K)
  • Space: O(K)
  • Heap of size K

Common Mistakes

  • Merging pairwise
    Use heap for efficient merging

Try It Yourself

Merge Two Sorted Lists - For two lists

Remove Nth Node From End Of List

What You'll Learn

Two pointers for nth from end.

Real-World Context

"Remove the nth node from the end of linked list."

Breaking It Down

  • Use dummy node
  • Two pointers, fast moves n+1 steps ahead
  • Then move both until fast reaches end
  • Remove the node at slow.next
Python 3
def removeNthFromEnd(head, n):
    dummy = ListNode(0, head)
    fast = dummy
    for _ in range(n + 1):
        fast = fast.next
    slow = dummy
    while fast:
        fast = fast.next
        slow = slow.next
    slow.next = slow.next.next
    return dummy.next

Line-by-Line Walkthrough

The fast pointer creates the gap of n nodes between slow and fast.

Complexity Analysis

  • Time: O(N)
  • Space: O(1)
  • Single pass

Common Mistakes

  • Counting from beginning
    Use two pointers

Try It Yourself

Remove Duplicates from Sorted List - Remove duplicates

Reorder List

What You'll Learn

Finding middle and reversing second half.

Real-World Context

"Reorder list to L0->Ln->L1->Ln-1->..."

Breaking It Down

  • Find middle with slow/fast
  • Reverse second half
  • Merge the two halves alternately
Python 3
def reorderList(head):
    if not head or not head.next:
        return
    # Find middle
    slow = fast = head
    while fast and fast.next:
        slow = slow.next
        fast = fast.next.next
    # Reverse second half
    prev = None
    current = slow
    while current:
        next_node = current.next
        current.next = prev
        prev = current
        current = next_node
    # Merge
    first = head
    second = prev
    while second.next:
        temp1 = first.next
        temp2 = second.next
        first.next = second
        second.next = temp1
        first = temp1
        second = temp2

Line-by-Line Walkthrough

We split the list, reverse the second part, and interleave them.

Complexity Analysis

  • Time: O(N)
  • Space: O(1)
  • Three passes

Common Mistakes

  • Not finding middle correctly
    Use slow/fast pointers

Try It Yourself

Palindrome Linked List - Check if palindrome

Matrix Problems

2D array manipulation and traversal.

Set Matrix Zeroes

What You'll Learn

Using first row and column as markers.

Real-World Context

"Set entire row and column to zero if element is zero."

Breaking It Down

  • Check if first row/column have zero
  • Use first row/column to mark
  • Iterate matrix, mark rows/columns
  • Set marked rows/columns to zero
  • Handle first row/column separately
Python 3
def setZeroes(matrix):
    if not matrix or not matrix[0]:
        return
    m, n = len(matrix), len(matrix[0])
    first_row_zero = any(matrix[0][j] == 0 for j in range(n))
    first_col_zero = any(matrix[i][0] == 0 for i in range(m))
    for i in range(1, m):
        for j in range(1, n):
            if matrix[i][j] == 0:
                matrix[i][0] = 0
                matrix[0][j] = 0
    for i in range(1, m):
        if matrix[i][0] == 0:
            for j in range(n):
                matrix[i][j] = 0
    for j in range(1, n):
        if matrix[0][j] == 0:
            for i in range(m):
                matrix[i][j] = 0
    if first_row_zero:
        for j in range(n):
            matrix[0][j] = 0
    if first_col_zero:
        for i in range(m):
            matrix[i][0] = 0

Line-by-Line Walkthrough

We use the first row and column to record which rows and columns need to be zeroed.

Complexity Analysis

  • Time: O(M*N)
  • Space: O(1)
  • In-place modification

Common Mistakes

  • Using extra space
    Use first row and column as markers

Try It Yourself

Game of Life - Similar in-place modification

Spiral Matrix

What You'll Learn

Directional traversal in matrix.

Real-World Context

"Traverse matrix in spiral order."

Breaking It Down

  • Initialize boundaries
  • While within bounds
  • Traverse right, down, left, up
  • Update boundaries after each direction
Python 3
def spiralOrder(matrix):
    if not matrix or not matrix[0]:
        return []
    result = []
    top, bottom = 0, len(matrix) - 1
    left, right = 0, len(matrix[0]) - 1
    while top <= bottom and left <= right:
        # Traverse right
        for j in range(left, right + 1):
            result.append(matrix[top][j])
        top += 1
        # Traverse down
        for i in range(top, bottom + 1):
            result.append(matrix[i][right])
        right -= 1
        # Traverse left
        if top <= bottom:
            for j in range(right, left - 1, -1):
                result.append(matrix[bottom][j])
            bottom -= 1
        # Traverse up
        if left <= right:
            for i in range(bottom, top - 1, -1):
                result.append(matrix[i][left])
            left += 1
    return result

Line-by-Line Walkthrough

We peel the matrix layer by layer, updating the boundaries.

Complexity Analysis

  • Time: O(M*N)
  • Space: O(1)
  • Visit each cell once

Common Mistakes

  • Not updating boundaries correctly
    Update after each direction traversal

Try It Yourself

Spiral Matrix II - Generate spiral matrix

Rotate Image

What You'll Learn

In-place matrix rotation.

Real-World Context

"Rotate matrix 90 degrees clockwise in-place."

Breaking It Down

  • Transpose the matrix
  • Reverse each row
Python 3
def rotate(matrix):
    n = len(matrix)
    # Transpose
    for i in range(n):
        for j in range(i, n):
            matrix[i][j], matrix[j][i] = matrix[j][i], matrix[i][j]
    # Reverse each row
    for row in matrix:
        row.reverse()

Line-by-Line Walkthrough

Transposing swaps elements across diagonal, reversing rows completes the rotation.

Complexity Analysis

  • Time: O(N²)
  • Space: O(1)
  • In-place operations

Common Mistakes

  • Rotating element by element
    Use transpose and reverse

Try It Yourself

Rotate Image - Counter-clockwise

String Problems

String manipulation and pattern matching.

Longest Substring Without Repeating Characters

What You'll Learn

Sliding window with hash set.

Real-World Context

"Find length of longest substring without repeating characters."

Breaking It Down

  • Use sliding window with hash set
  • Expand right, add to set
  • If duplicate, move left until removed
  • Update max length
Python 3
def lengthOfLongestSubstring(s):
    char_set = set()
    left = 0
    max_length = 0
    for right in range(len(s)):
        while s[right] in char_set:
            char_set.remove(s[left])
            left += 1
        char_set.add(s[right])
        max_length = max(max_length, right - left + 1)
    return max_length

Line-by-Line Walkthrough

The window expands until a duplicate is found, then contracts from the left.

Complexity Analysis

  • Time: O(N)
  • Space: O(min(N, 128))
  • Sliding window

Common Mistakes

  • Using brute force
    Use sliding window

Try It Yourself

Longest Substring with At Most K Distinct Characters

Longest Repeating Character Replacement

What You'll Learn

Sliding window with character count.

Real-World Context

"Find longest substring after replacing k characters."

Breaking It Down

  • Use sliding window
  • Track max frequency in window
  • If window size - max_freq > k, shrink left
  • Update max length
Python 3
def characterReplacement(s, k):
    count = {}
    left = 0
    max_length = 0
    max_freq = 0
    for right in range(len(s)):
        count[s[right]] = count.get(s[right], 0) + 1
        max_freq = max(max_freq, count[s[right]])
        if (right - left + 1) - max_freq > k:
            count[s[left]] -= 1
            left += 1
        max_length = max(max_length, right - left + 1)
    return max_length

Line-by-Line Walkthrough

We maintain a window where the number of replacements needed is <= k.

Complexity Analysis

  • Time: O(N)
  • Space: O(1)
  • Fixed alphabet size

Common Mistakes

  • Not tracking max frequency
    Keep track of highest count in window

Try It Yourself

Max Consecutive Ones III - Similar with flips

Minimum Window Substring

What You'll Learn

Sliding window with character counts.

Real-World Context

"Find minimum window containing all characters of t in s."

Breaking It Down

  • Use two pointers
  • Expand right until window has all chars
  • Shrink left while maintaining validity
  • Update min window
Python 3
def minWindow(s, t):
    if not t or not s:
        return ""
    from collections import Counter
    t_count = Counter(t)
    required = len(t_count)
    left = 0
    min_len = float('inf')
    min_start = 0
    window_count = Counter()
    formed = 0
    for right in range(len(s)):
        window_count[s[right]] += 1
        if s[right] in t_count and window_count[s[right]] == t_count[s[right]]:
            formed += 1
        while left <= right and formed == required:
            if right - left + 1 < min_len:
                min_len = right - left + 1
                min_start = left
            window_count[s[left]] -= 1
            if s[left] in t_count and window_count[s[left]] < t_count[s[left]]:
                formed -= 1
            left += 1
    return "" if min_len == float('inf') else s[min_start:min_start + min_len]

Line-by-Line Walkthrough

We expand the window until it contains all characters, then shrink from left.

Complexity Analysis

  • Time: O(N)
  • Space: O(1)
  • Fixed alphabet

Common Mistakes

  • Not shrinking window
    Shrink when window is valid

Try It Yourself

Substring with Concatenation of All Words

Valid Anagram

What You'll Learn

Character count comparison.

Real-World Context

"Check if two strings are anagrams."

Breaking It Down

  • If lengths differ, false
  • Count characters in first string
  • Decrement for second string
  • Check all counts are zero
Python 3
def isAnagram(s, t):
    if len(s) != len(t):
        return False
    count = [0] * 26
    for c in s:
        count[ord(c) - ord('a')] += 1
    for c in t:
        count[ord(c) - ord('a')] -= 1
    return all(c == 0 for c in count)

Line-by-Line Walkthrough

Anagrams have the same character counts.

Complexity Analysis

  • Time: O(N)
  • Space: O(1)
  • Fixed size count array

Common Mistakes

  • Sorting strings
    Use count array for efficiency

Try It Yourself

Group Anagrams - Group strings by anagrams

Group Anagrams

What You'll Learn

Hash map with sorted strings as keys.

Real-World Context

"Group strings that are anagrams."

Breaking It Down

  • Use hash map with sorted string as key
  • For each string, sort and add to map
  • Return the groups
Python 3
def groupAnagrams(strs):
    from collections import defaultdict
    groups = defaultdict(list)
    for s in strs:
        key = ''.join(sorted(s))
        groups[key].append(s)
    return list(groups.values())

Line-by-Line Walkthrough

Anagrams sort to the same string.

Complexity Analysis

  • Time: O(N K log K)
  • Space: O(N K)
  • Sorting each string

Common Mistakes

  • Using count arrays as keys
    Sort strings for simple keys

Try It Yourself

Valid Anagram - Check two strings

Valid Parentheses

What You'll Learn

Stack for parentheses matching.

Real-World Context

"Check if string has valid parentheses."

Breaking It Down

  • Use stack
  • For each char, if open, push
  • If close, check if matches top
  • Stack empty at end
Python 3
def isValid(s):
    stack = []
    mapping = {')': '(', '}': '{', ']': '['}
    for c in s:
        if c in mapping:
            if not stack or stack[-1] != mapping[c]:
                return False
            stack.pop()
        else:
            stack.append(c)
    return not stack

Line-by-Line Walkthrough

Stack keeps track of open parentheses.

Complexity Analysis

  • Time: O(N)
  • Space: O(N)
  • Stack usage

Common Mistakes

  • Not checking order
    Use stack to match pairs

Try It Yourself

Generate Parentheses - Generate valid combinations

Valid Palindrome

What You'll Learn

Two pointers for palindrome check.

Real-World Context

"Check if string is palindrome ignoring case and non-alphanumeric."

Breaking It Down

  • Use two pointers
  • Skip non-alphanumeric
  • Compare lowercase chars
  • Pointers meet or cross
Python 3
def isPalindrome(s):
    left, right = 0, len(s) - 1
    while left < right:
        while left < right and not s[left].isalnum():
            left += 1
        while left < right and not s[right].isalnum():
            right -= 1
        if s[left].lower() != s[right].lower():
            return False
        left += 1
        right -= 1
    return True

Line-by-Line Walkthrough

We compare from both ends, skipping invalid characters.

Complexity Analysis

  • Time: O(N)
  • Space: O(1)
  • In-place check

Common Mistakes

  • Not ignoring case
    Convert to lowercase

Try It Yourself

Valid Palindrome II - Delete one character

Longest Palindromic Substring

What You'll Learn

Expand around centers.

Real-World Context

"Find longest palindromic substring."

Breaking It Down

  • For each center, expand
  • Check odd and even lengths
  • Track max length and start
Python 3
def longestPalindrome(s):
    if not s:
        return ""
    start = 0
    max_len = 1
    def expand(left, right):
        while left >= 0 and right < len(s) and s[left] == s[right]:
            left -= 1
            right += 1
        return right - left - 1
    for i in range(len(s)):
        len1 = expand(i, i)
        len2 = expand(i, i+1)
        curr_len = max(len1, len2)
        if curr_len > max_len:
            max_len = curr_len
            start = i - (curr_len - 1) // 2
    return s[start:start + max_len]

Line-by-Line Walkthrough

We try expanding from each possible center.

Complexity Analysis

  • Time: O(N²)
  • Space: O(1)
  • Expansion from each center

Common Mistakes

  • DP approach
    Expand around centers for simplicity

Try It Yourself

Palindromic Substrings - Count all palindromes

Palindromic Substrings

What You'll Learn

Count palindromes by expanding.

Real-World Context

"Count number of palindromic substrings."

Breaking It Down

  • For each center, expand and count
  • Odd and even lengths
Python 3
def countSubstrings(s):
    count = 0
    def expand(left, right):
        nonlocal count
        while left >= 0 and right < len(s) and s[left] == s[right]:
            count += 1
            left -= 1
            right += 1
    for i in range(len(s)):
        expand(i, i)  # odd
        expand(i, i+1)  # even
    return count

Line-by-Line Walkthrough

Each valid expansion is a palindrome.

Complexity Analysis

  • Time: O(N²)
  • Space: O(1)
  • Expansion from each center

Common Mistakes

  • Brute force check
    Expand around centers

Try It Yourself

Longest Palindromic Substring - Find longest

Tree Problems

Binary tree traversal and manipulation.

Maximum Depth of Binary Tree

What You'll Learn

Recursive tree traversal.

Real-World Context

"Find maximum depth of binary tree."

Breaking It Down

  • If root is null, return 0
  • Return 1 + max(left depth, right depth)
Python 3
def maxDepth(root):
    if not root:
        return 0
    return 1 + max(maxDepth(root.left), maxDepth(root.right))

Line-by-Line Walkthrough

The depth is 1 plus the maximum of subtree depths.

Complexity Analysis

  • Time: O(N)
  • Space: O(H)
  • Tree traversal

Common Mistakes

  • Iterative approach
    Recursive is simpler

Try It Yourself

Minimum Depth of Binary Tree - Find minimum depth

Same Tree

What You'll Learn

Recursive tree comparison.

Real-World Context

"Check if two trees are identical."

Breaking It Down

  • If both null, true
  • If one null, false
  • If values differ, false
  • Recurse on left and right
Python 3
def isSameTree(p, q):
    if not p and not q:
        return True
    if not p or not q:
        return False
    if p.val != q.val:
        return False
    return isSameTree(p.left, q.left) and isSameTree(p.right, q.right)

Line-by-Line Walkthrough

We check structure and values recursively.

Complexity Analysis

  • Time: O(N)
  • Space: O(H)
  • Tree traversal

Common Mistakes

  • Not checking null cases
    Handle null nodes properly

Try It Yourself

Symmetric Tree - Check if mirror

Invert/Flip Binary Tree

What You'll Learn

Recursive tree inversion.

Real-World Context

"Invert a binary tree."

Breaking It Down

  • If root null, return
  • Swap left and right
  • Recurse on left and right
Python 3
def invertTree(root):
    if not root:
        return None
    root.left, root.right = root.right, root.left
    invertTree(root.left)
    invertTree(root.right)
    return root

Line-by-Line Walkthrough

Swap subtrees and recurse.

Complexity Analysis

  • Time: O(N)
  • Space: O(H)
  • Tree traversal

Common Mistakes

  • Not recursing
    Invert subtrees too

Try It Yourself

Same Tree - Check if inverted matches original

Binary Tree Maximum Path Sum

What You'll Learn

Recursive path sum calculation.

Real-World Context

"Find maximum path sum in binary tree."

Breaking It Down

  • Use helper to return max path from node
  • Update global max with path through node
  • Return max single path
Python 3
def maxPathSum(root):
    max_sum = float('-inf')
    def helper(node):
        nonlocal max_sum
        if not node:
            return 0
        left = max(helper(node.left), 0)
        right = max(helper(node.right), 0)
        current = node.val + left + right
        max_sum = max(max_sum, current)
        return node.val + max(left, right)
    helper(root)
    return max_sum

Line-by-Line Walkthrough

For each node, consider path through it, update global max.

Complexity Analysis

  • Time: O(N)
  • Space: O(H)
  • Tree traversal

Common Mistakes

  • Not considering negative values
    Take max with 0 for paths

Try It Yourself

Path Sum - Check if path sums to target

Binary Tree Level Order Traversal

What You'll Learn

BFS with queue.

Real-World Context

"Traverse tree level by level."

Breaking It Down

  • Use queue
  • While queue not empty
  • Process level, add children
  • Collect levels
Python 3
def levelOrder(root):
    if not root:
        return []
    from collections import deque
    queue = deque([root])
    result = []
    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)
        result.append(level)
    return result

Line-by-Line Walkthrough

BFS processes nodes level by level.

Complexity Analysis

  • Time: O(N)
  • Space: O(W)
  • Queue usage

Common Mistakes

  • DFS approach
    Use BFS for levels

Try It Yourself

Zigzag Level Order - Alternate directions

Serialize and Deserialize Binary Tree

What You'll Learn

Preorder traversal with null markers.

Real-World Context

"Serialize and deserialize binary tree."

Breaking It Down

  • Serialize: preorder with null markers
  • Deserialize: reconstruct from list
Python 3
def serialize(root):
    def helper(node):
        if not node:
            return ['null']
        return [str(node.val)] + helper(node.left) + helper(node.right)
    return ','.join(helper(root))

def deserialize(data):
    vals = data.split(',')
    i = 0
    def helper():
        nonlocal i
        if vals[i] == 'null':
            i += 1
            return None
        node = TreeNode(int(vals[i]))
        i += 1
        node.left = helper()
        node.right = helper()
        return node
    return helper()

Line-by-Line Walkthrough

Preorder traversal with null markers preserves structure.

Complexity Analysis

  • Time: O(N)
  • Space: O(N)
  • String and recursion

Common Mistakes

  • Level order
    Preorder preserves structure

Try It Yourself

Construct Binary Tree from Preorder and Inorder Traversal

Subtree of Another Tree

What You'll Learn

Recursive subtree check.

Real-World Context

"Check if one tree is subtree of another."

Breaking It Down

  • Check if trees are same
  • Or check left or right subtrees
Python 3
def isSubtree(root, subRoot):
    def isSame(p, q):
        if not p and not q:
            return True
        if not p or not q:
            return False
        return p.val == q.val and isSame(p.left, q.left) and isSame(p.right, q.right)
    if not root:
        return False
    if isSame(root, subRoot):
        return True
    return isSubtree(root.left, subRoot) or isSubtree(root.right, subRoot)

Line-by-Line Walkthrough

Check if subRoot matches any subtree of root.

Complexity Analysis

  • Time: O(N*M)
  • Space: O(H)
  • Traversal and comparison

Common Mistakes

  • Not checking all subtrees
    Recurse on left and right

Try It Yourself

Same Tree - Check if identical

Construct Binary Tree from Preorder and Inorder Traversal

What You'll Learn

Recursive construction using indices.

Real-World Context

"Build tree from preorder and inorder."

Breaking It Down

  • Use inorder to find root position
  • Recurse on left and right subtrees
Python 3
def buildTree(preorder, inorder):
    if not preorder or not inorder:
        return None
    root_val = preorder[0]
    root = TreeNode(root_val)
    mid = inorder.index(root_val)
    root.left = buildTree(preorder[1:mid+1], inorder[:mid])
    root.right = buildTree(preorder[mid+1:], inorder[mid+1:])
    return root

Line-by-Line Walkthrough

Preorder gives root, inorder gives left/right split.

Complexity Analysis

  • Time: O(N²)
  • Space: O(N)
  • Index search and slicing

Common Mistakes

  • Wrong slicing
    Careful with indices

Try It Yourself

Construct from Inorder and Postorder

Validate Binary Search Tree

What You'll Learn

Inorder traversal check.

Real-World Context

"Check if tree is valid BST."

Breaking It Down

  • Inorder traversal should be sorted
  • Check ascending order
Python 3
def isValidBST(root):
    def inorder(node, prev):
        if not node:
            return True
        if not inorder(node.left, prev):
            return False
        if prev[0] is not None and node.val <= prev[0]:
            return False
        prev[0] = node.val
        return inorder(node.right, prev)
    return inorder(root, [None])

Line-by-Line Walkthrough

Each node must be within bounds from ancestors.

Complexity Analysis

  • Time: O(N)
  • Space: O(H)
  • Tree traversal

Common Mistakes

  • Not checking bounds
    Pass min/max to recursion

Try It Yourself

Kth Smallest Element in a BST

Kth Smallest Element in a BST

What You'll Learn

Inorder traversal for sorted order.

Real-World Context

"Find kth smallest element in BST."

Breaking It Down

  • Inorder traversal
  • Count until kth
Python 3
def kthSmallest(root, k):
    count = 0
    result = None
    def inorder(node):
        nonlocal count, result
        if not node or result is not None:
            return
        inorder(node.left)
        count += 1
        if count == k:
            result = node.val
            return
        inorder(node.right)
    inorder(root)
    return result

Line-by-Line Walkthrough

Inorder gives sorted order, count to k.

Complexity Analysis

  • Time: O(N)
  • Space: O(H)
  • Traversal

Common Mistakes

  • Not using BST property
    Inorder gives order

Try It Yourself

Validate Binary Search Tree

Lowest Common Ancestor of BST

What You'll Learn

BST property for LCA.

Real-World Context

"Find LCA in BST."

Breaking It Down

  • If both < root, go left
  • If both > root, go right
  • Else root is LCA
Python 3
def lowestCommonAncestor(root, p, q):
    if p.val < root.val and q.val < root.val:
        return lowestCommonAncestor(root.left, p, q)
    elif p.val > root.val and q.val > root.val:
        return lowestCommonAncestor(root.right, p, q)
    else:
        return root

Line-by-Line Walkthrough

LCA is where paths diverge.

Complexity Analysis

  • Time: O(H)
  • Space: O(H)
  • Tree height

Common Mistakes

  • Iterative approach
    Recursive is simple

Try It Yourself

Lowest Common Ancestor of Binary Tree - Without BST property

Implement Trie (Prefix Tree)

What You'll Learn

Trie data structure.

Real-World Context

"Implement trie with insert, search, startsWith."

Breaking It Down

  • Node with children map and isEnd
  • Insert: traverse/create nodes
  • Search: traverse and check isEnd
  • StartsWith: traverse
Python 3
class Trie:
    def __init__(self):
        self.children = {}
        self.isEnd = False
    def insert(self, word):
        node = self
        for c in word:
            if c not in node.children:
                node.children[c] = Trie()
            node = node.children[c]
        node.isEnd = True
    def search(self, word):
        node = self
        for c in word:
            if c not in node.children:
                return False
            node = node.children[c]
        return node.isEnd
    def startsWith(self, prefix):
        node = self
        for c in prefix:
            if c not in node.children:
                return False
            node = node.children[c]
        return True

Line-by-Line Walkthrough

Trie stores prefixes efficiently.

Complexity Analysis

  • Time: O(L)
  • Space: O(N)
  • Length of word

Common Mistakes

  • Using hash set
    Trie for prefixes

Try It Yourself

Add and Search Word - With wildcards

Add and Search Word - Data structure design

What You'll Learn

Trie with wildcards.

Real-World Context

"Implement word dictionary with wildcards."

Breaking It Down

  • Use trie
  • For search, handle '.' as any char
  • DFS for wildcards
Python 3
class WordDictionary:
    def __init__(self):
        self.trie = {}
    def addWord(self, word):
        node = self.trie
        for c in word:
            if c not in node:
                node[c] = {}
            node = node[c]
        node['#'] = True
    def search(self, word):
        def dfs(node, i):
            if i == len(word):
                return '#' in node
            c = word[i]
            if c == '.':
                for child in node:
                    if child != '#' and dfs(node[child], i+1):
                        return True
                return False
            else:
                if c not in node:
                    return False
                return dfs(node[c], i+1)
        return dfs(self.trie, 0)

Line-by-Line Walkthrough

DFS handles the wildcard by trying all possibilities.

Complexity Analysis

  • Time: O(26^L)
  • Space: O(N)
  • Wildcard branching

Common Mistakes

  • No DFS for '.'
    Use DFS to explore all

Try It Yourself

Implement Trie - Without wildcards

Word Search II

What You'll Learn

Trie + DFS for word search.

Real-World Context

"Find all words in board."

Breaking It Down

  • Build trie with words
  • DFS from each cell
  • Mark visited, check trie
Python 3
def findWords(board, words):
    trie = {}
    for word in words:
        node = trie
        for c in word:
            if c not in node:
                node[c] = {}
            node = node[c]
        node['#'] = word
    m, n = len(board), len(board[0])
    result = set()
    def dfs(i, j, node):
        if '#' in node:
            result.add(node['#'])
        for di, dj in [(-1,0),(1,0),(0,-1),(0,1)]:
            ni, nj = i + di, j + dj
            if 0 <= ni < m and 0 <= nj < n and board[ni][nj] in node:
                temp = board[ni][nj]
                board[ni][nj] = '#'
                dfs(ni, nj, node[temp])
                board[ni][nj] = temp
    for i in range(m):
        for j in range(n):
            if board[i][j] in trie:
                temp = board[i][j]
                board[i][j] = '#'
                dfs(i, j, trie[temp])
                board[i][j] = temp
    return list(result)

Line-by-Line Walkthrough

Trie prunes invalid paths.

Complexity Analysis

  • Time: O(M*N*4^L)
  • Space: O(N)
  • Board traversal

Common Mistakes

  • No trie
    Use trie to optimize

Try It Yourself

Word Search - Single word

Heap Problems

Priority queues and heap operations.

Top K Frequent Elements

What You'll Learn

Heap for top k elements.

Real-World Context

"Find k most frequent elements."

Breaking It Down

  • Count frequencies
  • Use heap to keep top k
  • Return top k
Python 3
def topKFrequent(nums, k):
    from collections import Counter
    import heapq
    count = Counter(nums)
    heap = []
    for num, freq in count.items():
        heapq.heappush(heap, (freq, num))
        if len(heap) > k:
            heapq.heappop(heap)
    return [num for freq, num in heap]

Line-by-Line Walkthrough

Min-heap keeps smallest of top k.

Complexity Analysis

  • Time: O(N log K)
  • Space: O(N)
  • Heap operations

Common Mistakes

  • Sorting
    Use heap for efficiency

Try It Yourself

Kth Largest Element in Array

Find Median from Data Stream

What You'll Learn

Two heaps for median.

Real-World Context

"Find median in running stream."

Breaking It Down

  • Max-heap for lower half
  • Min-heap for upper half
  • Balance heaps
  • Median from heaps
Python 3
class MedianFinder:
    def __init__(self):
        self.small = []  # max-heap
        self.large = []  # min-heap
    def addNum(self, num):
        import heapq
        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

Line-by-Line Walkthrough

Max-heap for lower, min-heap for upper, keep balanced.

Complexity Analysis

  • Time: O(log N)
  • Space: O(N)
  • Heap operations

Common Mistakes

  • Single heap
    Two heaps for balance

Try It Yourself

Sliding Window Median