23  All Problems: Pseudocode Reference

Use this page as a compact review sheet. Each section states the problem, the central idea, and language-neutral pseudocode. Use the table of contents to jump directly to a problem; follow the problem title link for the complete explanation, tests, complexity analysis, and Java, Python, and Rust implementations.

23.1 Two Sum

Full chapter

Problem. Given an integer array nums and an integer target, return the indices of two elements whose values add up to target.

Idea. Scan once while mapping each value already seen to its index. Before storing the current value, look for its complement so the two returned indices must be distinct.

indexByValue = empty map
for i from 0 through nums.length - 1:
    needed = target - nums[i]
    if needed is in indexByValue:
        return [indexByValue[needed], i]
    indexByValue[nums[i]] = i
throw no-solution exception

23.2 Merge Intervals

Full chapter

Problem. Given a collection of intervals, combine every set of overlapping intervals and return the resulting non-overlapping intervals.

Idea. Sort by start, then maintain one open merged interval. Extend it when the next interval overlaps; otherwise flush it and begin a new one.

if intervals is empty: return empty list
sorted = copy intervals and sort by start
merged = empty list
current = sorted[0]
for each next interval after the first:
    if next.start < current.end:       // half-open overlap
        current = [min(current.start, next.start),
                   max(current.end, next.end)]
    else:
        add current to merged
        current = next
add current to merged
return merged

23.3 Longest Substring Without Repeating Characters

Full chapter

Problem. Given a string, return the length of its longest contiguous substring with no repeated characters.

Idea. Keep a duplicate-free sliding window. A last-seen map lets the left edge jump just beyond a repeated character without ever moving backward.

lastSeen = empty map
start = 0
best = 0
for end from 0 through s.length - 1:
    c = s[end]
    if c has a previous index:
        start = max(start, lastSeen[c] + 1)
    lastSeen[c] = end
    best = max(best, end - start + 1)
return best

23.4 Valid Parentheses

Full chapter

Problem. Given a string of brackets, return whether every opener is closed by the same type in the correct nested order.

Idea. Push the closer expected by each opener. Every actual closer must match the stack top, and no expected closers may remain after the scan.

expectedClosings = empty stack
for each character c in s:
    if c is an opener:
        push its matching closer
    else if stack is empty or pop() is not c:
        return false
return stack is empty

23.6 Three Sum

Full chapter

Problem. Given an integer array, return all unique triplets of values whose sum is zero.

Idea. Sort the values, fix one value, and sweep the remaining range with two pointers. Sorted order determines which pointer to move, while skipping equal values prevents duplicates.

sort nums
answer = empty list
for i from 0 through nums.length - 3:
    if i > 0 and nums[i] == nums[i - 1]: continue
    left = i + 1
    right = nums.length - 1
    while left < right:
        sum = nums[i] + nums[left] + nums[right]
        if sum < 0: left++
        else if sum > 0: right--
        else:
            add [nums[i], nums[left], nums[right]]
            move both pointers
            skip repeated left and right values
return answer

23.7 Group Anagrams

Full chapter

Problem. Partition strings so two strings share a group exactly when they contain the same characters with the same frequencies.

Idea. Convert each word to a canonical key by sorting its characters. Anagrams have identical keys and therefore land in the same map bucket.

groupByKey = empty map from string to list of strings
for each word:
    key = word's characters sorted and joined
    if key has no group: create an empty group
    append the original word to that group
return all map groups

23.8 Top K Frequent Words

Full chapter

Problem. Return the k most frequent distinct words, ordered by decreasing frequency and then lexicographically for ties.

Idea. Count each word, sort the distinct words with the contract’s two-part comparator, and take the first k.

frequency = empty map
for each word: frequency[word]++
rankedWords = list of distinct map keys
sort rankedWords by:
    higher frequency first
    then lexicographically smaller word first
return first k ranked words

23.9 Maximum Subarray

Full chapter

Problem. Given an integer array, return the greatest sum among all nonempty contiguous subarrays.

Idea. At each value, the best subarray ending there either starts fresh or extends the previous one. Track that local optimum and the best seen anywhere.

endingHere = nums[0]
best = nums[0]
for each x after nums[0]:
    endingHere = max(x, endingHere + x)
    best = max(best, endingHere)
return best

23.10 Product of Array Except Self

Full chapter

Problem. For each array index, return the product of every other element without using division.

Idea. Store each index’s left-side product in the output, then multiply it by a running right-side product during a reverse pass.

answer = new array of nums.length
prefix = 1
for i from left to right:
    answer[i] = prefix
    prefix = prefix * nums[i]
suffix = 1
for i from right to left:
    answer[i] = answer[i] * suffix
    suffix = suffix * nums[i]
return answer

23.12 Reverse a Singly Linked List

Full chapter

Problem. Given the head of a singly linked list, reverse the list and return its new head.

Idea. Walk the list once, saving the untouched suffix before redirecting each node’s link toward the already reversed prefix.

previous = null
current = head
while current is not null:
    next = current.next
    current.next = previous
    previous = current
    current = next
return previous

23.13 Detect a Linked-List Cycle

Full chapter

Problem. Given a linked structure, return whether repeatedly following next eventually revisits a node.

Idea. Advance one pointer one edge and another two edges. In a finite acyclic list the fast pointer exits; inside a cycle the pointers must eventually meet.

slow = head
fast = head
while fast is not null and fast.next is not null:
    slow = slow.next
    fast = fast.next.next
    if slow and fast are the same node: return true
return false

23.14 Merge Two Sorted Linked Lists

Full chapter

Problem. Merge all nodes from two sorted singly linked lists into one sorted list.

Idea. A sentinel removes the special case for the output head. Repeatedly attach the smaller front node, then attach the one remaining suffix.

sentinel = temporary node
tail = sentinel
while a and b are both non-null:
    if a.value <= b.value:
        tail.next = a
        a = a.next
    else:
        tail.next = b
        b = b.next
    tail = tail.next
tail.next = whichever of a or b remains
return sentinel.next

23.15 Binary-Tree Level-Order Traversal

Full chapter

Problem. Return a binary tree’s values grouped by depth from the root downward.

Idea. Breadth-first search naturally visits by depth. Snapshot the queue size before processing a level so children wait for the next output group.

answer = empty list
if root is null: return answer
queue = queue containing root
while queue is not empty:
    levelSize = queue.size
    level = empty list
    repeat levelSize times:
        node = dequeue
        append node.value to level
        enqueue node.left and node.right if present
    append level to answer
return answer

23.16 Validate a Binary Search Tree

Full chapter

Problem. Return whether every node in a binary tree satisfies the ordering constraints of a binary search tree.

Idea. Local parent-child comparisons are insufficient. Carry the strict lower and upper bounds imposed by every ancestor down the recursion.

isValidBst(root):
    return valid(root, negative infinity, positive infinity)
valid(node, low, high):
    if node is null: return true
    if node.value <= low or node.value >= high: return false
    return valid(node.left, low, node.value)
       and valid(node.right, node.value, high)

23.17 Lowest Common Ancestor of a Binary Tree

Full chapter

Problem. Return the deepest binary-tree node whose subtree contains both target nodes.

Idea. Recursively report a target or ancestor found below. If the left and right subtrees both report a result, the current node is their first meeting point.

lca(node, p, q):
    if node is null or node is p or node is q: return node
    left = lca(node.left, p, q)
    right = lca(node.right, p, q)
    if left and right are both non-null: return node
    if left is non-null: return left
    return right

23.18 Number of Islands

Full chapter

Problem. Count the separate four-directionally connected regions of land in a two-dimensional grid.

Idea. Each unvisited land cell begins one new island. Flood-fill from it and mark the entire connected region so no part of that island is counted again.

islands = 0
for every row and column:
    if cell is land:
        islands++
        visitIsland(row, column)
return islands
visitIsland(row, column):
    if outside grid or cell is not land: return
    mark cell as water
    visit down, up, right, and left neighbors

23.19 Course Schedule

Full chapter

Problem. Return whether every course can be completed while respecting all prerequisite pairs.

Idea. Use Kahn’s topological sort. Repeatedly complete courses with zero remaining prerequisites and unlock their dependents; a cycle exists if some courses never become ready.

create an outgoing adjacency list for every course
prerequisitesLeft = zero-filled array
for each [course, prerequisite]:
    add course to prerequisite's outgoing list
    prerequisitesLeft[course]++
queue every course with zero prerequisitesLeft
completed = 0
while queue is not empty:
    prerequisite = dequeue
    completed++
    for each course unlocked by prerequisite:
        prerequisitesLeft[course]--
        if it becomes zero: enqueue course
return completed == numCourses

23.20 Coin Change

Full chapter

Problem. Given coin denominations and an amount, return the minimum number of coins needed for an exact total, or an absence result.

Idea. Bottom-up dynamic programming computes the optimum for every smaller amount first. For each total, try every coin as the final choice and retain the best reachable predecessor.

impossible = amount + 1
fewest = array indexed 0 through amount
fewest[0] = 0
for current from 1 through amount:
    fewest[current] = impossible
    for each coin:
        if coin <= current:
            fewest[current] = min(
                fewest[current],
                fewest[current - coin] + 1)
return not found if fewest[amount] is impossible
otherwise return fewest[amount]