Total permutation problem

Posted by jeethau on Tue, 08 Feb 2022 09:05:05 +0100

Full permutation has two enumeration orders:
(1) Enumerate which number to fill in each position in order
(2) Enumerate each number in order and fill in which position

Both orders can be solved, but if you want to ensure the dictionary order, you need to use (1) enumerate which number to fill in each position in order. Because the priority is to put the small number in the front position.

leetcode 46. Full arrangement
Given an array nums without duplicate numbers, all possible permutations are returned. You can return answers in any order.

Example 1:
Input: num = [1,2,3]
Output: [[1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2], [3,2,1]]

Tips:
1 <= nums.length <= 6
-10 <= nums[i] <= 10
All integers in nums are different from each other

Scheme: adopt (1) enumerate which number to fill in each position in order

class Solution {
    List<Integer> path = new ArrayList<>();
    List<List<Integer>> ans = new ArrayList<>();;
    public List<List<Integer>> permute(int[] nums) {
        dfs(nums, 0, 0);
        return ans;
    }

    // u: Pit location: enumerate the number of each pit location in order
    public void dfs(int[] nums, int u, int state) {
        if (u == nums.length) {
            ans.add(new ArrayList<>(path));
            return;
        }
        for (int i = 0; i < nums.length; i++) { //Enumerate each number
            if ((state >> i & 1) == 0) { 
                path.add(nums[i]);
                dfs(nums, u + 1, state + (1 << i));
                path.remove(path.size() - 1);
            }
        }
    }
}

leetcode 47. Full arrangement II

Given a sequence nums that can contain repeated numbers, return all non repeated full permutations in any order.

Example 1:
Input: num = [1,1,2]
Output:
[[1,1,2],
[1,2,1],
[2,1,1]]

Tips:
1 <= nums.length <= 8
-10 <= nums[i] <= 10

Method 1: [enumerate each number in turn to see which pits can be filled]

class Solution {
public:
    vector<vector<int>> ans;
    vector<int> path;
    vector<vector<int>> permutation(vector<int>& nums) {
        sort(nums.begin(), nums.end());
        path.resize(nums.size());
        dfs(nums, 0, 0, 0);
        return ans;
    }

 // u: Enumeration to the first number start: where is the pit of the previous number state: mark whether the pit is occupied
    void dfs(vector<int> &nums, int u, int start, int state) {
        if (u == nums.size()) {
            ans.push_back(path);
            return;
        }
        if (!u || nums[u] != nums[u - 1]) start = 0; //The next repeating element can only be placed later, in order. The rest starts from the 0 pit to see whether it can be filled

        for (int i = start; i < nums.size(); i ++) //Enumerate the pits to see if nums[u] can be filled in
            if (!(state >> i & 1)) {
                path[i] = nums[u];
                dfs(nums, u + 1, i + 1, state + (1 << i));
            }
    }
};

Here i: pit position, u: the number. This is different from the previous question! The last question is to enumerate each pit in turn to see which numbers can be filled, while this question is to enumerate each number in turn to see which pits can be filled. (because this question enumerates each number in turn to judge the repeated number, and the repeated number can only be placed after the last repeated pit)
Link: https://www.acwing.com/activi...

Solution 2: [whether each number enumerated in pit u can be filled, that is, which number is filled in each position by enumerating in order]

vector<vector<int>> ans;
vector<int> path;
vector<bool> st;

void dfs(vector<int>& nums, int u) {
    if (u == nums.size()) {
        ans.push_back(path);
        return;
    }

    for (int i = 0; i < nums.size(); i++) // Enumerate each number in turn, so you can specify the order of the same number
        if (!st[i]) {
            if (i && (nums[i] == nums[i - 1]) && !st[i - 1]) // You can't jump with the same number you haven't used for the first time. Then the order is set
                continue;
            st[i] = true;
            path[u] = nums[i]; // Because the number of each position is enumerated in turn, path can also be used for u increment push_ back(nums[i]);
            dfs(nums, u + 1);
            st[i] = false;     // You need path pop_ back();
        }
}

vector<vector<int>> permuteUnique(vector<int>& nums) {
    sort(nums.begin(), nums.end());
    st = vector<bool>(nums.size(), false);
    path = vector<int>(nums.size());
    dfs(nums, 0);
    return ans;
}

I came back to watch the video again. It was really good. There were two core sentences. I sorted and wrote them with my own understanding.
A 1 minute 24 seconds to 2 minutes 00 seconds: why do you want to sort first? Because the results need to be de duplicated, and the results are in the form of a list. How can the list be judged to be duplicate? It's a comparison one by one after sorting, so it's better to sort first and then calculate, and judge whether there is repetition in the process of calculation, so as to avoid sorting and de duplication for each result.
B 2 minutes and 25 seconds to 4 minutes and 10 seconds: how to judge whether the current branch is a duplicate branch. Because the order has been arranged before, if the current element num [i] is the same as the previous element, that is, Num [i] = = num [I-1], it indicates that the branch may be repeated. However, there are two possibilities for this equality condition. One is 1 '2, that is, select the second 1 after selecting 1. Although the two elements are repeated, the second element is the next layer of the previous element. At this time, there is no problem. The other is that the previous same layer branches already have 1 1 '2. This time, 1' 1 2 is selected. The two elements are repeated, and the path of the same layer is repeated. That means it's a duplicate branch.
The specific way to distinguish is that if the used state of num [I-1] is selected, it means that the current num [i] is the next layer path of num [I-1]. Otherwise, if the status of num [I-1] is not selected, it indicates that the current num [i] is the same layer path of num [I-1].

An insider terminated AC
2021-10-01
@GB2312 is very good. Add it
Why "if the used state of num [I-1] is selected, it means that the current num [i] is the next layer path of num [I-1]."
Reason: if you recurse from the lower level, you will go to the lower level all the time, and dfs has not return ed, that is, used has not been traced back to unselected state. Therefore, the used state of num [I-1] on the same branch must be selected.
Why "if the status of num [I-1] is not selected, it means that the current num [i] is the same layer path of num [I-1]."
Reason: recurse to the leaf node and start backtracking upward. When backtracking to a certain layer, backtrack used[i-1] to the unselected state, and then for loop i + + moves horizontally. Naturally, it is judged that used[i-1] must be the unselected state.

Because we are enumerating which number to fill in each pit, the enumerations of the same number must be in order, and we can't and won't skip!

PS: the purpose of sorting in the two solutions is not to order the array, but to put the same numbers together.

Topics: leetcode