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

#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]);
}


 

posted on 2008-06-29 18:57  MainTao  阅读(577)  评论(0编辑  收藏  举报