Bird
Raised Fist0
Interview PrepbacktrackingmediumAmazonGoogleMicrosoft

Permutations II (With Duplicates)

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
🎯
Permutations II (With Duplicates)
mediumBACKTRACKINGAmazonGoogleMicrosoft

Imagine you have a collection of colored beads, some colors repeating, and you want to find all unique ways to arrange them on a string without repeating the same pattern twice.

💡 This problem is about generating all unique permutations of a list that may contain duplicates. Beginners often struggle because naive permutation generation produces duplicates, and handling duplicates efficiently requires careful pruning.
📋
Problem Statement

Given a collection of numbers that might contain duplicates, return all possible unique permutations in any order. Input: An array of integers nums. Output: A list of lists, where each list is a unique permutation of nums.

1 ≤ nums.length ≤ 8-10 ≤ nums[i] ≤ 10
💡
Example
Input"[1,1,2]"
Output[[1,1,2],[1,2,1],[2,1,1]]

The input has duplicates (two 1s). The output lists all unique permutations without repetition.

  • All elements are the same → only one unique permutation
  • Array with no duplicates → all permutations are unique
  • Empty array → returns an empty list or list with empty permutation
  • Array with negative and positive duplicates → handle sign correctly
⚠️
Common Mistakes
Not sorting input before skipping duplicates

Duplicate permutations are generated because duplicates are not adjacent and skipping logic fails

Always sort input before backtracking to group duplicates

Skipping duplicates incorrectly by checking only current and previous without used array

Valid permutations are skipped or duplicates remain

Use 'used' array or 'seen' set at recursion level to correctly skip duplicates

Modifying input array without backtracking swap

Input array remains changed, causing incorrect permutations

Always swap back after recursion to restore original state

Using set to remove duplicates after generating all permutations (brute force)

Solution is correct but inefficient and may time out on large inputs

Prune duplicates during recursion instead of filtering after generation

🧠
Brute Force (Generate All Permutations and Filter Duplicates)
💡 This approach helps understand the problem by first generating all permutations without worrying about duplicates, then removing duplicates afterward. It is simple but inefficient.

Intuition

Generate every possible permutation by swapping elements recursively, then use a set to filter out duplicates at the end.

Algorithm

  1. Recursively generate all permutations by swapping elements.
  2. At each recursion level, swap the current index with all possible indices.
  3. Once a permutation is complete, add it to a result list.
  4. After generating all permutations, convert the list to a set to remove duplicates.
💡 The main challenge is understanding how recursion generates permutations and why duplicates appear when input has repeated elements.
</>
Code
from typing import List

def permuteUnique(nums: List[int]) -> List[List[int]]:
    res = []
    def backtrack(start=0):
        if start == len(nums):
            res.append(nums[:])
            return
        for i in range(start, len(nums)):
            nums[start], nums[i] = nums[i], nums[start]
            backtrack(start + 1)
            nums[start], nums[i] = nums[i], nums[start]
    backtrack()
    # Remove duplicates by converting to set of tuples
    unique_res = list(map(list, set(tuple(lst) for lst in res)))
    return unique_res

# Driver code
if __name__ == '__main__':
    print(permuteUnique([1,1,2]))
Line Notes
def permuteUnique(nums: List[int]) -> List[List[int]]:Defines the function signature with type hints for clarity.
def backtrack(start=0):Starts recursion from index 0 to generate permutations.
if start == len(nums):Base case: when start reaches end, a permutation is complete.
nums[start], nums[i] = nums[i], nums[start]Backtrack by swapping back to restore original state.
backtrack(start + 1)Recurse to fix next element.
unique_res = list(map(list, set(tuple(lst) for lst in res)))Remove duplicates by converting lists to tuples and using a set.
import java.util.*;

public class Solution {
    public List<List<Integer>> permuteUnique(int[] nums) {
        List<List<Integer>> res = new ArrayList<>();
        backtrack(nums, 0, res);
        // Remove duplicates using a set of strings
        Set<String> set = new HashSet<>();
        for (List<Integer> perm : res) {
            StringBuilder sb = new StringBuilder();
            for (int num : perm) {
                sb.append(num).append(",");
            }
            set.add(sb.toString());
        }
        List<List<Integer>> uniqueRes = new ArrayList<>();
        for (String s : set) {
            String[] parts = s.split(",");
            List<Integer> list = new ArrayList<>();
            for (String part : parts) {
                if (!part.isEmpty()) {
                    list.add(Integer.parseInt(part));
                }
            }
            uniqueRes.add(list);
        }
        return uniqueRes;
    }

    private void backtrack(int[] nums, int start, List<List<Integer>> res) {
        if (start == nums.length) {
            List<Integer> permutation = new ArrayList<>();
            for (int num : nums) permutation.add(num);
            res.add(permutation);
            return;
        }
        for (int i = start; i < nums.length; i++) {
            swap(nums, start, i);
            backtrack(nums, start + 1, res);
            swap(nums, start, i);
        }
    }

    private void swap(int[] nums, int i, int j) {
        int temp = nums[i];
        nums[i] = nums[j];
        nums[j] = temp;
    }

    // Driver code
    public static void main(String[] args) {
        Solution sol = new Solution();
        List<List<Integer>> result = sol.permuteUnique(new int[]{1,1,2});
        System.out.println(result);
    }
}
Line Notes
public List<List<Integer>> permuteUnique(int[] nums)Function signature returning list of unique permutations.
backtrack(nums, 0, res);Start recursive permutation generation from index 0.
// Remove duplicates using a set of stringsConvert each permutation to a string and use a set to remove duplicates.
Set<String> set = new HashSet<>();Initialize a set to store unique permutation strings.
for (List<Integer> perm : res)Iterate over all generated permutations.
StringBuilder sb = new StringBuilder();Build a string representation of the permutation.
set.add(sb.toString());Add the string to the set to ensure uniqueness.
List<List<Integer>> uniqueRes = new ArrayList<>();Prepare list to hold unique permutations.
for (String s : set)Convert each unique string back to a list of integers.
private void backtrack(int[] nums, int start, List<List<Integer>> res)Recursive helper to generate permutations by swapping.
if (start == nums.length)Base case: permutation complete.
swap(nums, start, i);Backtrack by swapping back to original.
backtrack(nums, start + 1, res);Recurse to next position.
private void swap(int[] nums, int i, int j)Helper method to swap two elements in array.
// Driver codeMain method to test the solution.
System.out.println(result);Print the list of unique permutations.
#include <iostream>
#include <vector>
#include <set>
using namespace std;

class Solution {
public:
    vector<vector<int>> permuteUnique(vector<int>& nums) {
        vector<vector<int>> res;
        backtrack(nums, 0, res);
        set<vector<int>> unique_res(res.begin(), res.end());
        return vector<vector<int>>(unique_res.begin(), unique_res.end());
    }

    void backtrack(vector<int>& nums, int start, vector<vector<int>>& res) {
        if (start == nums.size()) {
            res.push_back(nums);
            return;
        }
        for (int i = start; i < nums.size(); i++) {
            swap(nums[start], nums[i]);
            backtrack(nums, start + 1, res);
            swap(nums[start], nums[i]);
        }
    }
};

// Driver code
int main() {
    Solution sol;
    vector<int> nums = {1,1,2};
    vector<vector<int>> result = sol.permuteUnique(nums);
    for (auto &perm : result) {
        for (int num : perm) cout << num << ' ';
        cout << '\n';
    }
    return 0;
}
Line Notes
vector<vector<int>> permuteUnique(vector<int>& nums)Function returns all unique permutations.
backtrack(nums, 0, res);Start recursion from index 0.
if (start == nums.size())Base case: permutation complete.
swap(nums[start], nums[i]);Backtrack by swapping back.
backtrack(nums, start + 1, res);Recurse to next position.
set<vector<int>> unique_res(res.begin(), res.end());Remove duplicates by using a set.
return vector<vector<int>>(unique_res.begin(), unique_res.end());Return unique permutations as vector.
function permuteUnique(nums) {
    const res = [];
    function backtrack(start = 0) {
        if (start === nums.length) {
            res.push(nums.slice());
            return;
        }
        for (let i = start; i < nums.length; i++) {
            [nums[start], nums[i]] = [nums[i], nums[start]];
            backtrack(start + 1);
            [nums[start], nums[i]] = [nums[i], nums[start]];
        }
    }
    backtrack();
    // Remove duplicates
    const unique = new Set(res.map(arr => arr.join(',')));
    return Array.from(unique).map(str => str.split(',').map(Number));
}

// Driver code
console.log(permuteUnique([1,1,2]));
Line Notes
function permuteUnique(nums) {Defines the main function.
function backtrack(start = 0) {Recursive helper function starting at index 0.
if (start === nums.length) {Base case: permutation complete.
for (let i = start; i < nums.length; i++) {Loop to swap current index with all possible indices.
[nums[start], nums[i]] = [nums[i], nums[start]];Backtrack by swapping back.
backtrack(start + 1);Recurse to next position.
const unique = new Set(res.map(arr => arr.join(',')));Remove duplicates by converting arrays to strings and using a set.
Complexity
TimeO(n! * n) due to generating all permutations and copying arrays
SpaceO(n! * n) for storing all permutations before deduplication

We generate all permutations including duplicates, which is n! permutations. Each permutation is copied (O(n)) and stored. Deduplication uses extra space.

💡 For n=8, n! is 40,320, so this approach quickly becomes impractical due to time and memory.
Interview Verdict: TLE / Inefficient for large inputs

This approach is too slow for large inputs but is useful to understand the problem and recursion basics.

🧠
Backtracking with Sorting and Used Array to Skip Duplicates
💡 This approach improves efficiency by sorting the input and skipping duplicates during recursion, avoiding generating duplicate permutations.

Intuition

Sort the array so duplicates are adjacent. Use a boolean array to track used elements. Skip elements that are the same as previous and not used to avoid duplicate permutations.

Algorithm

  1. Sort the input array to group duplicates together.
  2. Use a boolean array 'used' to track which elements are included in the current permutation.
  3. At each recursion level, iterate over nums; skip if used or if current equals previous and previous not used.
  4. Add the current number to the permutation, mark used, recurse, then backtrack.
💡 The key insight is skipping duplicates only when the previous identical element is not used, which prunes duplicate branches.
</>
Code
from typing import List

def permuteUnique(nums: List[int]) -> List[List[int]]:
    nums.sort()
    res = []
    used = [False] * len(nums)
    def backtrack(path):
        if len(path) == len(nums):
            res.append(path[:])
            return
        for i in range(len(nums)):
            if used[i]:
                continue
            if i > 0 and nums[i] == nums[i-1] and not used[i-1]:
                continue
            used[i] = True
            path.append(nums[i])
            backtrack(path)
            path.pop()
            used[i] = False
    backtrack([])
    return res

# Driver code
if __name__ == '__main__':
    print(permuteUnique([1,1,2]))
Line Notes
nums.sort()Sort input to bring duplicates together for easy skipping.
used = [False] * len(nums)Track which elements are used in current permutation.
if used[i]: continueSkip elements already included in current path.
if i > 0 and nums[i] == nums[i-1] and not used[i-1]: continueSkip duplicates unless previous identical element is used.
used[i] = TrueMark current element as used before recursion.
path.append(nums[i])Add current element to permutation path.
backtrack(path)Recurse to build permutation further.
path.pop()Backtrack by removing last element.
used[i] = FalseMark current element as unused after backtracking.
import java.util.*;

public class Solution {
    public List<List<Integer>> permuteUnique(int[] nums) {
        List<List<Integer>> res = new ArrayList<>();
        Arrays.sort(nums);
        boolean[] used = new boolean[nums.length];
        backtrack(nums, used, new ArrayList<>(), res);
        return res;
    }

    private void backtrack(int[] nums, boolean[] used, List<Integer> path, List<List<Integer>> res) {
        if (path.size() == nums.length) {
            res.add(new ArrayList<>(path));
            return;
        }
        for (int i = 0; i < nums.length; i++) {
            if (used[i]) continue;
            if (i > 0 && nums[i] == nums[i - 1] && !used[i - 1]) continue;
            used[i] = true;
            path.add(nums[i]);
            backtrack(nums, used, path, res);
            path.remove(path.size() - 1);
            used[i] = false;
        }
    }

    // Driver code
    public static void main(String[] args) {
        Solution sol = new Solution();
        System.out.println(sol.permuteUnique(new int[]{1,1,2}));
    }
}
Line Notes
Arrays.sort(nums);Sort input array to group duplicates.
boolean[] used = new boolean[nums.length];Track usage of elements in current permutation.
if (used[i]) continue;Skip elements already used in current path.
if (i > 0 && nums[i] == nums[i - 1] && !used[i - 1]) continue;Skip duplicates unless previous identical element is used.
used[i] = true;Mark element as used before recursion.
path.add(nums[i]);Add element to current permutation path.
backtrack(nums, used, path, res);Recurse to build permutation.
path.remove(path.size() - 1);Backtrack by removing last element.
used[i] = false;Mark element as unused after backtracking.
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

class Solution {
public:
    vector<vector<int>> permuteUnique(vector<int>& nums) {
        vector<vector<int>> res;
        vector<int> path;
        vector<bool> used(nums.size(), false);
        sort(nums.begin(), nums.end());
        backtrack(nums, used, path, res);
        return res;
    }

    void backtrack(vector<int>& nums, vector<bool>& used, vector<int>& path, vector<vector<int>>& res) {
        if (path.size() == nums.size()) {
            res.push_back(path);
            return;
        }
        for (int i = 0; i < nums.size(); i++) {
            if (used[i]) continue;
            if (i > 0 && nums[i] == nums[i - 1] && !used[i - 1]) continue;
            used[i] = true;
            path.push_back(nums[i]);
            backtrack(nums, used, path, res);
            path.pop_back();
            used[i] = false;
        }
    }
};

// Driver code
int main() {
    Solution sol;
    vector<int> nums = {1,1,2};
    vector<vector<int>> result = sol.permuteUnique(nums);
    for (auto &perm : result) {
        for (int num : perm) cout << num << ' ';
        cout << '\n';
    }
    return 0;
}
Line Notes
sort(nums.begin(), nums.end());Sort input to group duplicates for skipping.
vector<bool> used(nums.size(), false);Track which elements are used in current permutation.
if (used[i]) continue;Skip elements already used in current path.
if (i > 0 && nums[i] == nums[i - 1] && !used[i - 1]) continue;Skip duplicates unless previous identical element is used.
used[i] = true;Mark element as used before recursion.
path.push_back(nums[i]);Add element to current permutation path.
backtrack(nums, used, path, res);Recurse to build permutation.
path.pop_back();Backtrack by removing last element.
used[i] = false;Mark element as unused after backtracking.
function permuteUnique(nums) {
    nums.sort((a,b) => a - b);
    const res = [];
    const used = new Array(nums.length).fill(false);
    function backtrack(path) {
        if (path.length === nums.length) {
            res.push(path.slice());
            return;
        }
        for (let i = 0; i < nums.length; i++) {
            if (used[i]) continue;
            if (i > 0 && nums[i] === nums[i - 1] && !used[i - 1]) continue;
            used[i] = true;
            path.push(nums[i]);
            backtrack(path);
            path.pop();
            used[i] = false;
        }
    }
    backtrack([]);
    return res;
}

// Driver code
console.log(permuteUnique([1,1,2]));
Line Notes
nums.sort((a,b) => a - b);Sort input array to group duplicates.
const used = new Array(nums.length).fill(false);Track usage of elements in current permutation.
if (used[i]) continue;Skip elements already used in current path.
if (i > 0 && nums[i] === nums[i - 1] && !used[i - 1]) continue;Skip duplicates unless previous identical element is used.
used[i] = true;Mark element as used before recursion.
path.push(nums[i]);Add element to current permutation path.
backtrack(path);Recurse to build permutation.
path.pop();Backtrack by removing last element.
used[i] = false;Mark element as unused after backtracking.
Complexity
TimeO(n! * n) due to pruning duplicates but still generating permutations
SpaceO(n) recursion stack + O(n! * n) for results

Sorting and skipping duplicates reduces redundant branches, but worst case remains factorial.

💡 For n=8, pruning helps but complexity remains high; still practical for interview constraints.
Interview Verdict: Accepted / Efficient for interview constraints

This approach balances clarity and efficiency, making it ideal for interviews.

🧠
Backtracking with In-Place Swapping and Deduplication Using Sorting and Skip Condition
💡 This approach uses in-place swapping like brute force but adds sorting and a skip condition to avoid generating duplicate permutations during recursion.

Intuition

Sort the array and at each recursion level, skip swapping with duplicate elements that have already been fixed at this level to avoid duplicate permutations.

Algorithm

  1. Sort the input array to group duplicates.
  2. At each recursion level, use a set to track which elements have been fixed at this position.
  3. Iterate over indices from current start to end; skip if element already fixed at this level.
  4. Swap current index with chosen index, recurse, then swap back to backtrack.
💡 The key is using a set at each recursion depth to avoid swapping duplicate elements multiple times, pruning duplicate permutations early.
</>
Code
from typing import List

def permuteUnique(nums: List[int]) -> List[List[int]]:
    nums.sort()
    res = []
    def backtrack(start=0):
        if start == len(nums):
            res.append(nums[:])
            return
        seen = set()
        for i in range(start, len(nums)):
            if nums[i] in seen:
                continue
            seen.add(nums[i])
            nums[start], nums[i] = nums[i], nums[start]
            backtrack(start + 1)
            nums[start], nums[i] = nums[i], nums[start]
    backtrack()
    return res

# Driver code
if __name__ == '__main__':
    print(permuteUnique([1,1,2]))
Line Notes
nums.sort()Sort input to group duplicates for easy skipping.
def backtrack(start=0):Recursive function with current index to fix.
if start == len(nums):Base case: permutation complete.
seen = set()Track elements fixed at this recursion level to avoid duplicates.
if nums[i] in seen: continueSkip duplicate elements at this level.
seen.add(nums[i])Mark element as fixed at this level.
nums[start], nums[i] = nums[i], nums[start]Backtrack by swapping back.
backtrack(start + 1)Recurse to next position.
import java.util.*;

public class Solution {
    public List<List<Integer>> permuteUnique(int[] nums) {
        List<List<Integer>> res = new ArrayList<>();
        Arrays.sort(nums);
        backtrack(nums, 0, res);
        return res;
    }

    private void backtrack(int[] nums, int start, List<List<Integer>> res) {
        if (start == nums.length) {
            List<Integer> permutation = new ArrayList<>();
            for (int num : nums) permutation.add(num);
            res.add(permutation);
            return;
        }
        Set<Integer> seen = new HashSet<>();
        for (int i = start; i < nums.length; i++) {
            if (seen.contains(nums[i])) continue;
            seen.add(nums[i]);
            swap(nums, start, i);
            backtrack(nums, start + 1, res);
            swap(nums, start, i);
        }
    }

    private void swap(int[] nums, int i, int j) {
        int temp = nums[i];
        nums[i] = nums[j];
        nums[j] = temp;
    }

    // Driver code
    public static void main(String[] args) {
        Solution sol = new Solution();
        System.out.println(sol.permuteUnique(new int[]{1,1,2}));
    }
}
Line Notes
Arrays.sort(nums);Sort input to group duplicates.
Set<Integer> seen = new HashSet<>();Track elements fixed at current recursion level.
if (seen.contains(nums[i])) continue;Skip duplicate elements at this level.
seen.add(nums[i]);Mark element as fixed at this level.
swap(nums, start, i);Backtrack by swapping back.
backtrack(nums, start + 1, res);Recurse to next position.
#include <iostream>
#include <vector>
#include <algorithm>
#include <unordered_set>
using namespace std;

class Solution {
public:
    vector<vector<int>> permuteUnique(vector<int>& nums) {
        vector<vector<int>> res;
        sort(nums.begin(), nums.end());
        backtrack(nums, 0, res);
        return res;
    }

    void backtrack(vector<int>& nums, int start, vector<vector<int>>& res) {
        if (start == nums.size()) {
            res.push_back(nums);
            return;
        }
        unordered_set<int> seen;
        for (int i = start; i < nums.size(); i++) {
            if (seen.count(nums[i])) continue;
            seen.insert(nums[i]);
            swap(nums[start], nums[i]);
            backtrack(nums, start + 1, res);
            swap(nums[start], nums[i]);
        }
    }
};

// Driver code
int main() {
    Solution sol;
    vector<int> nums = {1,1,2};
    vector<vector<int>> result = sol.permuteUnique(nums);
    for (auto &perm : result) {
        for (int num : perm) cout << num << ' ';
        cout << '\n';
    }
    return 0;
}
Line Notes
sort(nums.begin(), nums.end());Sort input to group duplicates.
unordered_set<int> seen;Track elements fixed at current recursion level.
if (seen.count(nums[i])) continue;Skip duplicate elements at this level.
seen.insert(nums[i]);Mark element as fixed at this level.
swap(nums[start], nums[i]);Backtrack by swapping back.
backtrack(nums, start + 1, res);Recurse to next position.
function permuteUnique(nums) {
    nums.sort((a,b) => a - b);
    const res = [];
    function backtrack(start = 0) {
        if (start === nums.length) {
            res.push(nums.slice());
            return;
        }
        const seen = new Set();
        for (let i = start; i < nums.length; i++) {
            if (seen.has(nums[i])) continue;
            seen.add(nums[i]);
            [nums[start], nums[i]] = [nums[i], nums[start]];
            backtrack(start + 1);
            [nums[start], nums[i]] = [nums[i], nums[start]];
        }
    }
    backtrack();
    return res;
}

// Driver code
console.log(permuteUnique([1,1,2]));
Line Notes
nums.sort((a,b) => a - b);Sort input array to group duplicates.
const seen = new Set();Track elements fixed at current recursion level.
if (seen.has(nums[i])) continue;Skip duplicate elements at this level.
seen.add(nums[i]);Mark element as fixed at this level.
[nums[start], nums[i]] = [nums[i], nums[start]];Backtrack by swapping back.
backtrack(start + 1);Recurse to next position.
Complexity
TimeO(n! * n) with pruning duplicates early
SpaceO(n) recursion stack + O(n! * n) for results

Using a set at each recursion level avoids duplicate swaps, pruning duplicate permutations early.

💡 This approach is efficient and uses in-place swaps, making it memory-friendly and fast for interview constraints.
Interview Verdict: Accepted / Optimal for memory and speed

This is a preferred approach in interviews for its clarity and efficiency.

📊
All Approaches - One-Glance Tradeoffs
💡 Approach 2 or 3 are best for interviews; approach 1 is only for understanding basics.
ApproachTimeSpaceStack RiskReconstructUse In Interview
1. Brute ForceO(n! * n)O(n! * n)Yes (deep recursion)YesMention only - never code
2. Backtracking with Used Array and SkipO(n! * n)O(n! * n)YesYesCode this for clarity and efficiency
3. In-Place Swapping with Skip SetO(n! * n)O(n! * n)YesYesCode this for optimal memory and speed
💼
Interview Strategy
💡 Use this guide to understand the problem deeply, practice coding all approaches, and prepare to explain tradeoffs clearly in interviews.

How to Present

Step 1: Clarify input size and presence of duplicates.Step 2: Present brute force approach to generate all permutations and remove duplicates.Step 3: Improve by sorting and skipping duplicates using a used array.Step 4: Present in-place swapping with skip set for optimal memory and speed.Step 5: Discuss time and space complexity and edge cases.

Time Allocation

Clarify: 2min → Approach: 5min → Code: 10min → Test & Discuss: 3min. Total ~20min

What the Interviewer Tests

Interviewer tests your understanding of backtracking, handling duplicates, pruning recursion, and coding clean, bug-free solutions.

Common Follow-ups

  • What if input size is large? → Use iterative or heuristic methods.
  • Can you generate permutations in lexicographical order? → Use next permutation algorithm.
💡 Follow-ups test your ability to optimize further or adapt the solution to related problems.
🔍
Pattern Recognition

When to Use

1) Need all permutations, 2) Input may have duplicates, 3) Output must be unique permutations, 4) Backtracking or recursion is natural approach

Signature Phrases

unique permutationsduplicates in inputavoid repeating same permutation

NOT This Pattern When

Problems asking for combinations without order or counting subsets without duplicates

Similar Problems

Permutations I - simpler version without duplicatesSubsets II - generating unique subsets with duplicatesCombination Sum II - unique combinations with duplicates

Practice

(1/5)
1. Given the following code snippet, what is the final output of letterCombinations('23')?
from collections import deque

def letterCombinations(digits):
    if not digits:
        return []
    digit_map = {'2': 'abc', '3': 'def', '4': 'ghi', '5': 'jkl',
                 '6': 'mno', '7': 'pqrs', '8': 'tuv', '9': 'wxyz'}
    queue = deque([''])
    for digit in digits:
        letters = digit_map[digit]
        for _ in range(len(queue)):
            prev = queue.popleft()
            for char in letters:
                queue.append(prev + char)
    return list(queue)

print(letterCombinations('23'))
easy
A. ['ad', 'bd', 'cd', 'ae', 'be', 'ce', 'af', 'bf', 'cf']
B. ['abc', 'def']
C. ['ad', 'ae', 'af', 'bd', 'be', 'bf', 'cd', 'ce', 'cf']
D. ['a', 'b', 'c', 'd', 'e', 'f']

Solution

  1. Step 1: Trace queue after processing digit '2'

    Initial queue: [''] Letters for '2': 'abc' After processing: ['a', 'b', 'c']
  2. Step 2: Trace queue after processing digit '3'

    Letters for '3': 'def' For each prefix in ['a', 'b', 'c'], append each letter: 'a' + 'd','e','f' -> 'ad','ae','af' 'b' + 'd','e','f' -> 'bd','be','bf' 'c' + 'd','e','f' -> 'cd','ce','cf' Final queue: ['ad','ae','af','bd','be','bf','cd','ce','cf']
  3. Final Answer:

    Option C -> Option C
  4. Quick Check:

    Output matches expected letter combinations for '23' [OK]
Hint: Queue expands combinations breadth-first per digit [OK]
Common Mistakes:
  • Mixing order of combinations
  • Confusing concatenation order
2. You are given a partially filled 9x9 grid where each row, column, and 3x3 sub-box must contain digits 1 through 9 without repetition. Which algorithmic approach guarantees finding a valid solution if one exists?
easy
A. Greedy algorithm that fills cells with the first valid digit found
B. Breadth-first search exploring all possible digit placements simultaneously
C. Dynamic programming by storing subproblems of partially filled grids
D. Backtracking with constraint propagation and heuristic ordering of empty cells

Solution

  1. Step 1: Understand problem constraints

    The problem requires filling cells respecting row, column, and box constraints, which is a classic constraint satisfaction problem.
  2. Step 2: Identify algorithm that guarantees solution

    Backtracking with constraint propagation prunes invalid choices early, and heuristic ordering reduces branching, ensuring completeness and efficiency.
  3. Final Answer:

    Option D -> Option D
  4. Quick Check:

    Constraint propagation + heuristic ordering is standard for Sudoku solvers [OK]
Hint: Constraint satisfaction problems need backtracking with pruning [OK]
Common Mistakes:
  • Assuming greedy or BFS alone can solve Sudoku efficiently
3. Consider the following code snippet implementing the optimal backtracking solution for Word Search. Given the board = [["A","B","C"],["D","E","F"],["G","H","I"]] and word = "BEF", what is the return value of exist(board, word)?
easy
A. True
B. False
C. Raises IndexError
D. Infinite recursion

Solution

  1. Step 1: Trace dfs starting at cell (0,1) 'B'

    Word starts with 'B', found at (0,1). dfs(0,1,0) matches 'B'.
  2. Step 2: Explore neighbors for next chars 'E' and 'F'

    From (0,1), neighbors checked: (1,1) 'E' matches next char, then (1,2) 'F' matches last char. All matched, return True.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    All characters found sequentially with valid moves [OK]
Hint: Trace dfs calls carefully to confirm path exists [OK]
Common Mistakes:
  • Assuming no path due to adjacency confusion
  • Missing in-place marking effect
4. What is the time complexity of the bitmask backtracking solution for counting all N-Queens solutions on an nxn board?
medium
A. O(n^n) because each row tries all columns independently
B. O(2^n) due to bitmask operations over all subsets of columns
C. O(n^3) from checking conflicts and iterating over rows and columns
D. O(n!) because each queen placement reduces available columns by one

Solution

  1. Step 1: Identify branching factor per row

    Each row places one queen in a column not attacked by previous queens, reducing choices roughly by one each time.
  2. Step 2: Calculate total recursive calls

    Thus, total calls approximate n x (n-1) x (n-2) x ... = n! permutations.
  3. Final Answer:

    Option D -> Option D
  4. Quick Check:

    Backtracking prunes invalid columns, yielding O(n!) complexity [OK]
Hint: N-Queens backtracking explores permutations -> O(n!) [OK]
Common Mistakes:
  • Confusing bitmask ops with exponential subsets
  • Assuming polynomial due to pruning
5. What is the time complexity of the optimal in-place next permutation algorithm for an input array of length n?
medium
A. O(n) because it scans the array a constant number of times
B. O(n log n) because it sorts the suffix after the pivot
C. O(n!) due to generating all permutations
D. O(n^2) because it swaps elements multiple times in nested loops

Solution

  1. Step 1: Identify the main operations

    The algorithm scans from the end to find the pivot (O(n)), then scans again to find the swap element (O(n)), and finally reverses the suffix (O(n)).
  2. Step 2: Sum up the operations

    All steps are linear scans or swaps, so total time complexity is O(n).
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    No sorting or factorial generation involved, linear scans only [OK]
Hint: Next permutation scans array linearly multiple times [OK]
Common Mistakes:
  • Confusing suffix reversal with sorting
  • Assuming factorial due to permutations