全排列

力扣题目链接

给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。

示例 1:

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

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

示例 2:

输入:nums = [0,1]

输出:[[0,1],[1,0]]

示例 3:

输入:nums = [1]

输出:[[1]]

思路

咋一看好像还可以用之前的套路

只要判断一下当前遍历值是否有使用过,有就跳过即可

但是这个“跳过”的操作如果还是使用beginIndex的话就会出错

一种经典的错误思路是:仍然使用beginIndex作为遍历时的起始值,当每次调用递归时就使用 当前位置+1 作为下次遍历的beginIndex

这样做的话最后只能得到一条路径上的结果,因为我们没办法即根据beginIndex去回溯又将beginIndex作为for循环的依据(递归可以回溯,for循环过了的东西是回不去的)

因此这里需要用used数组来记录当前遍历值是否出现过,如果出现过就直接跳过

此处的used仅用于记录当前值是否被选择过,其作用类似于哈希表

停止条件也不太一样,因为我们又是需要找出所有的结果,所以实际上这里还是不需要停止条件的,不过我们需要在path数组保存到足够大小的排列值时将其保存至res结果数组

代码

代码比较套路就直接贴了

class Solution {
private:
vector<vector<int>> res;
vector<int> path;
void backtracking(vector<int>& nums, vector<bool>& used){
//确定终止条件,无
if(path.size() == nums.size()){//当path保存到足够的元素时,将其存至res
res.push_back(path);
return;
} //确定单层处理逻辑
for(int i = 0; i < nums.size(); ++i){
//判断当前元素是否被使用过
if(used[i] == true) continue;
used[i] = true;//记录当前值
path.push_back(nums[i]);
backtracking(nums, used);
path.pop_back();//回溯
used[i] = false;
}
}
public:
vector<vector<int>> permute(vector<int>& nums) {
vector<bool> used(nums.size(), false);
backtracking(nums, used);
return res;
}
};

注意,本题使用used数组的原因是排列结果中不能重复使用某一数字

例如第一次在[1,2,3]中选了1,此时path数组为[1]

在下一层递归中仍然从[1,2,3]中选数,但是就不能再选1了

面试题 08.07. 无重复字符串的排列组合

无重复字符串的排列组合。编写一种方法,计算某字符串的所有排列组合,字符串每个字符均不相同。

示例1:

输入:S = "qwe"

输出:["qwe", "qew", "wqe", "weq", "ewq", "eqw"]

示例2:

输入:S = "ab"

输出:["ab", "ba"]

提示:

字符都是英文字母。

字符串长度在[1, 9]之间。

思路

就是全排列

代码

tips:字符串也是数组,可以使用push_back()

class Solution {
private:
vector<string> res;
string path = "";//别忘了字符串也是数组
void traversal(string& S, vector<bool>& used){
if(path.size() == S.size()){
res.push_back(path);
return;
}
for(int i = 0; i < S.size(); ++i){
if(used[i] == true) continue;
used[i] = true;
path.push_back(S[i]);
traversal(S, used);
path.pop_back();
used[i] = false;
}
}
public:
vector<string> permutation(string S) {
vector<bool> used(S.size(), false);
traversal(S, used);
return res;
}
};

剑指 Offer 38. 字符串的排列

输入一个字符串,打印出该字符串中字符的所有排列。

你可以以任意顺序返回这个字符串数组,但里面不能有重复元素。

示例:

输入:s = "abc"

输出:["abc","acb","bac","bca","cab","cba"]

限制:

1 <= s 的长度 <= 8

思路

与全排列I一模一样

代码

tips:

1、字符串可以直接作为数组使用

2、在使用s[i] == s[i - 1] && used[i - 1] == 0跳过重复值之后,不要忘记判断当前字符是否已经被使用过

这是为了避免生成重复的排列。

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

全排列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]]

思路

与第一题不同之处是题目给的nums中多了重复值

将例1的结果画出示例图如下:

因此还需要在同层中去重,参考之前的去重操作即可(子集组合总和II

这里也需要在使用nums之前进行排序

代码分析

还是按规矩来写一遍

1、确定回溯函数的参数与返回值

跟上一题一样,不需要beginIndex,只需要used数组进行记录即可

class Solution {
private:
vector<vector<int>> res;
vector<int> path;
//确定回溯函数的参数与返回值
void backtracking(vector<int>& nums, vector<bool>& used){ }
public:
vector<vector<int>> permuteUnique(vector<int>& nums) { }
};

2、确定终止条件

还是跟上一题一样

class Solution {
private:
vector<vector<int>> res;
vector<int> path;
//确定回溯函数的参数与返回值
void backtracking(vector<int>& nums, vector<bool>& used){
//确定终止条件,无
if(path.size() == nums.size()){//当path保存到足够的元素时,将其存至res
res.push_back(path);//此时说明找到了一组
return;
}
}
public:
vector<vector<int>> permuteUnique(vector<int>& nums) { }
};

3、确定单层处理逻辑

在这里需要处理重复值

注意,我们需要处理的重复情况有两种

1、相邻重复值(这个很熟了,见过蛮多次的)

2、单纯的nums里出现多次的值,例如:[1,2,8,2,4],这个2就属于此类重复值

class Solution {
private:
vector<vector<int>> res;
vector<int> path;
//确定回溯函数的参数与返回值
void backtracking(vector<int>& nums, vector<bool>& used){
//确定终止条件,无
if(path.size() == nums.size()){//当path保存到足够的元素时,将其存至res
res.push_back(path);//此时说明找到了一组
return;
}
//确定单层处理逻辑
for(int i = 0; i < nums.size(); ++i){
if(i > 0 && nums[i] == nums[i - 1] && used[i - 1] == 0){
continue;//当前元素重复,跳过
}
used[i] = 1;
path.push_back(nums[i]);
backtracking(nums, used);
path.pop_back();
used[i] = 0;
} }
public:
vector<vector<int>> permuteUnique(vector<int>& nums) {
//先对nums进行排序
sort(nums.begin(), nums.end());
vector<bool> use(nums.size(), false);
backtracking(nums, used);
return res;
}
};

完整代码

在主函数中,需要对使用的nums数组先进行排序

class Solution {
private:
vector<vector<int>> res;
vector<int> path;
//确定回溯函数的参数与返回值
void backtracking(vector<int>& nums, vector<bool>& used){
//确定终止条件,无
if(path.size() == nums.size()){//当path保存到足够的元素时,将其存至res
res.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] == 0){
//找出相邻重复值
continue;//当前元素重复,跳过
}
//判断当前元素(非相邻重复值)是否被使用过
if(used[i] == true) continue;
used[i] = 1;
path.push_back(nums[i]);
backtracking(nums, used);
path.pop_back();
used[i] = 0;
} }
public:
vector<vector<int>> permuteUnique(vector<int>& nums) {
//先对nums进行排序
sort(nums.begin(), nums.end());
vector<bool> used(nums.size(), false);
backtracking(nums, used);
return res;
}
};

同样的题还有:面试题 08.08. 有重复字符串的排列组合

【LeetCode回溯算法#09】全排列,排列问题以及其中涉及的去重操作的更多相关文章

  1. 【LeetCode回溯算法#07】子集问题I+II,巩固解题模板并详解回溯算法中的去重问题

    子集 力扣题目链接 给你一个整数数组 nums ,数组中的元素 互不相同 .返回该数组所有可能的子集(幂集). 解集 不能 包含重复的子集.你可以按 任意顺序 返回解集. 示例 1: 输入:nums ...

  2. 【LeetCode回溯算法#extra01】集合划分问题【火柴拼正方形、划分k个相等子集、公平发饼干】

    火柴拼正方形 https://leetcode.cn/problems/matchsticks-to-square/ 你将得到一个整数数组 matchsticks ,其中 matchsticks[i] ...

  3. leetcode回溯算法--基础难度

    都是直接dfs,算是巩固一下 电话号码的字母组合 给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合. 给出数字到字母的映射如下(与电话按键相同).注意 1 不对应任何字母. 思路 一直 ...

  4. 【LeetCode回溯算法#06】复原IP地址详解(练习如何处理边界条件,判断IP合法性)

    复原IP地址 力扣题目链接(opens new window) 给定一个只包含数字的字符串,复原它并返回所有可能的 IP 地址格式. 有效的 IP 地址 正好由四个整数(每个整数位于 0 到 255 ...

  5. 【LeetCode回溯算法#08】递增子序列,巩固回溯算法中的去重问题

    递增子序列 力扣题目链接(opens new window) 给定一个整型数组, 你的任务是找到所有该数组的递增子序列,递增子序列的长度至少是2. 示例 1: 输入:nums = [4,6,7,7] ...

  6. 【LeetCode回溯算法#10】图解N皇后问题(即回溯算法在二维数组中的应用)

    N皇后 力扣题目链接(opens new window) n 皇后问题 研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击. 给你一个整数 n ,返回所有不同的 n 皇 ...

  7. LeetCode46 回溯算法求全排列,这次是真全排列

    本文始发于个人公众号:TechFlow,原创不易,求个关注 今天是LeetCode的26篇文章,我们来实战一下全排列问题. 在之前的文章当中,我们讲过八皇后.回溯法,也提到了全排列,但是毕竟没有真正写 ...

  8. LeetCode通关:连刷十四题,回溯算法完全攻略

    刷题路线:https://github.com/youngyangyang04/leetcode-master 大家好,我是被算法题虐到泪流满面的老三,只能靠发发文章给自己打气! 这一节,我们来看看回 ...

  9. LeetCode:回溯算法

    回溯算法 这部分主要是学习了 labuladong 公众号中对于回溯算法的讲解 刷了些 leetcode 题,在此做一些记录,不然没几天就忘光光了 总结 概述 回溯是 DFS 中的一种技巧.回溯法采用 ...

  10. [LeetCode] 46. Int数组全排列 ☆☆☆(回溯)

    描述 给定一个没有重复数字的序列,返回其所有可能的全排列. 示例: 输入: [1,2,3]输出:[ [1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2], [3,2, ...

随机推荐

  1. 从DevOps状态报告看技术团队的文化建设

    本文源自一次内部分享,借由此机会又把历年的DevOps状态报告翻看了一遍,其实大多数时候我们对于DevOps的理解都在于流程,工具,实践这些看得见摸得着的东西,但就像文末的几点思考所说的那样,我们一直 ...

  2. js中的宏任务和微任务详细讲解

    微任务有哪些 Promise await和async 宏任务有哪些 setTimeout setInterval DOM事件 AJAX请求 看下面的代码 <script> console. ...

  3. js 删除数组中的某一个内容

    <script type="text/javascript"> let list=["为","啊发大水","阿斯蒂芬& ...

  4. TortoiseGit 常见问题汇总

    1.test分支修改后合并到master分支 1)切换本地分支到master分支 2)TortoiseGit --->  merge,选择远程分支test 提交到远程分支master 2.将远程 ...

  5. word文档删除空白页

    记住两个快捷键 CTRL+Backspace Shift+Backspace 鼠标箭头放在空白的页面 按住键盘上的快捷键 就可以成功删除了不要天天看营销号设置什么磅值,全选删除啥的 效果如下

  6. 虚幻引擎 4 学习笔记 [1] :蓝图编程 Demo

    虚幻引擎 4 学习笔记 [1] :蓝图编程 Demo ​ 最近学习虚幻引擎,主要看的是 Siki 学院的课,课程链接:Unreal蓝图案例 - 基础入门 - SiKi学院|SiKi学堂 - unity ...

  7. navicat连接远程docker中的mysql报错解决

    总是报错 填写主机地址有错误,终于找到方法,是因为docker中的mysql没有设置ip地址,navicat不识别,奉上 https://blog.csdn.net/qq_42838723/artic ...

  8. NLP领域任务如何选择合适预训练模型以及选择合适的方案【规范建议】【ERNIE模型首选】

    1.常见NLP任务 信息抽取:从给定文本中抽取重要的信息,比如时间.地点.人物.事件.原因.结果.数字.日期.货币.专有名词等等.通俗说来,就是要了解谁在什么时候.什么原因.对谁.做了什么事.有什么结 ...

  9. 在K8S中,节点故障驱逐pod过程时间怎么定义?

    在Kubernetes中,节点故障驱逐Pod的过程涉及多个参数和组件的相互作用.以下是该过程的简要概述: 默认设置:在默认配置下,节点故障时,工作负载的调度周期约为6分钟. 关键参数: node-mo ...

  10. Python Fire:自动生成命令行接口

    命令行程序是平时写一些小工具时最常用的方式. 为了让命令行程序更加灵活,我们常常会设置一些参数,根据参数让程序执行不同的功能.这样就不用频繁的修改代码来执行不同的功能. 随着命令行程序功能的丰富,也就 ...