回溯法
*
- 字符串的排列和数字的排列都属于回溯的经典问题
- 回溯算法框架:解决一个问题,实际上就是一个决策树的遍历过程:
-
- 路径:做出的选择
-
- 选择列表:当前可以做的选择
-
- 结束条件:到达决策树底层,无法再做选择的条件
- 伪代码:
- result = []
- def backtrack(路径,选择列表):
-
if 满足结束条件:
-
result.add(路径)
-
return
-
for 选择 in 选择列表:
-
做选择
-
backtrack(路径,选择列表)
-
撤销选择
- 核心是for循环中的递归,在递归调用之前“做选择”,
- 在递归调用之后“撤销选择”。
- 字符串的排列可以抽象为一棵决策树:
-
[ ]
-
[a] [b] [c]
-
[ab] [ac] [bc] [ba] [ca] [cb]
-
[abc] [acb] [bca] [bac] [cab] [cba]
- 考虑字符重复情况:
-
[ ]
-
[a] [a] [c]
-
[aa] [ac] [ac] [aa] [ca] [ca]
-
[aac] [aca] [aca] [aac] [caa] [caa]
- 字符串在做排列时,等于从a字符开始,对决策树进行遍历,
- "a"就是路径,“b”"c"是"a"的选择列表,"ab"和"ac"就是做出的选择,
- “结束条件”是遍历到树的底层,此处为选择列表为空。
- 本题定义backtrack函数像一个指针,在树上遍历,
- 同时维护每个点的属性,每当走到树的底层,其“路径”就是一个全排列。
class Solution {
public:
vector<string> permutation(string s)
{
set<string>res;
backfind(s,0,res);
return vector<string>(res.begin(),res.end());
}
void backfind(string s,int start,set<string> &res)
{
if(start==s.size())
{res.insert(s);
return;}
for(int i=start;i<s.size();i++)
{
swap(s[i],s[start]);
backfind(s,start+1,res);
swap(s[i],s[start]);
}
}
};