Permutations

Updated on 01 July, 2025
Permutations header image

Problem Statement

The task requires generating all possible permutations of a given array of distinct integers, nums. A permutation of an array is a rearrangement of its elements. You are to return the permutations in any order. Each integer in the array is unique and can range from -10 to 10.

Examples

Example 1

Input:

nums = [1,2,3]

Output:

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

Example 2

Input:

nums = [0,1]

Output:

[[0,1],[1,0]]

Example 3

Input:

nums = [1]

Output:

[[1]]

Constraints

  • 1 <= nums.length <= 6
  • -10 <= nums[i] <= 10
  • All the integers of nums are unique.

Approach and Intuition

Understanding Permutations

Permutations involve arranging elements of a set in all possible orders. For an array nums of length n, there are n! (factorial of n) permutations. The factorial function usually grows very quickly, which informs our constraints on the maximum array length.

Example Analysis

  1. Example 1:

    • Input: nums = [1,2,3]
    • Output: All permutations of [1,2,3].
    • As nums has 3 elements, there should be 3! = 6 permutations. These include arrangements such as [1,2,3], where we start with 1 and permute 2 and 3, and [2,1,3], where we start with 2 and permute 1 and 3.
  2. Example 2:

    • Input: nums = [0,1]
    • Output: The permutations are [0,1] and [1,0].
    • These are straightforward as the array has only two elements, so swapping them gives the permutations.
  3. Example 3:

    • Input: nums = [1]
    • Output: [[1]]
    • With only one element, the only "permutation" is the element itself.

Generation Techniques

  • Backtracking Algorithm:

    • Recursively choose an element to be the next in the permutation sequence.
    • Use a temporary structure (like a list or a set) to keep track of what elements have been used.
    • Backtrack once you have considered all elements for the current sequence.
  • Iterative Algorithms:

    • Techniques like Heap's Algorithm can be used to generate permutations by swapping elements systematically.

Given the constraints:

  • Maximum array length is 6, so at most we are generating 720 permutations (6!).
  • Elements are within a small integer range and are guaranteed unique, simplifying checks and swaps.

Each method suits different scenarios and complexities, especially when considering the reusability of elements and the cost of recursion or iteration control structures for higher factorial values.

Solutions

  • C++
  • Java
  • C
  • JavaScript
  • Python
cpp
class Solution {
public:
    vector<vector<int>> generatePermutations(vector<int>& elements) {
        vector<vector<int>> result;
        vector<int> permutation;
        findPermutations(permutation, result, elements);
        return result;
    }
    
    void findPermutations(vector<int>& permutation, vector<vector<int>>& result, vector<int>& elements) {
        if (permutation.size() == elements.size()) {
            result.push_back(permutation);
            return;
        }
    
        for (int element : elements) {
            if (find(permutation.begin(), permutation.end(), element) == permutation.end()) {
                permutation.push_back(element);
                findPermutations(permutation, result, elements);
                permutation.pop_back();
            }
        }
    }
};

The provided C++ program features a class named Solution that includes two primary functions to generate all possible permutations of a given array of integers. The main approach utilizes backtracking, which is a common technique for such permutation problems.

Here’s a brief breakdown of each function within the class:

  • generatePermutations: This function initializes the necessary containers for the problem—a result container vector<vector<int>> for storing all permutations and a temporary vector<int> to hold the current permutation. It then calls the helper function findPermutations passing these initialized containers along with the input vector elements.

  • findPermutations: A recursive function that generates permutations. The base case checks if the size of the temporary permutation vector matches the size of the input vector elements. If they match, it means a complete permutation is formed and this permutation is added to the result container. The function then iterates over each element in the input vector, checking if the element is already included in the current permutation to ensure no duplicates. If the element is not in the current permutation, it’s added, and the function recursively calls itself. Once the call returns, the element is removed (backtracked), allowing for the subsequent permutations to be formed correctly.

Make sure to include the necessary headers before implementing this class, such as #include <vector> and #include <algorithm> to handle the C++ Standard Library containers and functions used in the code.

java
class Solution {
    public List<List<Integer>> generatePermutations(int[] elements) {
        List<List<Integer>> result = new ArrayList<>();
        permuteHelper(new ArrayList<>(), result, elements);
        return result;
    }
    
    public void permuteHelper(
        List<Integer> current,
        List<List<Integer>> result,
        int[] elements
    ) {
        if (current.size() == elements.length) {
            result.add(new ArrayList<>(current));
            return;
        }
    
        for (int element : elements) {
            if (!current.contains(element)) {
                current.add(element);
                permuteHelper(current, result, elements);
                current.remove(current.size() - 1);
            }
        }
    }
}

This code in Java is designed to generate all permutations of a given array of integers. The main class, named Solution, contains a method generatePermutations which utilizes a helper method permuteHelper to facilitate the recursive generation of permutations.

Here's a breakdown of how the solution operates:

  • The generatePermutations method initializes an empty list called result that will eventually hold all permutations as lists of integers. It calls the permuteHelper method, passing it an empty list (current permutation), the result list, and the elements array which contains the integers to permute.

  • The recursive method permuteHelper determines when a permutation is complete (when the size of the current list equals the size of the elements array) and adds it to the result list. For permutation generation, the method iterates over each element in the elements array. If the element is not already in the current permutation represented by current, it is added, and permuteHelper is recursively called with this new state. After the recursive call, the last added element is removed to backtrack and explore other permutations.

This code effectively captures all permutations by ensuring each integer is considered for each position in the permutation, provided it's not already included. The use of recursion coupled with backtracking allows the exploration of all possible configurations of the given integers, ensuring comprehensive permutation generation.

c
void generate_permutations(int* elements, int total_elements, int* current_permu, int curr_size, int** results,
               int* countResults) {
    if (curr_size == total_elements) {
        results[*countResults] = malloc(sizeof(int) * total_elements);
        memcpy(results[*countResults], current_permu, total_elements * sizeof(int));
        *countResults += 1;
        return;
    }
    for (int pos = 0; pos < total_elements; pos++) {
        bool already_used = false;
        for (int prev = 0; prev < curr_size; prev++) {
            if (current_permu[prev] == elements[pos]) {
                already_used = true;
                break;
            }
        }
        if (!already_used) {
            current_permu[curr_size] = elements[pos];
            generate_permutations(elements, total_elements, current_permu, curr_size + 1, results, countResults);
        }
    }
}
int** permute_array(int* elements, int len, int* size_result_set,
              int** size_results) {
    int** permutations = (int**)malloc(sizeof(int*) * 10000);
    *size_results = (int*)malloc(sizeof(int) * 10000);
    for (int i = 0; i < 10000; i++) {
        (*size_results)[i] = len;
    }
    *size_result_set = 0;
    int* temp_permu = (int*)malloc(len * sizeof(int));
    memset(temp_permu, -1, len * sizeof(int));
    generate_permutations(elements, len, temp_permu, 0, permutations, size_result_set);
    free(temp_permu);
    return permutations;
}

The provided C code defines a function for generating all possible permutations of a given integer array. Below is an overview of the solution:

  1. Function Definitions:

    • generate_permutations—Recursive function that builds permutations.
    • permute_array—Initializes the process and gathers results.
  2. How It Works:

    • In permute_array, memory allocation occurs for storing permutations and a temporary array (temp_permu) is used for constructing permutations.
    • The generate_permutations function then recursively generates permutations by trying each element in the current position and checking if it has been used already in the permutation being constructed.
    • Once a permutation of the required size is formed, it is stored in the results and the count of results is incremented.
  3. Memory Management:

    • Dynamic memory is allocated for permutations and the temporary array. Ensure to manage this memory properly to avoid leaks, especially in large-scale uses or modifications of this function.
    • After recursion, occupied memory for the temporary array is freed in permute_array to prevent memory leakage.
  4. Usage Example:

    • Call permute_array with the array of integers you want to permute along with its length, and retrieve the collection of permutations and their size.
    • The function will return a pointer to an array of integer pointers, each pointing to an array that represents a permutation.

This approach is effective for generating permutations in a systematic manner without redundancies, owing to the use of the already_used flag that avoids reusing elements in a single permutation. When implementing or modifying this logic, ensure proper error handling and boundary checks for robustness, especially when handling the potential large allocations for storing permutations.

js
var generatePermutations = function (elements) {
    var results = [];
    var recurse = function (current) {
        if (current.length === elements.length) {
            results.push([...current]);
            return;
        }
        for (var element of elements) {
            if (!current.includes(element)) {
                current.push(element);
                recurse(current);
                current.pop();
            }
        }
    };
    recurse([]);
    return results;
};

The given JavaScript function generatePermutations is designed to calculate all possible permutations of a given list of elements. The process is as follows:

  • Declare an array results to store the resulting permutations.
  • Define a recursive function recurse that:
    • Checks if the length of the current permutation current is equal to the length of elements. If so, it adds a copy of current to results.
    • Iterates over each element in elements to expand the current permutation:
      • If the element is not already in current, add it, recurse, and then remove it to backtrack.
  • Initiate the recursive process by calling recurse with an empty array.
  • Return the results array containing all permutations.

This implementation uses a backtracking approach to explore all possible combinations by adding elements one by one, and removing them (backtracking) once all permutations for that specific prefix are found. The use of array operations like push, pop, and includes facilitates the management of the current permutation's state during the recursion.

python
class Solution:
    def permute(self, numbers: List[int]) -> List[List[int]]:
        def generate_permutations(current):
            if len(current) == len(numbers):
                result.append(current.copy())
                return
                
            for number in numbers:
                if number not in current:
                    current.append(number)
                    generate_permutations(current)
                    current.pop()
    
        result = []
        generate_permutations([])
        return result

The Python3 solution provided addresses the problem of generating all permutations of a list of numbers.

  • Working with the permute method, the approach involves defining an inner recursive function generate_permutations that builds permutations by choosing numbers not currently in the permutation being constructed.

  • Initialization of an empty list result is done to store all the permutations. The recursion starts with an empty list representing the initial state of the permutation.

  • During each recursive call:

    1. If the current list is equal in length to the input list numbers, it signifies a complete permutation, which is then added to the result.
    2. The function iterates over each number in the input list numbers.
    3. For each number, if it is not already in the current list (to avoid repetition), it gets added, and the function recurses deeper with this new state.
    4. After recursion returns, the last added element is removed (backtracking), allowing for exploration of different numbers to add next.
  • The function returns the result list once all permutations have been explored and compiled. This technique effectively employs recursive backtracking to address the permutation problem, ensuring that all possible combinations of the list elements are considered without repetition of elements within a permutation.

Comments

No comments yet.