【LeetCode-回溯】全排列 II

题目描述

给定一个可包含重复数字的序列,返回所有不重复的全排列。
示例:

输入: [1,1,2]
输出:
[
  [1,1,2],
  [1,2,1],
  [2,1,1]
]

题目描述: https://leetcode-cn.com/problems/permutations-ii/
做这题之前可以先做一下全排列

思路

这题和全排列基本是一样的。这题中的数组可能包含重复的数字,所以牵扯到去重的操作。为什么会出现重复呢?举个例子,假如数组是[1,1,2],我们首先使用第一个数字1进行递归,会得到两个排列[1,1,2]和[1,2,1];然后我们再使用第二个数字1进行递归,会得到两个排列[1,1,2]和[1,2,1];最后使用第三个数字2进行递归,得到两个排列[2,1,1]和[2,1,1]。可以看到使用第二个数字1进行递归和使用第三个数字2进行递归会产生重复的结果。这两种会产生重复结果的情况的共同点是:在同一层递归使用了相同的数字。例如第一个数字1是第1层的第一个节点,此时这层只有这一个节点,不存在重复的情况,所以可以递归。而第二个1是第1层的第2个节点,此时这一层已经有一个1了,所以就不能继续递归了(剪枝)。使用第3个数字2进行递归也是类似的。

在实际操作的时候,我们先将数组排序,然后在递归的时候比较当前值是否和前一个相等,如果相等并且前一个值已经加入到排列中了,那就将当前值跳过。代码如下:

class Solution {
public:
    vector<vector<int>> ans;
    vector<vector<int>> permuteUnique(vector<int>& nums) {
        if(nums.empty()) return {{}};

        vector<int> track;
        vector<int> visit(nums.size(), 0);
        sort(nums.begin(), nums.end()); // 先排序有助于去重
        dfs(nums, 0, track, visit);
        return ans;
    }

    void dfs(vector<int> nums, int start, vector<int> track, vector<int> visit){
        if(track.size()==nums.size()){
            ans.push_back(track);
            return;
        }

        for(int i=0; i<nums.size(); i++){
            if(i>0 && nums[i]==nums[i-1] && visit[i-1]) continue;   // 去重操作
            if(!visit[i]){
                track.push_back(nums[i]);
                visit[i] = 1;
                dfs(nums, 0, track, visit);
                track.pop_back();
                visit[i] = 0;
            }
        }

    }
};

这篇题解对于去重的操作讲的很好。

相关题目

1、全排列:https://www.cnblogs.com/flix/p/12771867.html
2、子集:https://www.cnblogs.com/flix/p/12769354.html
3、子集II:https://www.cnblogs.com/flix/p/12770371.html

posted @ 2020-04-25 11:39  Flix  阅读(186)  评论(0编辑  收藏  举报