在数学与计算机科学中,递归是指在函数的定义中使用函数自身的方法。

递归算法是一种直接或者间接地调用自身算法的过程。在计算机编写程序中,递归算法对解决一大类问题是十分有效的,它往往使算法的描述简洁而且易于理解。
递归算法解决问题的特点:
(1) 递归就是在过程或函数里调用自身。
(2) 在使用递归策略时,必须有一个明确的递归结束条件,称为递归出口。
(3) 递归算法解题通常显得很简洁,但递归算法解题的运行效率较低。所以一般不提倡用递归算法设计程序。
(4) 在递归调用的过程当中系统为每一层的返回点、局部量等开辟了栈来存储。递归次数过多容易造成栈溢出等。所以一般不提倡用递归算法设计程序。在实际编程中尤其要注意栈溢出问题。
借助递归方法,我们可以把一个相对复杂的问题转化为一个与原问题相似的规模较小的问题来求解,递归方法只需少量的程序就可描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量。但在带来便捷的同时,也会有一些缺点,也即:通常用递归方法的运行效率不高。
递归算法实例
1.Fibonacci函数
讲到递归,我们最先接触到的一个实例便是斐波那契数列。
斐波那契数列指的是这样一个数列 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, …
特别指出:第0项是0,第1项是第一个1。
这个数列从第二项开始,每一项都等于前两项之和。
斐波那契数列递归法实现:
int Fib(int n)
{
  if(n<1)
  {
return  -1;
  }
  if(n  == 1|| n  == 2)
  {
return 1;
  }
  return Fib(n-1)+Fib(n-2);  }
2.汉诺塔问题
汉诺塔是根据一个传说形成的数学问题:
有三根杆子A,B,C。A杆上有N个(N>1)穿孔圆盘,盘的尺寸由下到上依次变小。要求按下列规则将所有圆盘移至C杆:
每次只能移动一个圆盘;
大盘不能叠在小盘上面。
提示:可将圆盘临时置于B杆,也可将从A杆移出的圆盘重新移回A杆,但都必须遵循上述两条规则。
问:如何移?最少要移动多少次?
最早发明这个问题的人是法国数学家爱德华·卢卡斯。
传说印度某间寺院有三根柱子,上串64个金盘。寺院里的僧侣依照一个古老的预言,以上述规则移动这些盘子;预言说当这些盘子移动完毕,世界就会灭亡。这个传说叫做梵天寺之塔问题(Tower of Brahma puzzle)。但不知道是卢卡斯自创的这个传说,还是他受他人启发。
若传说属实,僧侣们需要264 ? 1步才能完成这个任务;若他们每秒可完成一个盘子的移动,就需要5849亿年才能完成。整个宇宙现在也不过137亿年。
这个传说有若干变体:寺院换成修道院、僧侣换成修士等等。寺院的地点众说纷纭,其中一说是位于越南的河内,所以被命名为“河内塔”。另外亦有“金盘是创世时所造”、“僧侣们每天移动一盘”之类的背景设定。
佛教中确实有“浮屠”(塔)这种建筑;有些浮屠亦遵守上述规则而建。“河内塔”一名可能是由中南半岛在殖民时期传入欧洲的。
以下是汉诺塔问题的递归求解实现:
using namespace std;  
#include <iostream>  
#include <cstdio>   
void hannoi (int n, char from, char buffer, char to)
  {
  if (n  == 1)
  {
  cout  << "Move  disk "  << n  << "  from "  << from  << "  to "  << to  << endl;
  }
  else
  {
  hannoi (n-1, from, to, buffer);
  cout  << "Move  disk "  << n  << "  from "  << from  << "  to "  << to  << endl;
  hannoi (n-1, buffer, from, to);
  }
  }
  int main()
  {
  int n;
  cin  >> n;
  hannoi (n, 'A', 'B', 'C');
  return 0;  }
3.二叉树遍历
在计算机科学中,二叉树是每个节点最多有两个子树的树结构。通常子树被称作“左子树”(left subtree)和“右子树”(right subtree)。
一颗简单的二叉树
二叉树的遍历分为三种:前(先)序、中序、后序遍历。
设L、D、R分别表示二叉树的左子树、根结点和遍历右子树,则先(根)序遍历二叉树的顺序是DLR,中(根)序遍历二叉树的顺序是LDR,后(根)序遍历二叉树的顺序是LRD。还有按层遍历二叉树。这些方法的时间复杂度都是O(n),n为结点个数。
假设我们有一个包含值的value和指向两个子结点的left和right的树结点结构。我们可以写出这样的过程:
先序遍历(递归实现):
    visit(node)
print node.value
if node.left  != null then visit(node.left)
if node.right  != null then visit(node.right)
中序遍历(递归实现):
visit(node)
       if node.left  != null then visit(node.left)
print node.value
if node.right  != null then visit(node.right)
后序遍历(递归实现):
visit(node)
      if node.left  != null then visit(node.left)
      if node.right  != null then visit(node.right)
      print node.value
4.字符串全排列
问题:
写一个函数返回一个串的所有排列。
解析:
对于一个长度为n的串,它的全排列共有A(n, n)=n!种。这个问题也是一个递归的问题, 不过我们可以用不同的思路去理解它。为了方便讲解,假设我们要考察的串是”abc”, 递归函数名叫permu。
思路一:
我们可以把串“abc”中的第0个字符a取出来,然后递归调用permu计算剩余的串“bc” 的排列,得到{bc, cb}。然后再将字符a插入这两个串中的任何一个空位(插空法), 得到最终所有的排列。比如,a插入串bc的所有(3个)空位,得到{abc,bac,bca}。 递归的终止条件是什么呢?当一个串为空,就无法再取出其中的第0个字符了,
 所以此时返回一个空的排列。代码如下:
    typedef vector<string> vs;
vs  permu(string s){
vs result;
if(s  == ""){
result.push_back("");
return result;
}
string c  = s.substr(0, 1);
vs res  = permu(s.substr(1));
for(int i=0; i<res.size();  ++i){
string t  = res[i];
for(int j=0; j<=t.length();  ++j){
string u  = t;
u.insert(j, c);
result.push_back(u);
}
}
return result;
//调用result的拷贝构造函数,返回它的一份copy,然后这个局部变量销毁(与基本类型一样)  }   
思路二:
我们还可以用另一种思路来递归解这个问题。还是针对串“abc”, 我依次取出这个串中的每个字符,然后调用permu去计算剩余串的排列。 然后只需要把取出的字符加到剩余串排列的每个字符前即可。对于这个例子, 程序先取出a,然后计算剩余串的排列得到{bc,cb},然后把a加到它们的前面,得到 {abc,acb};接着取出b,计算剩余串的排列得到{ac,ca},然后把b加到它们前面,
 得到{bac,bca};后面的同理。最后就可以得到“abc”的全序列。代码如下:
    vs  permu1(string s){
vs result;
if(s  == ""){
result.push_back("");
return result;
}
for(int i=0; i<s.length();  ++i){
string c  = s.substr(i, 1);
string t  = s;
vs res  = permu1(t.erase(i, 1));
for(int j=0; j<res.size();  ++j){
result.push_back(c  + res[j]);
}
}
return result;
}
5.八皇后问题
问题:
经典的八皇后问题,即在一个8*8的棋盘上放8个皇后,使得这8个皇后无法互相攻击( 任意2个皇后不能处于同一行,同一列或是对角线上),输出所有可能的摆放情况。
解析:
8皇后是个经典的问题,如果使用暴力法,每个格子都去考虑放皇后与否,一共有264 种可能。所以暴力法并不是个好办法。由于皇后们是不能放在同一行的, 所以我们可以去掉“行”这个因素,即我第1次考虑把皇后放在第1行的某个位置, 第2次放的时候就不用去放在第一行了,因为这样放皇后间是可以互相攻击的。 第2次我就考虑把皇后放在第2行的某个位置,第3次我考虑把皇后放在第3行的某个位置,
 这样依次去递归。每计算1行,递归一次,每次递归里面考虑8列, 即对每一行皇后有8个可能的位置可以放。找到一个与前面行的皇后都不会互相攻击的位置, 然后再递归进入下一行。找到一组可行解即可输出,然后程序回溯去找下一组可靠解。
我们用一个一维数组来表示相应行对应的列,比如c[i]=j表示, 第i行的皇后放在第j列。如果当前行是r,皇后放在哪一列呢?c[r]列。 一共有8列,所以我们要让c[r]依次取第0列,第1列,第2列……一直到第7列, 每取一次我们就去考虑,皇后放的位置会不会和前面已经放了的皇后有冲突。 怎样是有冲突呢?同行,同列,对角线。由于已经不会同行了,所以不用考虑这一点。
 同列:c[r]==c[j]; 同对角线有两种可能,即主对角线方向和副对角线方向。 主对角线方向满足,行之差等于列之差:r-j==c[r]-c[j]; 副对角线方向满足, 行之差等于列之差的相反数:r-j==c[j]-c[r]。 只有满足了当前皇后和前面所有的皇后都不会互相攻击的时候,才能进入下一级递归。
代码如下:
#include <iostream>
using namespace std;
int c[20],n=8,cnt=0;
void print(){
for(int i=0;i<n;  ++i){
for(int j=0; j<n;  ++j){
if(j  == c[i]) cout<<"1  ";
else cout<<"0  ";
}          
cout<<endl;}
cout<<endl;
}
void search(int r){
if(r  == n){
print();
++cnt;
return;
}
for(int i=0; i<n;  ++i){
c[r]  = i;
int ok  = 1;
for(int j=0; j<r;  ++j)
if(c[r]==c[j]  || r-j==c[r]-c[j]  || r-j==c[j]-c[r]){
ok  = 0;
break;
}
if(ok) search(r+1);
}
}
int main(){
search(0);
cout<<cnt<<endl;
return 0;
}

数据结构基础 之 递归算法实例讲解相关推荐

  1. python链表的创建_python数据结构之链表的实例讲解

    在程序中,经常需要将组(通常是同为某个类型的)数据元素作为整体 管理和使,需要创建这种元素组,变量记录它们,传进传出函数等. 组数据中包含的元素个数可能发变化(可以增加或删除元素). 对于这种需求,最 ...

  2. python链表值讲解_python数据结构之链表的实例讲解

    在程序中,经常需要将⼀组(通常是同为某个类型的)数据元素作为整体 管理和使⽤,需要创建这种元素组,⽤变量记录它们,传进传出函数等. ⼀组数据中包含的元素个数可能发⽣变化(可以增加或删除元素). 对于这 ...

  3. C语言全排列递归算法理解,C#算法之全排列递归算法实例讲解

    排列:从n个元素中任取m个元素,并按照一定的顺序进行排列,称为排列: 全排列:当n==m时,称为全排列: 比如:集合{ 1,2,3}的全排列为: { 1 2 3} { 1 3 2 } { 2 1 3 ...

  4. 【Python基础】手把手教你数据可视化!(附实例讲解)

    点击上方"小白学视觉",选择加"星标"或"置顶" 重磅干货,第一时间送达 作者:CrescentAI,华南理工大学,Datawhale优秀学 ...

  5. python创建链表实例_python数据结构链表之单向链表(实例讲解)

    python数据结构链表之单向链表(实例讲解) 单向链表 单向链表也叫单链表,是链表中最简单的一种形式,它的每个节点包含两个域,一个信息域(元素域)和一个链接域.这个链接指向链表中的下一个节点,而最后 ...

  6. VLAN划分基础及实例讲解

    目录 一.VLAN划分基础 1.vlan概念 2.vlan作用 3.vlan分类 4.vlan常用范围 5.vlan接口类型 二.实例讲解 1.实验要求 2.实验准备 3.实验拓扑 4.实验步骤 一. ...

  7. c语言中字符指针变量输入,C语言指针基础知识实例讲解

    对程序进行编译的时候,系统会把变量分配在内存单位中,根据不同的变量类型,分配不同的字节大小.比如int整型变量分配4个字节,char字符型变量分配1个字节等等.被分配在内存的变量,可以通过地址去找到, ...

  8. 【编程实践】Git命令基础教程和代码实例讲解

    Git命令基础教程和代码实例讲解 Git是一个开源的分布式版本控制系统,用于敏捷高效地处理任何或小或大的项目.Git与常用的版本控制工具CVS.Subversion等不同,它采用了分布式版本库的方式. ...

  9. linux常用命令大全(linux基础命令入门到精通+实例讲解+持续更新+命令备忘录+面试复习)

    前言 本文特点 授之以渔:了解命令学习方法.用途:不再死记硬背,拒绝漫无目的: 准确无误:所有命令执行通过(环境为centos7),拒绝复制粘贴: 实用性高:命令多为实际工作中用到的,实例讲解,拒绝纯 ...

最新文章

  1. 一次打流过程的优化反思(iperf3的灵活运用)
  2. 群同态基本定理证明_近世代数(3)——群的基本性质
  3. 结构体名和结构体名是个指针的区别
  4. GDCM:变更dcm文件的序列的测试程序
  5. element Table表格实现前x行恒展示,不会被折叠,设置点击表头按钮排序。
  6. Angular jasmine returnValue方法的实现原理
  7. mysql5.5删除干净_MySql5.5 安装及卸载
  8. Oracle 数据文件(Datafile ) 大小 限制 说明
  9. EXPDP Fails With ORA-04031 (streams pool, ...)
  10. 2010年的读书计划
  11. hackrf+portapack 组装上手体验记录
  12. Oracle技巧之 desc+表名
  13. 搭建fastdfs服务,及单机redis服务,springboot实现h5与fastdfs之间的断点续传,大文件上传,秒传文件和批量上传
  14. 如何从购物数据中挖掘出啤酒与尿布的关联关系?
  15. 30天自制操作系统(day10)
  16. APS生产计划排程系统解决方案,在制造企业中有哪些作用?
  17. 电脑端压缩工具哪个好用
  18. flash怎么强制gc_Adode Flash初级教程
  19. BOOST升压电路参数计算
  20. PowerBuilder/PB常用备忘

热门文章

  1. python getch函数_pyhton 下 使用getch(), 输入字符无需回车
  2. 华为HCIA鲲鹏生态体系介绍
  3. H3C无线控制器AP license共享配置
  4. 使用certbot openresty执行获取 Let’s Encrypt https 免费证书
  5. 鸿蒙升级后桌面背景底纹怎么弄,ps添加背景怎么弄
  6. 【组队学习】【35期】组队学习内容详情
  7. 计算机在军事方面的应用
  8. 苹果开发者账号注册、申请续费整个流程
  9. 静态路由,缺省路由和默认网关的区别
  10. mysql57免安装教程_mysql5.7免安装版配置教程