回溯法求解全排列问题(可去除重复排列)
1. 回溯法使用标记法求解
include <cstdio>
include <algorithm>
using namespace std;
int vis[100]; //标记数组,直接判断不需要循环判断,循环见全排列2.cpp
int re[]= {2,1,2}; //给出数据
int ans[100];//下标记录
void dfs(int cur,int border)//可以去除重复排列,必须所给集合升序且正整数
{
int i,j,jl;
if(cur==border) //有满足条件的一组解
{
for(i=0; i<border; i++)
printf("%d ",re[ans[i]]);
putchar('\n');
}
else
{
jl=0; //记录上一次这个节点的数值
for(j=0; j<border; j++)
if(!vis[j] && re[j]>jl) //如果本次这个节点的值大于上一次就一定不重复
{
ans[cur]=j;
jl=re[j]; //更新节点数值
vis[j]=1; //标记走过的点
dfs(cur+1,border); //满足条件进入下一层
vis[j]=0; //递归返回的时候还原最初状态,有标记的一步一定要记住还原
}
}
}
int main(void)
{
sort(re,re+3); //必须升序,排序算法才能保证不重复
dfs(0,3);
return 0;
}
2. 回溯法不使用标记数组,利用循环判断是否可行
#include <cstdio>
#include <algorithm> //这个全排列不使用标记数组
using namespace std;
int ans[100];
int re[]= {1,2,1};
void dfs(int cur,int border)
{
int i,j,k,ok,jl;
if(cur>=border)
{
for(k=0; k<border; k++)
printf("%d ",re[ans[k]]);
putchar('\n');
}
else
{
jl=0;
for(j=0; j<border; j++)
{
ok=1; //先认为是有效值下面用循环进行检测是否有效
ans[cur]=j;
for(i=0; i<cur; i++)
if(j==ans[i])
{
ok=0;
break;
}
if(ok && re[j]>jl)
{
jl=re[j];
dfs(cur+1,border);
}
}
}
}
int main(void)
{
sort(re,re+3);
dfs(0,3);
return 0;
}
3. STL库next_permulation()实现
template<typename T>
bool nextPermulation(T begin, T end) //数据开始地址,结束地址
{
if(begin==end) return false;//空序列
if(end==begin+1) return false;//一个元素
T i=end-1;
while(1)
{
T t=i;
i--;
if(*i < *t)
{
T j=end;
while(!(*i < *--j));
iter_swap(i,j);
reverse(t,end);
return true;
}
if(i==begin)
{
reverse(begin,end); //已经为最大序列 例如:321
return false;
}
}
}
1、从尾部开始往前寻找两个相邻的元素第1个元素i,第2个元素j(从前往后数的),且 i < j
2、再从尾往前找第一个大于i的元素k。将i、k**对调**
3、[j,last)范围的元素置逆(颠倒排列)