排列组合问题
排列组合问题
1、子集问题
1.1、求数字的所有子集。
题目链接
给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。
说明:解集不能包含重复的子集。
输入: nums = [1,2,3]
输出:
[
[3],
[1],
[2],
[1,2,3],
[1,3],
[2,3],
[1,2],
[]
]
1.2、思路
解决一个回溯问题,实际上就是一个决策树的遍历过程。你只需要思考 3 个问题:
- 1、路径:也就是已经做出的选择。
- 2、选择列表:也就是你当前可以做的选择。
- 3、结束条件:也就是到达决策树底层,无法再做选择的条件。
fun backtrack(路径, 选择列表):
if 满足结束条件:
result.add(路径)
return
for 选择 in 选择列表:
做选择
backtrack(路径, 选择列表)
撤销选择
写 backtrack 函数时,需要维护走过的「路径」和当前可以做的「选择列表」,当触发「结束条件」时,将「路径」记入结果集。
下面对子集说明:
画出递归树:
1、找结束条件
所有路径都应该加入结果集,所以不存在结束条件。或者说当start参数越过数组边界
的时候,程序就自己跳过下一层递归了,因此不需要手写结束条件,直接加入结果集
res为结果集
res.push_back(path);//把每一条路径加入结果集
2、选择列表,做出选择
for(int i=start;i<nums.size();i++)
{
track.push_back(nums[i]);//做出选择
backtrack(nums,path,i+1);//递归进入下一层,注意i+1,标识下一个选择列表的开始位置,最重要的一步
track.pop_back();//撤销选择
}
重点
子集、组合类问题,关键是用一个start参数来控制选择列表
①画出递归树,找到状态变量(回溯函数的参数),这一步非常重要※
②根据题意,确立结束条件
③找准选择列表(与函数参数相关),与第一步紧密关联※
④判断是否需要剪枝
⑤作出选择,递归调用,进入下一层
⑥撤销选择
1.3、题解
vector<vector<int>> res;
vector<vector<int>> subsets(vector<int>& nums) {
// 记录走过的路径
vector<int> track;
backtrack(nums, 0, track);
return res;
}
void backtrack(vector<int>& nums, int start, vector<int>& track) {
res.push_back(track);
// 注意 i 从 start 开始递增
for (int i = start; i < nums.size(); i++) {
// 做选择
track.push_back(nums[i]);
// 回溯
backtrack(nums, i + 1, track);
// 撤销选择
track.pop_back();
}
}
2、组合问题
2.1、 k 个数的组合
题目链接
给定两个整数 n 和 k,返回 1 … n 中所有可能的 k 个数的组合。
输入: n = 4, k = 2
输出:
[
[2,4],
[3,4],
[2,3],
[1,2],
[1,3],
[1,4],
]
2.2、思路
参考上面的子集
2.3、题解
class Solution {
public:
vector<vector<int>>res;
vector<vector<int>> combine(int n, int k) {
if (k <= 0 || n <= 0) return res;
vector<int> track;
backtrack(n, k, 1, track);
return res;
}
void backtrack(int n, int k, int start, vector<int>& track) {
// 到达树的底部
if (k == track.size()) {
res.push_back(track);
return;
}
// 注意 i 从 start 开始递增
for (int i = start; i <= n; i++) {
// 做选择
track.push_back(i);
backtrack(n, k, i + 1, track);
// 撤销选择
track.pop_back();
}
}
};
3、排列
3.1、无重复字符串的排列
题目链接
无重复字符串的排列组合。编写一种方法,计算某字符串的所有排列组合,字符串每个字符均不相同。
示例1:
输入:S = "qwe"
输出:["qwe", "qew", "wqe", "weq", "ewq", "eqw"]
示例2:
输入:S = "ab"
输出:["ab", "ba"]
提示:
1、字符都是英文字母。
2、字符串长度在[1, 9]之间。
3.1.1、思路
1、递归树
首先,我们固定1,然后只有2、3可选:如果选2,那就只剩3可选,得出结果[1,2,3];如果选3,那就只剩2可选,得出结果[1,3,2]。再来,如果固定2,那么只有1,3可选:如果选1,那就只剩3,得出结果[2,1,3]…
- 如果我们
固定了(选择了)某个数
,那么他的下一层的选择列表就是——除去这个数以外的其他数
!!
2、结束条件
if(path.size()==nums.size())
{
res.push_back(path);
return;
}
3、选择列表、作出选择
for(int i=0;i<nums.size();i++)
{
if(!used[i])//从给定的数中除去用过的,就是当前的选择列表
{
path.push_back(nums[i]);//做选择
used[i]=true;//设置当前数已用
backtrack(nums,used,path);//进入下一层
used[i]=false;//撤销选择
path.pop_back();//撤销选择
}
}
重点
“排列”类型问题和“子集、组合”问题不同在于:“排列”问题使用used数组来标识选择列表
,而“子集、组合”问题则使用start参数。
3.1.2、题解
class Solution {
public:
vector<string>res;
vector<string> permutation(string S) {
if(S.size()==0)
return{};
string temp="";
vector<bool>used(S.size());
backtrack(S,temp,used);
return res;
}
void backtrack(string s,string& path,vector<bool>& used)//used数组
{
if(path.size()==s.size())
{
res.push_back(path);
return;
}
for(int i=0;i<s.size();i++)
{
if(!used[i])
{
path.push_back(s[i]);
used[i]=true;
backtrack(s,path,used);
used[i]=false;
path.pop_back();
}
}
}
};
3.2、有重复字符串的排列组合
原题链接
有重复字符串的排列组合。编写一种方法,计算某字符串的所有排列组合。
示例1:
输入:S = "qqe"
输出:["eqq","qeq","qqe"]
示例2:
输入:S = "ab"
输出:["ab", "ba"]
提示:
- 1、字符都是英文字母。
- 2、字符串长度在[1, 9]之间。
3.2.1、思路
1、递归树
- 1、首先要对给出的nums数组排序,让重复的元素并列排在一起,
- 2、添加剪枝条件:
if(i>0&&nums[i]==nums[i-1]&&!used[i-1])
,当i可以选第一个元素之后的元素时,然后判断当前元素是否和上一个元素相同?- 如果相同,再判断上一个元素是否能用?如果三个条件都满足,那么该分支一定是重复的,应该剪去
3.2.2、题解
//vector<string>res为全局变量,表示最终的结果集,最后要返回的
class Solution {
public:
vector<string>res;
vector<string> permutation(string s)
{
if(s.size()==0)
return{};
string temp="";
sort(s.begin(),s.end());
vector<bool>used(s.size());
backtrack(s,temp,used);
return res;
}
void backtrack(string s,string& path,vector<bool>& used)//used数组
{
if(path.size()==s.size())
{
res.push_back(path);
return;
}
for(int i=0;i<s.size();i++)
{
if(!used[i])
{
if(i>=1&&s[i-1]==s[i]&&!used[i-1])//判重剪枝
continue;
path.push_back(s[i]);
used[i]=true;
backtrack(s,path,used);
used[i]=false;
path.pop_back();
}
}
}
};
参考
1、https://leetcode-cn.com/problems/subsets/solution/c-zong-jie-liao-hui-su-wen-ti-lei-xing-dai-ni-gao-/