递归

 

什么样的问题可以用递归?
其实很多问题都可以用递归解决,例如数列的求和:

#include  < iostream >
using   namespace  std;

template 
< class  T >
T    recrusive_sum(T a[], 
int  idx)
{
    
if (idx  ==   0 //  退出条件
         return  a[ 0 ];
    
else
        
return  a[idx]  +  recrusive_sum(a, idx  -   1 );  //  使用内层返回的结果
}

int  main( void )
{
    
const   int  elem_cnt  =   100 ;
    
int  a[elem_cnt];
    
for ( int  i  =   0 ; i  <  elem_cnt;  ++ i)
        a[i] 
=  i  +   1 ;
    cout 
<<  recrusive_sum(a, elem_cnt  -   1 <<  endl;
    
return   0 ;
}

显然,这不是一个高效的算法,我们通常用率更高的迭代法来解决上面的问题。用这个例子只是想说明,很多问题可以用递归解决。
能用递归解决的问题通常具有两个特点:
1  有退出条件
2  外层需要用到内层算出的结果(也可能是内层需要外层的计算结果,但比较少见)
最难的地方是找出外层利用内层结果的方法,这往往需要在思考问题的过程中发现规律,纸笔是不可缺少的。
另外退出条件需要拿捏准确,这也是一个容易出错的地方。

下面是求全排列和求全部子集的算法,注意以上两点在代码中的体现。

(*) 求全排列
不妨写出一个简单例子,我们用P(a,b,c)表示a,b,c的全排列,
则P(a,b,c)=
a b c
a c b
b a c
b c a
c a b
c b a
我们发现,以上结果按首字母可划分为三组,它们是
a + P(b,c)
b + P(a,c)
c + P(a,b)
其实就是第一个字母轮换,其余两个位置是剩下两个字母的全排列。“剩下两个字母的全排列”正是我们可以利用的内层结果。
代码如下:

#include  < iostream >
using   namespace  std;

template 
< class  T >
void     perm(T list[],  int  begin,  int  end)
{
    
if (begin  ==  end){
        
for ( int  i  =   0 ; i  <=  end;  ++ i)
            cout 
<<  list[i]  <<   ' /t ' ;
        cout 
<<  endl;
    }
    
else {
        
for ( int  i  =  begin; i  <=  end;  ++ i){
            swap(list[begin], list[i]);            
            perm(list, begin 
+   1 , end);            
            swap(list[begin], list[i]);
        }
    }
}

int  main()
{
    
char  a[]  =  { ' 1 ' ' 2 ' ' 3 ' ' 4 ' };
    
int  min_idx  =   0 ;
    
int  max_idx  =   sizeof  a  /   sizeof   * -   1 ;
    perm(a, min_idx, max_idx);
    
return   0 ;
}


(*) 求所有子集

不妨写一个简单的例子,然后从中发现规律。例如,集合{a,b,c}的子集有:
{}
{a}
{b}
{a, b}
{c}
{a, c}
{b, c}
{a, b, c}
耐心分析结果,发现:
{}                                (0)
{a} = {a} + {}                    (a)
{b} = {b} + {}                    (b)
{a, b} = {b} + {} + {a}           (b)
{c} = {c} + {}                    (c)
{a, c} = {c} + {a}                (c)
{b, c} = {c} + {b}                (c)
{a, b, c} = {c} + {a, b}          (c)
其实我们能从上面的分析过程中得到不只一条结论:
1 所有的子集总数是二项展开式系数和C(n,0)+C(n,1)+...+C(n,n)=2^n.这个结论虽然对解决本题没什么帮助,但它应该,也是最容易被注意到的。
2 我们将上面的所有子集分组,发现从最简单的空集开始,新出现的组都是新元素和之前所有组的笛卡尔积。这正是递归利用内层计算结果的地方。代码如下:

#include  < iostream >
#include 
< vector >
#include 
< string >
using   namespace  std;

template 
< class  T >
void     subset(vector <  vector < T >   >&  res,  const  vector < T >&  src,  int  idx);

template 
< class  T >
void     show_1d( const  vector < T >&  vec);

template 
< class  T >
void     show_2d( const  vector <  vector < T >   >&  vec);

template 
< class  T >
void     append_cartesian(vector <  vector < T >   >&  res, T appendant);

int  main()
{
    vector
< string >         src;
    vector
<  vector < string >   >     res;
    
    src.push_back(
" c " );
    src.push_back(
" b " );
    src.push_back(
" a " );

    subset(res, src, 
0 );

    show_2d(res);
    
return   0 ;
}

template 
< class  T >
void     subset(vector <  vector < T >   >&  res,  const  vector < T >&  src,  int  idx)
{
    
if (src.size()  ==  idx){        
        vector
< T >  empty_set;
        res.push_back(empty_set);
//  append an empty vector when reach base
    }
    
else {
        subset(res, src, idx 
+   1 );
        append_cartesian(res, src[idx]);
// 将内层算出的结果笛卡尔积到本层
    }
}

template 
< class  T >
void     append_cartesian(vector <  vector < T >   >&  res, T appendant)
{
    
int  len  =  res.size();
    
for ( int  i  =   0 ; i  <  len;  ++ i){
        vector
< T >  tmp  =  res[i];
        tmp.push_back(appendant);
        res.push_back(tmp);
    }
}

template 
< class  T >
void     show_1d( const  vector < T >&  vec)
{
    cout 
<<   ' { ' ;
    
for ( int  i  =   0 ; i  <  vec.size();  ++ i){
        cout 
<<  vec[i];
        
if (i  !=  vec.size()  -   1 )
            cout 
<<   " " ;
    }
    cout 
<<   ' } '   <<  endl;
}

template 
< class  T >
void     show_2d( const  vector <  vector < T >   >&  vec)
{
    
for ( int  i  =   0 ; i  <  vec.size();  ++ i)
        show_1d(vec[i]);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值