回溯法也就是我们所说的dfs,可以系统地搜索一个问题的所有解或任意解。基本的含义是按照深度优先的策略,从根节点出发寻找解空间,在到达最底端时进行回溯并判断,继续向下搜索。
一般回溯法可以画一个完全二叉树就可以形象的表示。
回溯算法一般以递归实现,有模板如下:
void dfs(int cur)
{
if(到达出口){
输出解;//result.add(路径)->如果不要求打印要求返回
return;
}
for(int i=0 ;i<方案数; i++)//选择列表
{
if(方案可行){
做选择//这里一般都是借用一个vis数组记录下标是否已被选择
dfs(cur+1);
撤销选择//回溯的基本
}
}
}
在进行全排列时,我们可以方便的调用系统函数next_permutation进行实现。
next_permutation(first,last)
将[first,last]范围内的元素重新排列为下一个按字典顺序更大的排列中。这里字典序就是从小到大进行排序。
例如: 1 2 3 的下一个字典序为 1 3 2 下一个字典序为 2 1 3 ,2 3 1 …
成功返回true,失败(也就是说已经是最大了 如3 2 1 )则返回false
给定一个没有重复数字的序列,返回其所有可能的全排列。
第一种方法:调用系统函数next_permutation(first,last)
这里要注意一点是,我们不知道给的序列是否排好序,因为next_permutation(first,last) 是按字典序的,所以之前必须先用sort进行排序
//要求打印
void permutation(vector<int>& ar)
{
sort(ar.begin(), ar.end());
do {
for (int i = 0; i <ar.size();i++ )
cout << ar[i] << " ";
cout << endl;
} while (next_permutation(ar.begin(), ar.end()));
}
//要求返回
vector<vector<int>> permutation(vector<int>& ar)
{
sort(ar.begin(), ar.end());
vector<vector<int>> ans;
do {
ans.push_back(ar);
} while (next_permutation(ar.begin(), ar.end()));
return ans;
}
第二种方法: 递归实现
我们发现 1 2 3 全排列是
1 2 3, 1 3 2
2 1 3 , 2 3 1
3 1 2 , 3 2 1
要么1开头,剩下的做全排列,要么2开头(swap(1,2)),剩下的做全排列,要么3开头(swap(1,3),剩下的做全排列。明显就是一个递归
void Recpermutation(vector<int>& ar,int p,int q)
{
if (p == q) //打印
{
for (int i = 0; i < ar.size(); i++)
cout << ar[i] << " ";
cout << endl;
}
else
fo(int i=p ; i<= q; p++)
{
std::swap(ar[i], ar[p]);//先交换
Recpermutation(ar, p + 1, q);
std::swap(ar[i], ar[p]);//在交换回来进行下一次递归(2开头)
}
}
int main()
{
vector<int> ar = {1,2,3,4};
Recpermutation(ar,0,3);
}
第三种方案,dfs,所以需要一个vis[n]数组记录选择,已经选的=1,否则为0
vector<int> vis;
vector<int> nu;
vector<int> ret;
vector<vector<int>> ans;
int n;
void dfs(int cur)
{
if (cur == n)
{
ans.push_back(ret);
return;
}
for (int i = 0; i < n; i++)
{
if (vis[i] == 0) {
vis[i] = 1;//开始选择
ret[cur] = nu[i];//记录已经做的选择
dfs(cur + 1);//递归
vis[i] = 0;//撤销选择
}
}
}
vector<vector<int>> Dfspermutation(vector<int>& ar)//深度优先遍历
{
n = ar.size();
vis.assign(n, 0);
ret.assign(n, 0);
nu = ar;
dfs(0);
}