「前端刷题」47. 全排列 II

187 阅读1分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

题目

给定一个可包含重复数字的序列 nums按任意顺序 返回所有不重复的全排列。

 

示例 1:

**输入:**nums = [1,1,2] 输出: [[1,1,2], [1,2,1], [2,1,1]]

示例 2:

**输入:**nums = [1,2,3] 输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]

 

提示:

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

思路

该题是基础全排列的变种,主要在于去重。

针对同一层次的计算,对连续的相同的元素只选取一个进行后续的替换,即可等价于基础全排列。例如,当前层次是[1,2, 1, 2], 我们可以只选取第一次出现的元素作为替换: 对于第一个元素1, 第一次出现,则其结果为1与[2, 1, 2]的所有全排列的连接,标记1已使用;对于第二个元素2,2未使用,则其结果为2与[1, 1, 2]的全排列的连接,并标记2已使用; 对于第3个元素1,其已使用,跳过;对于最后一个元素2,由于2已使用,跳过。

permute(8).png

var swap = function(nums, i, j) {
    if (i === j)
        return;
    const t = nums[i];
    nums[i] = nums[j];
    nums[j] = t;
};

var cal = function (nums, first, result) {
    if (nums.length === first) {
        result.push([...nums]);
        return;
    }

    const map = new Map();
    for (let i = first; i < nums.length; i++) {
        if (!map.get(nums[i])) {
            map.set(nums[i], true);
            swap(nums, first, i);
            cal(nums, first + 1, result);
            swap(nums, first, i);
        }
    }
};

/**
 * @param {number[]} nums
 * @return {number[][]}
 */
var permuteUnique = function(nums) {
    if (nums == null)
        return;
    
    nums.sort((a, b) => a - b);
    const res = [];
    cal(nums, 0, res);
    return res; 
};

优化

class Solution{
public:
    vector<vector<int>> permuteUnique(vector<int>& nums)
    {
        vector<int> track(nums.begin(),nums.end());
        sort(track.begin(),track.end());
        vector<vector<int>> result;
        backtrack(track,result,0,track.size());
        return result;
        
    }
    
    /*
    针对同一层次的计算,对连续的相同的元素只选取一个进行后续的替换,即可等价于基础全排列。例如,当前层次是[1,2, 1, 2], 我们可以只选取第一次出现的元素作为替换: 对于第一个元素1, 第一次出现,则其结果为1与[2, 1, 2]的所有全排列的连接,标记1已使用;对于第二个元素2,2未使用,则其结果为2与[1, 1, 2]的全排列的连接,并标记2已使用; 对于第3个元素1,其已使用,跳过;对于最后一个元素2,由于2已使用,跳过。
    */
    void backtrack(vector<int>& track,vector<vector<int>>& result,int index,int size)
    {
        if(index==size)result.push_back(track);
        else{
            unordered_map<int,int> mp;
            for(int i=index;i<track.size();++i)//index的起始点表示选择列表的范围
            {
                /*剪枝:同层次此元素已使用多次,在使用必然会照成重复全排列,所以直接跳过*/
                if(mp.count(track[i]))continue;
                /*决策路径加上这个决策*/
                swap(track[index],track[i]);
                /*进入下一步决策*/
                backtrack(track,result,index+1,size);
                /*决策路径移除这个决策*/
                swap(track[index],track[i]);
                /*标记此层次这个元素已使用一次了*/
                mp[track[i]]=1;
            }
        }
    }
};