(回溯) LeetCode 47. 全排列||

原题链接

建议先练习:全排列|

一. 题目描述

给定一个可包含重复数字的序列 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

二. 解题思路

这个题目和之前的全排列一相似,唯一不相同的地方就是在上一题中明确规定数组中不存在重复元素,但是这个里面存在重复的元素,所以我们自然而然的可以想到去重,那该怎么去重呢,原理很简单,与之前组合一和组合二的去重方法相同,建议大家可以先去练习一下。

老套路:递归三件套!!但是这个在做之前得将原数组nums 排序,保证重复的元素在一起,这样才方便进行去重操作。

1. 确定递归条件:和之前全排列一的思路相同,只需要一个nums 原数组和 used 数组记录path 已经加入的元素。

2. 确定递归的终止条件:由于是递归,所以也很简单,只需要path.size() == nums.size() 的时候,将结果收集到res 中再return 即可。

3. 单层递归原则:首先我们得进行去重操作:如下图所示,当第一层中选取了第一个1的时候,回溯之后就没必要选取第二个1了,因为同一层选取相同的元素势必会出现相同的结果,所以直接剪枝即可,但是怎么剪枝呢?(1)判断当前元素和前一个元素是否相同,即 i > 0 && nums[i] == nums[i - 1]。(2) 判断当前元素是否是同一层相同元素回溯得来,即 used[i - 1] == false,因为在回溯之前会选取 nums[i - 1] ,这时候会将used[i - 1] 赋值为 true, 在回溯结束之后,会将 used[i - 1] 重新赋值为 false,这就是在树层上去重的一个操作。其次,在进行树层上的剪枝之后,需要进行树枝上的剪枝,即判断 used[i] 时候为 true ,如果为true ,说明在path 中已经选取了该元素,continue 即可。最后就是熟悉的递归操作,将used[i] 赋值为 true,并在 path 中添加 nums[i] ,然后进行递归,递归结束后回溯即可。

写了一大堆,可能可读性不是很好,希望大家可以看懂。

话不多说!!!上代码!!

三. 代码

class Solution {
public:
    vector<vector<int>> res;
    vector<int> path;
    void back(vector<int>& nums, vector<bool> used){
        if(path.size() == nums.size()) {
            res.push_back(path);
            return;
        }
        for(int i = 0; i < nums.size(); i++){
            if(i > 0 && nums[i] == nums[i - 1] && !used[i - 1]){        
                continue;
            }
            if(used[i]) continue;
            used[i] = true;
            path.push_back(nums[i]);
            back(nums, used);
            used[i] = false;
            path.pop_back();
        }
    }
    vector<vector<int>> permuteUnique(vector<int>& nums) {
        sort(nums.begin(), nums.end());
        vector<bool> used(nums.size(), false);
        back(nums, used);
        return res;
    }
};

四. 总结

本题还是属于回溯里面比较进阶的题目吧,建议大家练习,但是一定要懂得每一步的作用以及结果,写代码一定要勤思考,多思考就能印象深刻。加油!!我们共勉!

时间复杂度:O(n! * n)

空间复杂度:O(n)

喜欢的话给个关注吧!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值