1. 程式人生 > >回溯法求解全排列問題(可去除重複排列)

回溯法求解全排列問題(可去除重複排列)

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)範圍的元素置逆(顛倒排列)