Bird
Raised Fist0
Interview PrepbacktrackingmediumAmazonMicrosoftGoogleBloomberg

Permutations

Choose your preparation mode4 modes available

Start learning this pattern below

Jump into concepts and practice - no test required

or
Recommended
Test this pattern10 questions across easy, medium, and hard to know if this pattern is strong
Steps
setup

Initialize result and counters

The algorithm starts by copying the input array [1,2,3] into the results list and initializing the counters array c with zeros for each element.

💡 This setup is crucial because the counters track how many times each element has been swapped, enabling the algorithm to generate permutations without recursion.
Line:res = [nums[:]] c = [0] * len(nums) i = 0
💡 The initial permutation is always included, and counters start at zero to track swaps per element.
📊
Permutations - Watch the Algorithm Execute, Step by Step
Watching each swap and counter update reveals how Heap's algorithm efficiently produces permutations without recursion, making the process clear and intuitive.
Step 1/20
·Active fillAnswer cell
enteringnums=[1,2,3]
Call Path (current → root)
permute([1,2,3])
Found 1: [1,2,3]
choosingnums=[1,2,3]
Call Path (current → root)
permute([1,2,3])
Tried
no swap at i=0
Remaining
reset c[0], increment i
Found 1: [1,2,3]
backtrackingnums=[1,2,3]
Call Path (current → root)
permute([1,2,3])
Tried
reset c[0], increment i
Remaining
check c[1] < 1
Found 1: [1,2,3]
choosingnums=[1,2,3]
Call Path (current → root)
permute([1,2,3])
Tried
swap at i=1
Found 1: [1,2,3]
choosingnums=[2,1,3]
Call Path (current → root)
permute([1,2,3])
Tried
swap nums[0] and nums[1]
Found 1: [1,2,3]
backtrackingnums=[2,1,3]
Call Path (current → root)
permute([1,2,3])
Tried
added [2,1,3]
Found 2: [1,2,3] [2,1,3]
backtrackingnums=[2,1,3]
Call Path (current → root)
permute([1,2,3])
Tried
increment c[1], reset i
Remaining
check c[0] < 0
Found 2: [1,2,3] [2,1,3]
choosingnums=[2,1,3]
Call Path (current → root)
permute([1,2,3])
Tried
no swap at i=0
Remaining
reset c[0], increment i
Found 2: [1,2,3] [2,1,3]
backtrackingnums=[2,1,3]
Call Path (current → root)
permute([1,2,3])
Tried
reset c[0], increment i
Remaining
check c[1] < 1
Found 2: [1,2,3] [2,1,3]
choosingnums=[2,1,3]
Call Path (current → root)
permute([1,2,3])
Tried
no swap at i=1
Remaining
reset c[1], increment i
Found 2: [1,2,3] [2,1,3]
backtrackingnums=[2,1,3]
Call Path (current → root)
permute([1,2,3])
Tried
reset c[1], increment i
Remaining
check c[2] < 2
Found 2: [1,2,3] [2,1,3]
choosingnums=[2,1,3]
Call Path (current → root)
permute([1,2,3])
Tried
swap at i=2
Found 2: [1,2,3] [2,1,3]
choosingnums=[3,1,2]
Call Path (current → root)
permute([1,2,3])
Tried
swap nums[0] and nums[2]
Found 2: [1,2,3] [2,1,3]
backtrackingnums=[3,1,2]
Call Path (current → root)
permute([1,2,3])
Tried
added [3,1,2]
Found 3: [2,1,3] [3,1,2]
backtrackingnums=[3,1,2]
Call Path (current → root)
permute([1,2,3])
Tried
increment c[2], reset i
Remaining
check c[0] < 0
Found 3: [2,1,3] [3,1,2]
choosingnums=[3,1,2]
Call Path (current → root)
permute([1,2,3])
Tried
no swap at i=0
Remaining
reset c[0], increment i
Found 3: [2,1,3] [3,1,2]
backtrackingnums=[3,1,2]
Call Path (current → root)
permute([1,2,3])
Tried
reset c[0], increment i
Remaining
check c[1] < 1
Found 3: [2,1,3] [3,1,2]
choosingnums=[3,1,2]
Call Path (current → root)
permute([1,2,3])
Tried
swap at i=1
Found 3: [2,1,3] [3,1,2]
choosingnums=[1,3,2]
Call Path (current → root)
permute([1,2,3])
Tried
swap nums[0] and nums[1]
Found 3: [2,1,3] [3,1,2]
backtrackingnums=[1,3,2]
Call Path (current → root)
permute([1,2,3])
Tried
added [1,3,2]
Found 4: [3,1,2] [1,3,2]

Key Takeaways

Heap's algorithm uses counters and parity-based swaps to generate all permutations efficiently without recursion.

This insight is hard to see from code alone because the counters' role and parity-based swapping logic are subtle and intertwined.

The counters array controls how many times each element is swapped, ensuring no duplicate permutations and systematic coverage.

Understanding counters' gating role clarifies why the algorithm resets and increments indices, which is not obvious from reading code.

Swapping elements based on whether the current index is even or odd ensures all permutations are generated by minimal swaps.

The parity-based swap decision is a key branching point that determines the swap pattern, which is clearer when visualized step-by-step.

Practice

(1/5)
1. Given the following code for next permutation, what is the content of the array nums after calling next_permutation([1, 3, 2])?
easy
A. [1, 2, 3]
B. [2, 3, 1]
C. [2, 1, 3]
D. [1, 3, 2]

Solution

  1. Step 1: Trace the pivot search

    Start with i = 1 (index of 3). Since nums[1]=3 >= nums[2]=2, decrement i to 0. nums[0]=1 < nums[1]=3, so pivot i=0.
  2. Step 2: Find j to swap with pivot

    Start j=2 (value 2). nums[2]=2 > nums[0]=1, so j=2. Swap nums[0] and nums[2]: array becomes [2,3,1]. Then reverse suffix from i+1=1 to end: reverse [3,1] -> [1,3]. Final array: [2,1,3].
  3. Final Answer:

    Option C -> Option C
  4. Quick Check:

    Array changes from [1,3,2] to [2,1,3] after next permutation [OK]
Hint: Pivot found at first decreasing from right, swap and reverse suffix [OK]
Common Mistakes:
  • Swapping with wrong element
  • Not reversing suffix after swap
2. What is the time complexity of the optimal backtracking solution for the Expression Add Operators problem, where n is the length of the input string? Assume the solution tries all operator insertions and substring splits with pruning and efficient string building.
medium
A. O(4^n) because at each position there are up to 4 choices (3 operators + no operator split)
B. O(n * 3^n) because each digit can be combined with 3 operators and substring splits
C. O(2^n) since only two operators are considered at each step
D. O(n^3) due to substring parsing and operator insertions

Solution

  1. Step 1: Identify branching factor per digit

    At each digit, we can insert one of 3 operators (+, -, *) or choose to extend the current operand (no operator), but the main branching factor is 3 operators per split, and substring splits add a factor of n.
  2. Step 2: Calculate total complexity

    The total complexity is roughly O(n * 3^n) because for each of the n positions, there are up to 3 operator choices, and substring splits add an n factor.
  3. Final Answer:

    Option B -> Option B
  4. Quick Check:

    Exponential branching with 3 operators and substring splits [OK]
Hint: 3 operators per digit and substring splits lead to O(n * 3^n) complexity [OK]
Common Mistakes:
  • Confusing substring parsing cost as cubic
  • Assuming only 2 operators reduce complexity
  • Miscounting branching factor as 4
3. Consider this modified code snippet for generating parentheses. Which line contains a subtle bug that can cause invalid sequences to be generated?
medium
A. Line with 'if close_count <= open_count:' condition
B. Line with 's.pop()' after backtrack calls
C. Line with 'if open_count < n:' condition
D. Line with 'if len(s) == 2 * n:' base case

Solution

  1. Step 1: Examine close_count condition

    The condition 'close_count <= open_count' allows adding ')' even when close_count equals open_count, which can produce invalid sequences.
  2. Step 2: Correct condition

    The correct condition is 'close_count < open_count' to ensure ')' is added only when there are unmatched '('.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    Changing to '<' fixes invalid sequence generation [OK]
Hint: Close count must be strictly less than open count [OK]
Common Mistakes:
  • Using <= instead of < for close_count condition
  • Forgetting to pop after recursion
4. What is the time complexity of the optimal backtracking algorithm that generates unique permutations of an array with duplicates by sorting and skipping duplicates during recursion?
medium
A. O(n! * log n) due to sorting and binary search for duplicates
B. O(n! * n^2) because each permutation requires copying the array and checking duplicates
C. O(n^n) because the recursion explores all possible swaps without pruning
D. O(n! * n) because pruning duplicates early reduces redundant branches but each permutation still requires O(n) copying

Solution

  1. Step 1: Identify complexity of outer and inner loops

    Backtracking explores permutations, which is O(n!). Each permutation requires copying O(n) elements to result.
  2. Step 2: Check if pruning duplicates reduces complexity

    Pruning duplicates early avoids redundant branches, so complexity is O(n! * n), not worse. Sorting is O(n log n) but done once.
  3. Final Answer:

    Option D -> Option D
  4. Quick Check:

    Pruning reduces branches but copying each permutation costs O(n) [OK]
Hint: Pruning duplicates reduces branches but copying costs O(n) per permutation [OK]
Common Mistakes:
  • Confusing pruning effect or ignoring copy cost
5. Examine the following snippet from an optimized Sudoku solver. Which line contains a subtle bug that can cause incorrect solutions or failure to backtrack properly?
def backtrack():
    if not empties:
        return True
    empties.sort(key=lambda x: len(get_candidates(x[0], x[1])))
    r, c = empties[0]
    for ch in get_candidates(r, c):
        board[r][c] = ch
        rows[r].add(ch)
        cols[c].add(ch)
        boxes[(r//3)*3 + c//3].add(ch)
        empties.pop(0)
        if backtrack():
            return True
        # Undo changes
        board[r][c] = '.'
        rows[r].remove(ch)
        cols[c].remove(ch)
        boxes[(r//3)*3 + c//3].remove(ch)
        empties.insert(0, (r, c))
    return False
medium
A. Line that pops the first empty cell from empties
B. Line that sorts empties by candidate count
C. Line that adds ch to rows[r]
D. Line that resets board[r][c] to '.' after backtracking

Solution

  1. Step 1: Identify mutation of empties list

    The line popping empties[0] removes the cell but is never restored on backtrack failure.
  2. Step 2: Consequence of missing restoration

    Without re-adding the cell to empties after failed recursion, future calls miss this cell, causing incorrect state and solutions.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    Backtracking requires undoing all changes including empties list [OK]
Hint: Backtracking must undo all state changes, including empties list [OK]
Common Mistakes:
  • Forgetting to restore empties after pop
  • Assuming board reset is sufficient