经典算法题2:递归和字典序的全排列算法

来源:互联网 发布:skynet windows 编辑:程序博客网 时间:2024/05/21 06:30

1,递归的全排列算法

      以ABCD为例:

      对于ABCD,首先以第一位的字符分为四类:

     (1)以A为第一位 ABCD  固定A的位置,A后面跟着BCD的全排列

     (2)以B为第一位 BACD (即交换第一个字符A和B的位置),固定B的位置,B后面跟着ACD的全排列

     (3)以C为第一位 CBAD (即交换第一个字符A和C的位置),固定C的位置,C后面跟着BAD的全排列

     (4)以D为第一位 DBCA (即交换第一个字符A和D的位置),固定D的位置,D后面跟着BCA的全排列 

     

        同样的,对于(1)中BCD、(2)中的ACD、(3)中的BAD、(4)中的的BCA的全排列,分别按照上面的形式递归求解。


代码如下:

#include <iostream>using namespace std;template<typename T>void permutation(T array[], int start, int end){    int i;    if(array==NULL||start<0||end<0||start>end)        return;   //reach the end, print array[]    if(start == end){        for(i = 0; i <= end; ++i){            cout<<array[i]<<" ";        }        cout<<endl;        return;    }    for(i = start; i <= end; ++i) {        //调换第i个位置与第一个位置        swap(array[i], array[start]);        permutation(array, start + 1, end);       //恢复数组        swap(array[i], array[start]);    }}int main(int argc, char **argv){    char arr[4] = {'A', 'B', 'C', 'D'};    int length=sizeof(arr) / sizeof(arr[0]) - 1;    permutation(arr, 0, length) ;    return 0;}

2, 字典序全排列算法

字典序排列算法的核心是要找到所有比当前序列大的最小的那个序列,也就是刚好比当前序列大的那个序列。例如1234的下一个字典序为1243,1243刚好比1234大一些。

字典序的实现如下:
设P是1~n的一个全排列:p=p1p2......pn=p1p2......pj-1pjpj+1......pk-1pkpk+1......pn
1)从排列的右端开始,找出第一个比右边数字小的数字的序号j(j从左端开始计算),即 j=max{i|pi<pi+1}
2)在pj的右边的数字中,找出所有比pj大的数中最小的数字pk,即 k=max{i|pi>pj}(右边的数从右至左是递增的,因此k是所有大于pj的数字中序号最大者)
3)对换pj,pk
4)再将pj+1......pk-1pkpk+1......pn倒转得到排列p'=p1p2.....pj-1pjpn.....pk+1pkpk-1.....pj+1,这就是排列p的下一个排列。

举个栗子:

有数列:23541,要找出它的下一个字典序,具体步骤如下:

1)从右端数字1开始,找到第一个比相邻的右边小的数字,这个数字为3,记下3在数列中的位置 j=1(最左边从0开始数)

2)找出3右边的数字中,第一个比3大的数字,这个数为4,记下这个数的位置 k=3

3)交换位置 j 的数字(3)和位置k 的数字(4),数列变为24531

4)反转位置 j后面的数列(531),最终数列变为24135

24135即为原数列23541的下一个字典序排列。


注意,如果原始序列为54321,已经为最大排列,那么按照上面的方法,是找不到下一个字典序的排列的。

所以,首先要对原始序列进行预处理,有两种方法:

方法1,利用快速排序将原始序列按升序排序,这样就得到最小的排列Pmin

方法2,判断原始序列是不是最大排列Pmax,若是,反转Pmax,使其成为Pmin


实现代码:

//交换数组s中下标为x和y的两个元素值Swap(char *s,int x, int y){   char temp=s[x];    s[x]=s[y];    s[y]=temp;}//反转序列Reverse(char *s,int x, int y){for(;x<y;x++,y--){Swap(s,x,y);}}//快速排序void Qsort(char *s,int low,int high){if(low>=high)return;int start = low;int end = high;int key = s[start];while(start!=end){while(start<end&&s[end]>=key){end--;}s[start]=s[end];while(start<end&&s[start]<=key){start++;}s[end]=s[start];}s[start]=key;Qsort(s,low,start-1);Qsort(s,end+1,high);}bool next_permutation(char *s,int len)int start=len-1;int end=len-1;                //s[start-1]>=s[start]中的等号是为了避免序列中有重复的字符而导致的重复的排列        while(start>0&&s[start-1]>=s[start]){start--;}if(start==0)return false;while(end>start-1&&s[start-1]>=s[end]){end--;}Swap(s,start-1,end);Reverse(s,start,len-1);return true;}void permutation(char *s,int len){//快速排序,使原序列为最小一个排列PminQsort(s,0,len-1);       /*       //也可以不使用Qsort对原序列进行排序,首先判断原序列是否是最大的排列Pmax,如果是的话,反转原序列int boolvalue=0;for(int i=0;i<len-1;i++){if(s[i]<s[i+1]){boolvalue=1;break;}}if(boolvalue==0)Reverse(s,0,len-1);       */ do{     for(int i=0;i<len;i++) cout<<s[i];     cout<<endl; }while(next_permutation(s,len));}int main() {char s[]="12345";        int len=strlen(s);permutation(s,len);return 0;}

3,使用C++的STL来实现全排列

如果不想自己写全排列的代码的话,可以直接使用C++的STL中的函数next_permutation. 它的作用是判断一个序列,若存在下一个字典序排列,那么就返回true并产生这个排列,否则返回false。

#include <iostream>  #include <algorithm>  using namespace std;    template <typename BidirectionalIterator>  void permutation(BidirectionalIterator array, int len)  {      sort(array, array + len);      do{          for(int i = 0; i < len; ++i){              cout<<array[i]<<" ";          }          cout<<endl;      }while(next_permutation(array, array + len));  }    int main()  {   char s[]="12345";    int len=strlen(s);
    permutation(a, len); return 0; 
}


参考文章:http://blog.csdn.net/hackbuteer1/article/details/6657435


0 0
原创粉丝点击