全排列的递归与非递归实现

问题:输入一个序列(元素无重复),输出其全排列

一般采用经典的递归解法,后来想将其改造为非递归代码,思考很久后觉得并不好写,手工模拟递归栈的行为容易出错。然后上网搜索了一下众网友的非递归代码,发现很多人的非递归代码是各种全新的求解算法,而不是相同算法的非递归实现,和我想要的不一样。

递归解法:

假设输入序列[0,1,2,3],将其分解为4个子问题

0+[1,2,3],

1+[0,2,3],

2+[0,1,3],

3+[0,1,2],这样每个子问题的规模减小了1,一直递归下去直到无法再分解。

 1 //对a[idx]~a[n-1]的元素进行全排列,原地排列

 2 //第一个参数为序列集合,第二个为序列长度,第三个为当前子问题(子序列)的起始位置

 3 void recursivePermutation(int a[],int n,int idx)

 4 {

 5     if(idx == n-1) //递归结束条件

 6     {

 7         for(int i=0; i < n;++i)

 8         {

 9             printf("%d",a[i]);

10         }

11         printf("\n");

12         return;

13     }

14     assert(idx < n-1);

15     for(int i=idx; i < n; ++i)

16     {

17         std::swap(a[idx],a[i]);

18         recursivePermutation(a,n,idx+1);

19         std::swap(a[idx],a[i]);

20     }

21 }

非递归: 

需要一个数据结构来模拟调用栈,std::vector是一个不错的选择,其长度相当于递归深度,每个元素用来保存每次递归调用中的循环变量i。

代码流程来说,最外层需要一个无脑的循环,里面需要

1、判断某个排列是否结束,并输出排列值

2、判断当前子问题是否已经解决,如果结束则返回上一层子问题,如果未结束则进入下一个子问题

 1 void permutation(int a[],int n) //假设无重复

 2 {

 3     vector<int> idx(1,0); //模拟递归算法中每个迭代的循环idx,其长度等于递归算法中的递归深度

 4     while(1)

 5     {

 6         if(idx.size()==n)

 7         {

 8             for(int i=0; i < n;++i)

 9             {

10                 printf("%d",a[i]);

11             }

12             printf("\n");

13             idx.back()++; //idx+1,与下面的if配合,回退到上一个迭代

14         }

15         

16         if(idx.back() < n) //模拟递归算法中的循环判断条件

17         {

18             int i = idx.size()-1;

19             std::swap(a[i],a[idx.back()]);

20             idx.push_back(idx.size()); //idx size增加,模拟进入下一层递归

21         }

22         else

23         {

24             idx.pop_back(); //回退到上一层递归

25             if(idx.size()==0){break;}

26             int i =idx.size()-1;

27             std::swap( a[i],a[idx.back()]);

28             idx.back()++; //模拟递归算法中循环i的递增

29         }

30     };

31     

32 }

 

你可能感兴趣的:(全排列)