回溯法求解全排列问题(可去除重复排列)

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)范围的元素置逆(颠倒排列)

    原文作者:回溯法
    原文地址: https://blog.csdn.net/Newbie006/article/details/57581768
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞