1.全排列
给定一个 没有重复 数字的序列,返回其所有可能的全排列。
示例:
输入: [1,2,3]
输出:
[
[1,2,3],
[1,3,2],
[2,1,3],
[2,3,1],
[3,1,2],
[3,2,1]
]
class Solution {
public:
vector<vector<int>> permute(vector<int>& nums) {
vector<int> path;
vector<vector<int>>res;
dfs(path,res,nums);
return res;
}
void dfs(vector<int> &path,vector<vector<int>>&res,vector<int>& nums){
if(path.size()==nums.size())
res.push_back(path);
for(int i=0;i<nums.size();i++){
vector<int>::iterator it = find(path.begin(),path.end(),nums[i]);
if(it==path.end()){
path.push_back(nums[i]);
dfs(path,res,nums);
path.pop_back();
}
}
}
};
2.全排列二
给定一个可包含重复数字的序列,返回所有不重复的全排列。
示例:
输入: [1,1,2]
输出:
[
[1,1,2],
[1,2,1],
[2,1,1]
]
分析:这题和上一个题的区别在于,出现了重复数字,如果每次的搜索空间还是整个数组,那重复数字会导致重复结果,如图所示,【1(1),1(2),2】和【1(2),1(1),1】其实是一种情况。所以为了避免重复,搜索空间只能是不重复的数字组成,而同时又要考虑到一条路径上可以出现重复,所以使用hash,hash的键值就是搜索空间,value不为0的就可以添加到路径中。
class Solution {
public:
vector<vector<int>> permuteUnique(vector<int>& nums) {
vector<vector<int>>res;
vector<int>path;
map<int,int>wide;
for(int i=0;i<nums.size();i++){
wide[nums[i]]++;
}
dfs(res,path,nums,wide);
return res;
}
void dfs(vector<vector<int>>&res,vector<int>&path,vector<int> nums,map<int,int>&wide){
if(path.size()==nums.size())
res.push_back(path);
map<int,int>::iterator it;
for(it=wide.begin();it!=wide.end();it++){
if(it->second!=0){
path.push_back(it->first);
it->second--;
dfs(res,path,nums,wide);
it->second++;
path.pop_back();
}
}
}
};
3.电话号码的字母组合
给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。
给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。
示例:
输入:“23”
输出:[“ad”, “ae”, “af”, “bd”, “be”, “bf”, “cd”, “ce”, “cf”].
分析:搜索空间是数字对应的字母。这里的回溯要同时回溯路径和当前层数cur。
class Solution {
public:
vector<string> letterCombinations(string digits) {
map<int,string>mp;
vector<string> res;
string path="";
string list="abcdefghijklmnopqrstuvwxyz";
if(digits=="")
return res;
int ind = 0;
for(int i=2;i<7;i++){
mp[i]+=list.substr(ind,ind+3);
ind+=3;
}
mp[7]= "pqrs";
mp[8]= "tuv";
mp[9] = "wxyz";
int cur = 0;//记录层数
dfs(cur,res,path,digits,mp);
return res;
}
void dfs(int cur,vector<string> &res,string path,string digits,map<int,string>mp){
if(cur==digits.size()){
res.push_back(path);
return;
}
int ind = digits[cur]-'0';//得到map的key
int end=3;
if(ind ==7||ind==9) end=4;
for(int i=0;i<end;i++){
path+=mp[ind][i];
dfs(++cur,res,path,digits,mp);
cur--;
path.pop_back();
}
}
};
4.N皇后
给定一个整数 n,返回所有不同的 n 皇后问题的解决方案。
每一种解法包含一个明确的 n 皇后问题的棋子放置方案,该方案中 ‘Q’ 和 ‘.’ 分别代表了皇后和空位。
示例:
输入: 4
输出: [
[".Q…", // 解法 1
“…Q”,
“Q…”,
“…Q.”],
["…Q.", // 解法 2
“Q…”,
“…Q”,
“.Q…”]
]
解释: 4 皇后问题存在两个不同的解法。
n皇后问题就是在n*n的棋盘上放置n个皇后,要求所有皇后不在同一行、列、对角线上。
这个题的复杂之处在于,想要在当前位置放置皇后需要判断这个位置同一行、列、对角线上是否已经有皇后了。
这个问题里搜索树的层数就是棋盘当前的行数,搜索空间是棋盘的列数,只需要考虑当前行之前放置的皇后对这次放置的影响(因为之后的行还未放置)。
class Solution {
public:
/*每个皇后放置的行列斜对角线都不能重复*/
/*
层数:行数
搜索空间:列数
找到解的条件:当前层数已经到达n
*/
vector<vector<string>> solveNQueens(int n) {
vector<vector<string>> res;
vector<string> path;
for(int i=0;i<n;i++){
string tmp = "";
for(int j=0;j<n;j++)
tmp+=".";
path.push_back(tmp);
}
int row =0;
dfs(res,path,row,n);
return res;
}
bool isValid(int col,int row,vector<string> &path,int n){
//检查同一行上是否有皇后
for(int i=0;i<row;i++){
if(path[i][col]=='Q')
return false;
}
//检查主对角线上是否有皇后
for(int i=row-1,j=col-1;i>=0&&j>=0;i--,j--){
if(path[i][j]=='Q')
return false;
}
//检查副对角线上是否有皇后
for(int i=row-1,j=col+1;i>=0&&j<n;i--,j++){
if(path[i][j]=='Q')
return false;
}
return true;
}
void dfs(vector<vector<string>> &res,vector<string> &path,int row,int n){
if(row==n){
res.push_back(path);
return;
}
for(int i=0;i<n;i++){
path[row][i]='Q';
if(isValid(i,row,path,n)){
row++;
dfs(res,path,row,n);
row--;
}
path[row][i]='.';//注意这句要写在if外面,因为不管有没有往下搜索都已经放置皇后了,必须撤销操作
}
}
};
5.总结
回溯法框架:
result = []
def backtrack(路径, 选择列表):
if 满足结束条件:
result.add(路径)
return
for 选择 in 选择列表:
做选择
backtrack(路径, 选择列表)
撤销选择
实质是多叉树的遍历问题。