代码编织梦想

主要是我自己刷题的一些记录过程。如果有错可以指出哦,大家一起进步。
转载代码随想录
原文链接:
代码随想录
leetcode链接:47. 全排列 II

题目:

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

思路:

这道题目和46.全排列的区别在与给定一个可包含重复数字的序列,要返回所有不重复的全排列。

这里又涉及到去重了。

在40.组合总和II 、90.子集II我们分别详细讲解了组合问题和子集问题如何去重。

那么排列问题其实也是一样的套路。

还要强调的是去重一定要对元素进行排序,这样我们才方便通过相邻的节点来判断是否重复使用了。

我以示例中的 [1,1,2]为例 (为了方便举例,已经排序)抽象为一棵树,去重过程如图:
在这里插入图片描述图中我们对同一树层,前一位(也就是nums[i-1])如果使用过,那么就进行去重。

一般来说:组合问题和排列问题是在树形结构的叶子节点上收集结果,而子集问题就是取树上所有节点的结果。

在46.全排列中已经详细讲解了排列问题的写法,在40.组合总和II 、90.子集II 中详细讲解了去重的写法,所以这次我就不用回溯三部曲分析了,直接给出代码,如下:

C++代码

class Solution {
private:
    vector<vector<int>> result;
    vector<int> path;
    void backtracking (vector<int>& nums, vector<bool>& used) {
        // 此时说明找到了一组
        if (path.size() == nums.size()) {
            result.push_back(path);
            return;
        }
        for (int i = 0; i < nums.size(); i++) {
            // used[i - 1] == true,说明同一树枝nums[i - 1]使用过
            // used[i - 1] == false,说明同一树层nums[i - 1]使用过
            // 如果同一树层nums[i - 1]使用过则直接跳过
            if (i > 0 && nums[i] == nums[i - 1] && used[i - 1] == false) {
                continue;
            }
            if (used[i] == false) {
                used[i] = true;
                path.push_back(nums[i]);
                backtracking(nums, used);
                path.pop_back();
                used[i] = false;
            }
        }
    }
public:
    vector<vector<int>> permuteUnique(vector<int>& nums) {
        result.clear();
        path.clear();
        sort(nums.begin(), nums.end()); // 排序
        vector<bool> used(nums.size(), false);
        backtracking(nums, used);
        return result;
    }
};

拓展

大家发现,去重最为关键的代码为:

if (i > 0 && nums[i] == nums[i - 1] && used[i - 1] == false) {
    continue;
}

如果改成 used[i - 1] == true, 也是正确的!,去重代码如下:

if (i > 0 && nums[i] == nums[i - 1] && used[i - 1] == true) {
    continue;
}

这是为什么呢,就是上面我刚说的,如果要对树层中前一位去重,就用used[i - 1] == false,如果要对树枝前一位去重用used[i - 1] == true

对于排列问题,树层上去重和树枝上去重,都是可以的,但是树层上去重效率更高!

这么说是不是有点抽象?

来来来,我就用输入: [1,1,1] 来举一个例子。

树层上去重(used[i - 1] == false),的树形结构如下:

在这里插入图片描述树枝上去重(used[i - 1] == true)的树型结构如下:
在这里插入图片描述

总结

这道题其实还是用了我们之前讲过的去重思路,但有意思的是,去重的代码中,这么写:

if (i > 0 && nums[i] == nums[i - 1] && used[i - 1] == false) {
    continue;
}

和这么写:

if (i > 0 && nums[i] == nums[i - 1] && used[i - 1] == true) {
    continue;
}

都是可以的,这也是很多同学做这道题目困惑的地方,知道used[i - 1] == false也行而used[i - 1] == true也行,但是就想不明白为啥。

所以我通过举[1,1,1]的例子,把这两个去重的逻辑分别抽象成树形结构,大家可以一目了然:为什么两种写法都可以以及哪一种效率更高!

是不是豁然开朗了!!

自己的代码

class Solution {
    vector<vector<int>>result;
    vector<int>path;
    void dfs(vector<int>& nums, vector<bool>used) {
        if (path.size() == nums.size()) {
            result.push_back(path);
            return;
        }
        int curDepthDup[25]{0};
        for (int i = 0; i < nums.size(); ++i) {
            if (used[i] == true|| curDepthDup[nums[i] + 10] == 1) {      //已经被用过了,就不能再用了
                continue;
            }
            curDepthDup[nums[i] + 10] = 1;
            path.push_back(nums[i]);
            used[i] = true;
            dfs(nums, used);
            used[i] = false;
            path.pop_back();
        }
        return;
    }

public:
    vector<vector<int>> permuteUnique(vector<int>& nums) {
        vector<bool>used(nums.size(), false);
        dfs(nums, used);
        return result;
    }
};
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/Ge_yangwen/article/details/129642533

python回溯算法全排列_从全排列看回溯算法-爱代码爱编程

从全排列看回溯算法 最近又刷起了算法,仿佛回到了大一时奋战到深夜场景,走上社会之初发现大学里学的都是啥玩意儿,工作中基本遇不到,各种数据结构都被封装的妥妥的根本不需要我们去操心,以至于越来越浮于表面。 现在觉得大学的课程是真功夫,是无数学者总结提炼的精华,是计算机从业人员是基本功,基本功不扎实很快就会遇到瓶颈,对算法与数据结构掌握与理解不透彻很难写

python回溯算法全排列_回溯算法详解[力扣46:全排列]-爱代码爱编程

解决一个回溯问题,实际上就是一个决策树的遍历过程。你只需要思考 3 个问题: 1、路径:也就是已经做出的选择。 2、选择列表:也就是你当前可以做的选择。 3、结束条件:也就是到达决策树底层,无法再做选择的条件。 如果你不理解这三个词语的解释,没关系,我们后面会用「全排列」和「N 皇后问题」这两个经典的回溯算法问题来帮你理解这些词语是什么意思,现

回溯算法:全排列问题-爱代码爱编程

回溯算法:全排列问题 思路:利用一个数组进行去重 class Solution { public: vector<vector<int>> res; vector<int> path; vector<int> use; vector<vector<int>

全排列回溯算法C语言,用回溯算法解决全排列问题-爱代码爱编程

全排列问题的回溯解法: public class Permute { public int N; public int[] X; public static void main(String[] args) { new Permute().test(4); } public void test(int _N){ N=_N;X=new i

leetcode刷题/回溯算法 47. 全排列 II-爱代码爱编程

47. 全排列 II 题意 给定一个可包含重复数字的序列 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,

回溯算法解决全排列-爱代码爱编程

回溯法 采用试错的思想,它尝试分步的去解决一个问题。在分步解决问题的过程中,当它通过尝试发现现有的分步答案不能得到有效的正确的解答的时候,它将取消上一步甚至是上几步的计算,再通过其它的可能的分步解答再次尝试寻找问题的答案。回溯法通常用最简单的递归方法来实现,在反复重复上述的步骤后可能出现两种情况: 46. 全排列 难度中等1542收藏分享切

力扣算法回溯篇:全排列-爱代码爱编程

回溯三部曲: 1、确定回溯函数参数 需要一个数组标记某元素已经使用过,used void backtracking(vector<int>& nums,vector<int>& used) 2、确定递归终止条件 当收集元素大小等于nums.size()时,即可停止 3、确定单层搜索过程 for循环控制横向遍历

回溯算法:全排列问题(python)-爱代码爱编程

回溯算法:全排列问题(python) 图解 代码实现 ```python def dfs(a,used,res,path,depth,lengh): #当深度=数组长度时候 将copy的数据放入res结果集 if depth==lengh: res.append(path.copy()) return

python 全排列(回溯算法)-爱代码爱编程

问题:输入列表L(不含重复元素),输出L的全排列。 如输入:L=[1,2,3] 则输出:[[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]]         全排列问题,可以用回溯法解决,详细分析请参考东哥公众号:labuladong,看了之后醍醐灌顶。        

【算法】递归回溯:全排列-爱代码爱编程

递归与回溯:46. 全排列 //C++版本 #include<iostream> #include<vector> using namespace std; // pair<int, int> t(0, 0); vector<int> path; vector<int> path_note(9

算法学习:47.全排列 II-爱代码爱编程

全排列 II 题目链接:力扣题目链接难度:中等 给定一个可包含重复数字的序列 nums ,按任意顺序 返回所有不重复的全排列。 示例 1: 输入:nums = [1,1,2] 输出: [[1,1,2], [1,2,1], [2,1,1]] 思路 此题和46.全排列的区别在于给定一个可包含重复数字的序列, 要返回所有不重复的全排列。涉及到了去

回溯算法:全排列-爱代码爱编程

本文解决几个问题: 回溯算法是什么?解决回溯算法相关的问题有什么技巧?如何学习回溯算法?回溯算法代码是否有规律可循? 其实回溯算法其实就是我们常说的 DFS 算法,本质上就是一种暴力穷举算法。 废话不多说,直接上回溯算法框架。解决一个回溯问题,实际上就是一个决策树的遍历过程。 站在回溯树的一个节点上,你只需要思考 3 个问题: 1、路径:也就是已

Leetcode46:全排列问题(回溯算法)-爱代码爱编程

Leetcode46:全排列问题 解法一:(推荐) 题目: 给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。思路:回溯算法+代码注释代码如下:class Solution { //定义二维数组存放结果集 List<List<Integer>> res = n

Leetcode47:全排列II(回溯算法)-爱代码爱编程

Leetcode47:全排列II 知识前提:以leetcode46全排列为基础 题目: 给定一个可包含重复数字的序列 nums ,按任意顺序 返回所有不重复的全排列。 思路:在全排列(leetcode46题的基础上进行去重) 去重最为关键的代码为:(树层相同元素进行去重) if (i > 0 && nums[i]

20220514回溯算法:全排列-爱代码爱编程

        题目描述:给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。         编码实现: public List<List<Integer>> permute(int[] nums) { List<List<Integer>

leetcode刷题:回溯算法13(全排列 ii)_涛涛英语学不进去的博客-爱代码爱编程

47.全排列 II 力扣题目链接 给定一个可包含重复数字的序列 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

回溯算法集合(全排列,组合,子集)_资料加载中的博客-爱代码爱编程

一套模板搞定全排列,组合,子集问题(递归嵌套for循环) 组合问题:leetcode77、leetcode39、leetcode40、leetcode216 全排列问题:leetcode46、leetcode47 子集问题:leetcode78、leetcode90 基础模板 def __init__(self): #收集所有符合条件的集