算法学习(一) 全排列的几种递归算法

      全排列是算法学习的一个初级问题,也是近几年IT公司比较热衷的问题。最近因为一个朋友的实际问题用到了类似全排列的算法,所以把相关的代码总结一下。

一、问题描述

      全排列的问题非常简单,比如给定三个数字1、2、3,请将三个数字的所有排列组合按大小顺序给出。这样我们期待的结果就是:123,132,213,231,312,321

二、第一种递归算法分析

      对于给定的n个数字,显然有n!种排列方式。关键在于怎样将所有的排列得到,一种显然的方式是首先选出第一位上的数字,然后回溯选择第二位上的数字,然后是第三位……只需要确保每一位上选择的数字不重复就可以了。这种算法比较好理解,递归也比较好设计,先上代码(c++):

 1 #include<iostream>
 2 #include<fstream>
 3 using namespace std;
 4 
 5 ifstream fin;
 6 ofstream fout;
 7 int flags[100];
 8 int numbers[100];
 9 int n;
10 
11 void search(int loc){
12     if(loc==n){
13         fout<<numbers[0]+1;
14         for(int i=1;i!=n;i++)
15             fout<<","<<numbers[i]+1;
16         fout<<endl;
17     }else{
18         for(int i=0;i!=n;i++)
19             if(flags[i]){
20                 flags[i]=0;
21                 numbers[loc]=i;
22                 search(loc+1);
23                 flags[i]=1;
24             }
25     }
26 }
27 
28 int main(){
29     
30     //fin.open("input.txt",ios::in);
31     fout.open("QPL.txt",ios::out);
32     
33     cin>>n;
34     for(int i=0;i!=n;i++)
35         flags[i]=1;
36     search(0);
37 }

      算法的关键就是search函数的实现。首先是退出条件的确定,search函数从左到右每个坑里填一个数字,当loc==n的时候填完所有的坑,这样一个排列便完成了。然后是往每个坑里填数字的过程,就是那个for循环,对于每个坑数字从0到n填,同时判定没有重复使用。

      这种实现的好处是比较直观,可拓展性比较强。大家可以试一下修改边界条件的判定标准会有什么效果。

三、第二种递归算法分析

      网上还有这样一种看似简洁的递归算法。算法通过观察全排列的产生方式,以123为例,他的全排列为123,132,213,231,312,321.通过观察可以发现,123的全排列就是1+(23的全排列)加上2+(13的全排列)加上3+(12的全排列)这里的递归的设计就是1+perm(23)然后交换1和2再执行2+perm(13)然后交换2和3执行3+perm(23)。

      代码设计如下:

 1 #include <iostream>
 2 #include <fstream>
 3 #include <stdlib.h>
 4 
 5 using namespace std;
 6 
 7 void swap(char *a,char *b)
 8 {
 9     char temp;
10     temp=*a;
11     *a=*b;
12     *b=temp;
13 }
14 
15 void Perm(char *pszStr, int k, int m)
16 {
17     if (k == m)
18     {
19         static int s_i = 1;
20         cout<<"the "<<s_i ++<<" line"<<pszStr<<endl;
21     }
22     else
23     {
24         for (int i = k; i <= m; i++) //第i个数分别与它后面的数字交换就能得到新的排列
25         {
26             swap(pszStr + k, pszStr + i);
27             Perm(pszStr, k + 1, m);
28             swap(pszStr + k, pszStr + i);//恢复现场
29         }
30     }
31 }
32 
33 int main(int argc, const char * argv[])
34 {
35     char str[]="1234";
36     Perm(str,0, 3);
37     return 0;
38 }

 

posted @ 2014-08-09 20:20  福尔摩斯の子弟  阅读(4126)  评论(0编辑  收藏  举报