Programming & Development / April 8, 2025

LeetCode 47: Permutations II in Go – Backtracking with Handling Duplicates

Go Golang LeetCode Permutations II Backtracking Duplicates Time Complexity O(n!) Space Complexity O(n) Recursive Solution Interview Question

Introduction

LeetCode 47: Permutations II is an extension of the Permutations problem. The challenge here is to generate all distinct permutations of a collection of numbers, which may contain duplicates.

The main difference from LeetCode 46: Permutations is that we need to avoid generating duplicate permutations. This requires a slightly modified approach where we handle duplicates during the recursion step.

This problem can be solved efficiently using backtracking combined with sorting and a mechanism to skip over repeated elements.

Problem Statement

Given a collection of numbers that may contain duplicates, return all possible unique permutations.

Example 1:

go

Input: nums = [1,1,2]
Output: 
[
  [1,1,2],
  [1,2,1],
  [2,1,1]
]

Example 2:

go

Input: nums = [1,2,3]
Output:
[
  [1,2,3],
  [1,3,2],
  [2,1,3],
  [2,3,1],
  [3,1,2],
  [3,2,1]
]

Approach: Backtracking with Handling Duplicates

The approach involves backtracking but with an additional step of sorting the input array and using a used array to track which elements have been considered for the current position. By sorting the input array, we ensure that duplicates are adjacent, making it easy to skip over them during the recursion step.

Steps:

  1. Sort the Array: Sorting ensures that duplicates are adjacent, which allows us to easily skip duplicates.
  2. Backtracking: At each index, try to use each element in the current position, but ensure we don't use the same element twice in a given recursion level.
  3. Used Array: A used array helps track which elements have been used in the current recursive call.
  4. Skip Duplicates: If an element is the same as the previous element and the previous element has not been used in the current recursion, skip it to avoid generating duplicate permutations.

Go Implementation

go

func permuteUnique(nums []int) [][]int {
    var res [][]int
    sort.Ints(nums)  // Sort the input to handle duplicates
    backtrack(nums, []int{}, &res, make([]bool, len(nums)))
    return res
}

func backtrack(nums []int, current []int, res *[][]int, used []bool) {
    // If the current permutation is complete, add it to the result
    if len(current) == len(nums) {
        temp := make([]int, len(nums))
        copy(temp, current)
        *res = append(*res, temp)
        return
    }

    for i := 0; i < len(nums); i++ {
        // Skip used elements or duplicate elements that haven't been used yet
        if used[i] || (i > 0 && nums[i] == nums[i-1] && !used[i-1]) {
            continue
        }

        // Mark the current element as used and recurse
        used[i] = true
        backtrack(nums, append(current, nums[i]), res, used)
        used[i] = false  // Backtrack and mark the element as unused
    }
}

Example Walkthrough

Example 1:

go

Input: nums = [1,1,2]
  1. Initial Call: backtrack([1, 1, 2], [], res, used)
  • Sort nums: [1, 1, 2]
  • Start at index 0, attempt to place 1 at the first position.
  • Recursively generate permutations: backtrack([1, 1, 2], [1], res, used)
  • Next, try placing 1 again at the second position.
  • Recursively generate permutations: backtrack([1, 1, 2], [1, 1], res, used)
  • Finally, place 2 in the last position.
  • Base case: Record permutation [1, 1, 2].
  • Backtrack, and try other combinations while skipping duplicates.
  1. Final Output:
go

[
  [1, 1, 2],
  [1, 2, 1],
  [2, 1, 1]
]

Example 2:

go

Input: nums = [1, 2, 3]
  1. Initial Call: backtrack([1, 2, 3], [], res, used)
  • Sort nums: [1, 2, 3]
  • Start at index 0, place 1 first and recursively generate permutations for [2, 3].
  • Continue with 2 and 3 at the next recursive levels, ensuring no duplicates are generated.
  • All permutations are recorded.
  1. Final Output:
go

[
  [1, 2, 3],
  [1, 3, 2],
  [2, 1, 3],
  [2, 3, 1],
  [3, 1, 2],
  [3, 2, 1]
]

Time and Space Complexity

MetricComplexityTime ComplexityO(n!)Space ComplexityO(n)

  • Time Complexity: The number of unique permutations for n distinct elements is n!, so the time complexity remains O(n!). The sorting step is O(n log n), but the backtracking dominates the complexity.
  • Space Complexity: The space complexity is O(n) due to the recursion stack and the storage of the result, where n is the length of the input array.

Edge Cases

  • Single Element: A single-element array has only one permutation.
  • Input: [1]
  • Output: [[1]]
  • All Elements Identical: If the array contains the same element repeated, there will be only one unique permutation.
  • Input: [2, 2, 2]
  • Output: [[2, 2, 2]]
  • Empty Array: An empty array has only one permutation, which is the empty array itself.
  • Input: []
  • Output: [[]]

Key Takeaways

  • Backtracking with duplicate handling is crucial for problems like this, where we need to ensure unique permutations while respecting constraints.
  • Sorting the input array ensures that duplicates are adjacent, making it easy to skip over them during the recursion step.
  • The approach can be generalized to problems requiring the generation of unique combinations or permutations with repetition.



Comments

No comments yet

Add a new Comment

NUHMAN.COM

Information Technology website for Programming & Development, Web Design & UX/UI, Startups & Innovation, Gadgets & Consumer Tech, Cloud Computing & Enterprise Tech, Cybersecurity, Artificial Intelligence (AI) & Machine Learning (ML), Gaming Technology, Mobile Development, Tech News & Trends, Open Source & Linux, Data Science & Analytics

Categories

Tags

©{" "} Nuhmans.com . All Rights Reserved. Designed by{" "} HTML Codex