【LeetCode回溯算法#09】全排列,排列问题以及其中涉及的去重操作
全排列
给定一个不含重复数字的数组 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】全排列,排列问题以及其中涉及的去重操作的更多相关文章
- 【LeetCode回溯算法#07】子集问题I+II,巩固解题模板并详解回溯算法中的去重问题
子集 力扣题目链接 给你一个整数数组 nums ,数组中的元素 互不相同 .返回该数组所有可能的子集(幂集). 解集 不能 包含重复的子集.你可以按 任意顺序 返回解集. 示例 1: 输入:nums ...
- 【LeetCode回溯算法#extra01】集合划分问题【火柴拼正方形、划分k个相等子集、公平发饼干】
火柴拼正方形 https://leetcode.cn/problems/matchsticks-to-square/ 你将得到一个整数数组 matchsticks ,其中 matchsticks[i] ...
- leetcode回溯算法--基础难度
都是直接dfs,算是巩固一下 电话号码的字母组合 给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合. 给出数字到字母的映射如下(与电话按键相同).注意 1 不对应任何字母. 思路 一直 ...
- 【LeetCode回溯算法#06】复原IP地址详解(练习如何处理边界条件,判断IP合法性)
复原IP地址 力扣题目链接(opens new window) 给定一个只包含数字的字符串,复原它并返回所有可能的 IP 地址格式. 有效的 IP 地址 正好由四个整数(每个整数位于 0 到 255 ...
- 【LeetCode回溯算法#08】递增子序列,巩固回溯算法中的去重问题
递增子序列 力扣题目链接(opens new window) 给定一个整型数组, 你的任务是找到所有该数组的递增子序列,递增子序列的长度至少是2. 示例 1: 输入:nums = [4,6,7,7] ...
- 【LeetCode回溯算法#10】图解N皇后问题(即回溯算法在二维数组中的应用)
N皇后 力扣题目链接(opens new window) n 皇后问题 研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击. 给你一个整数 n ,返回所有不同的 n 皇 ...
- LeetCode46 回溯算法求全排列,这次是真全排列
本文始发于个人公众号:TechFlow,原创不易,求个关注 今天是LeetCode的26篇文章,我们来实战一下全排列问题. 在之前的文章当中,我们讲过八皇后.回溯法,也提到了全排列,但是毕竟没有真正写 ...
- LeetCode通关:连刷十四题,回溯算法完全攻略
刷题路线:https://github.com/youngyangyang04/leetcode-master 大家好,我是被算法题虐到泪流满面的老三,只能靠发发文章给自己打气! 这一节,我们来看看回 ...
- LeetCode:回溯算法
回溯算法 这部分主要是学习了 labuladong 公众号中对于回溯算法的讲解 刷了些 leetcode 题,在此做一些记录,不然没几天就忘光光了 总结 概述 回溯是 DFS 中的一种技巧.回溯法采用 ...
- [LeetCode] 46. Int数组全排列 ☆☆☆(回溯)
描述 给定一个没有重复数字的序列,返回其所有可能的全排列. 示例: 输入: [1,2,3]输出:[ [1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2], [3,2, ...
随机推荐
- VCSA 添加新管理用户的方法
VCSA 6.7 添加单独虚拟机管理员账户的方法 最近一次护网工作中,要求升级vCenter的版本, 所以比较无奈进行了相关的升级和设置, 管理员的分发与之前的vcenter的不太一致, 这里面简单记 ...
- 【VictoriaMetrics】一个小优化:循环改查表,性能提升56.48 倍
作者:张富春(ahfuzhang),转载时请注明作者和引用链接,谢谢! cnblogs博客 zhihu Github 公众号:一本正经的瞎扯 做了一个 vm-storage 数据文件 merge 的工 ...
- 【验证码逆向专栏】某验深知 V2 业务风控逆向分析
声明 本文章中所有内容仅供学习交流使用,不用于其他任何目的,不提供完整代码,抓包内容.敏感网址.数据接口等均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关! 本文章未经许 ...
- 从零开始配置 vim(10)——快捷键配置
之前我们对neovim 进行了基础的配置,这篇主要介绍我比较常用的快捷键配置.到这篇开始我们的配置已经可以为两个大的模块--基础配置和快捷键配置.我们的目录也应该按照模块来进行组织.在正式配置之前让我 ...
- 7.2 通过API创建新进程
创建新的进程是Windows程序开发的重要部分,它可以用于实现许多功能,例如进程间通信.并行处理等.其中,常用的三种创建进程的方式分别是WinExec().ShellExecute()和CreateP ...
- 配置VSFTP文件服务器
FTP 文件传输协议.用于互联网上的控制文件的双向传输,使用FTP来传输时,其实是具有一定程度的危险性,因为数据在因特网上面是完全没有受到保护的明文传输方式,VSFTP是一个基于GPL发布的类Unix ...
- 物联网浏览器(IoTBrowser)-Modbus协议集成和测试
Modbus协议在应用中一般用来与PLC或者其他硬件设备通讯,Modbus集成到IoTBrowser使用串口插件模式开发,不同的是采用命令函数,具体可以参考前面几篇文章.目前示例实现了Modbus-R ...
- Windows7 SP1 安装.NET Framework4失败
系统版本是Windows7旗舰版,已经安装了SP1补丁,但还是无法安装.NET Framework4或者4.5,提示安装失败. 这时可以安装.NET Framework4.8的开发包,我这里安装开发包 ...
- 使用DoraCloud构建远程办公桌面云
公司总部在上海.员工分布在各地.部分员工需要远程办公.为了实现远程办公,有几种备选方案. 方案1.在员工的PC上安装向日葵.ToDesk之类的远程工具. 方案2.公司总部提供VPN,员工通过VPN拨号 ...
- shell find 根据时间获取文件列表
根据时间得到文件,可以使用find进行查找,支持查找: find以时间为条件查找可用选项: -amin n:查找n分钟以前被访问过的所有文件. -atime n:查找n天以前被访问过的所有文件. -c ...