递归问题与递归结构(三)

来源:互联网 发布:淘宝取件通知改回短信 编辑:程序博客网 时间:2024/05/21 22:48

排列(Permutations)问题

        一个典型的排列问题,输入一个字符串,比如“abcd”,要求输出这4个字符组成的所有字符串。输入字符串长度若为n,输出字符串个数就为n!。用递归方法求得所有输出结果,可以这样来考虑:

        从长度为n的输入字符串中选择任一字符作为第一个输出字符,共有n种选法,每种选法都会导致不同的输出结果(这一点与子集(组合)问题不同,在n选k的组合问题中,第一个选a与第一个选b可能得到的是同一个输出结果,比如“ab”与“ba”就是相同的结果),因而需要逐一偿试并得到相应结果。第二个字符需要在剩余的n-1个字符中选取,共有n-1种选法,在第一个字符相同的情况下,这n-1种选择又会导致n-1种不同的输出结果,因而需要逐一偿试并得到相应结果。递归结构找到。由于每确定一个输出字符,问题规模就会减1,所以最后会落到Base Case n=0 中。给出代码:

void   RecPermute( string  soFar,  string rest)

{

      if (rest == "")

      {

             cont << soFar << endl;

      }

     else

     {

            for( int  i=0; i<rest.length(); i++)

            {

                  string  next = soFar + rest[i];    // 取出第i个字符,加入到输出队列中

                  string  remaining = rest.Substr(0, i)     // 从rest中第1个字符开始,取出长度为 i 的子字符串(即:0 ~ i-1 )。

                                                     +  rest.Substr(i+1);  //  取出以rest中第i 个字符的下一个字符作为首字符的子字符串

                 RecPermute(next,  remaining);

            }

     }

}


这个问题还可以变一变,比如给出n个字符,要求从中任取k个字符作为输出字符串,要求输出所有可能的结果。


另一个典型的排列问题是“旅行者问题”,一个旅行者如何才能在最短行程里游遍事先给定的n 座城市。


排列问题和子集(组合)问题相似的地方:

  用分治原则来解决排列和子集(组合)问题,其过程都可以形成一棵递归树。递归树的每一个结点都是一个选择(choice或称decision);每作出一次选择问题规模都会递减;每个结点的度表示在当前选择下,后续可能的选择个数;递归树的高表示由输出到得到所有输出结果中的一个输出结果要作出的选择次数。

排列问题和子集(组合)问题不同的地方:

  排列问题中,每分离出一个元素后,对剩余元素的分离需要逐一枚举,因为它们会导致不同的输出结果。而在组合问题中,对元素的枚举分离,会导致重复的输出结果(参见之前的讨论),因此,组合问题分离一个元素的目的是为了能构建一个集合,使得递归树中的每一次选择(结点)都对应这样一个问题,被分离的元素包含在此集合还是不包含在此集合中。正是由于这样的不同,使得解决排列与组合的递归函数在结构上也有所差别。排列问题需要逐一枚举,因而,很容易在它的Recursive Case中出现循环结构,组合问题的每一次递归要回答是包括还是不包括的问题,因而不太会在它的Recursive Case部分出现循环结构(这里讨论的循环结构,主要是指用于缩减问题规模的结构,而不是指用于其他目的的循环结构)。当然,也会有例外,比如当排列问题的可选项小于等于两项时,也可以不用循环结构。


  因此,当我们用分治法大致对问题作出判断后,在写出具体代码前,就可以想象出相应代码的大致形式。这对于我们最终写出递归函数是有帮助的。




原创粉丝点击