递归

原创 2008年10月03日 10:28:00
 

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

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

如何去理解递归,想到递归,运用递归

递归:        首先要明白栈的知识,“后进后出” 显示通用树   文本的方式显示一颗树   A  ---------B  ----------------------E...
  • u011237814
  • u011237814
  • 2013年08月24日 22:44
  • 2000

【算法】递归与while循环的通俗比较

前言 小结:至于两者的转换,又是另一个问题了。...
  • qsbbl
  • qsbbl
  • 2017年08月05日 21:45
  • 377

汇编语言学习系列 递归实现

假如汇编语言要实现如下C语言的功能,编译环境Ubuntu14.04(32位)。 #include int refact(int n){ if(n == 1) return ...
  • csujiangyu
  • csujiangyu
  • 2015年03月01日 14:04
  • 1541

递归集与递归可枚举集

递归函数(一):开篇 递归函数(二):编写递归函数的思路和技巧 递归函数(三):归纳原理 递归函数(四):全函数与计算的可终止性 递归函数(五):递归集与递归可枚举集 递归函数(六):最多有...
  • qq_36510261
  • qq_36510261
  • 2017年03月29日 22:02
  • 651

递归算法的经典运用

递归(recursion):程序调用自身的编程技巧 递归满足两个条件: (1)有反复执行的过程(调用自身) (2)有跳出反复执行过程的条件(递归出口)递归例子(常用的地方): (1)阶乘 n...
  • OREO_GO
  • OREO_GO
  • 2016年04月01日 09:54
  • 2968

Sql递归(用with 实现递归查询)

用with代码段实现递归查询,效率高
  • lqh4188
  • lqh4188
  • 2016年07月19日 14:17
  • 14017

汇编递归过程

汇编的递归过程习惯写C程序后,一般很少会去敲汇编代码,最近在研究系统学习方面知识,需要用到汇编,考虑到编译器是如何将C程序编译成汇编代码的,这里对于C语言中的语句(赋值,运算,选择,循环)就不着重介绍...
  • u010865478
  • u010865478
  • 2017年03月28日 16:07
  • 1128

Java中的递归原理分析

参加工作已经三四年了,再回头来看这些很基础的东西,觉得理解又深入了一层! 解释:程序调用自身的编程技巧叫做递归。 程序调用自身的编程技巧称为递归( recursion)。递归做为一种算法在程序设...
  • it_wangxiangpan
  • it_wangxiangpan
  • 2013年01月08日 01:26
  • 21600

线性递归与尾递归

在数据结构与算法中,包括在做《剑指OFFER》的过程中,很多时候都会用到递归,之前对尾递归概念模模糊糊的,看了很多前辈写的博客,这里对其做点总结。 1.递归:在计算机科学领域中,递归是通过递归函数来...
  • tomcmd
  • tomcmd
  • 2015年08月25日 10:36
  • 901

递归写法总结

递归写法总结 递归是算法中的一种很重要思想。好的递归程序逻辑清楚,代码简洁,有时候时间上也非常高效;此外链表、二叉树等结构用递归算法一般都有鲜明优势。 往往递归问题口头说起来感觉十分清晰顺畅;而用代...
  • jack_thu
  • jack_thu
  • 2015年05月18日 13:07
  • 1542
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:递归
举报原因:
原因补充:

(最多只允许输入30个字)