c印记(四): 递归
来源:互联网 发布:mmd模型动作数据 编辑:程序博客网 时间:2024/05/16 19:30
目录
- 目录
- 前因
- 概述
- 例子
- 总结
1. 前因
前几天闲来无事就在csdn学院上转了转,然后看了一下尹成的c语言视频教程,虽然是讲的是c语言基础 ,但是讲的过程中会时不时的讲一些c语言的知识点对应到实际应用中是什么情况的。感觉还可以,于是乎就瞄了几眼,主要是看了递归,数组以及劫持这几块东西。说实话,虽然工作也有五六年了,但感觉在工作的工程中并未用到什么很”牛X”的算法,以前也没有怎么研究过递归这玩意儿。看了尹成讲的递归之后,感觉递归还是有点儿意思,所以就准备写点儿东西记录一下,不管是回顾也好,还是欣赏也罢。
2. 概述
基于以上的前因,顺播和度娘深度交流了一番,于是乎就有了这段记录。
咱首先还是教科书一点,将递归的概念摆出来(这里主要是指编程领域中的概念,百科上直接拿的):程序调用自身的编程技巧称为递归( recursion)。
知道这个概念之后,再进一步查询了一下,这递归还要分很多种类:
- 线性递归: 即一般型的递归,自身调用自身,这其中又可以分为以下几类:
- 直接递归: 函数A嵌套调用函数A自身
- 间接递归:函数A调用函数B,然后在函数B中再调用函数A
-
- 尾递归: 尾递归就是从最后开始计算, 每递归一次就算出相应的结果, 也就是说, 函数调用出现在调用者函数的尾部, 因为是尾部, 所以根本没有必要去保存任何局部变量. 直接让被调用的函数返回时越过调用者, 返回到调用者的调用者去.
- 递归树:就是成树状的递归调用(如斐波那契数列)
3. 例子
这里先不管线性递归还是尾递归,就列几个例子出来。
最简单的就拿个比较入门级的问题,从1加到100,即1+2+..+100,当然这里不会去使用n(n+1)/2这个公式,就是一般的累加实现,最直接的方法,也是初学时容易想到的方法——直接循环累加,其代码如下:
int sum = 0; int i; for (i = 1; i <= 100; i++) { sum += i; }
接下来,看看使用递归方式该如何实现,提到递归,就不得不说说数学归纳法了,其实从问题到解法实现的过程就是,首先使用数学归纳法分析并总结出一半规律和基本条件,然后再根据这个一般规律去
实现递归主体部分,而基本条件作为递归的结束条件。按照这个顺序,这里首先就是用数学归纳法来分析从1累加到一般的一般规律:
大家都知道1累加到100的结果是5050,这里:
5050 = 1+2+…+98+99+100//依次类推
4950 = 1+2+…+98+99
4851 = 1+2+…+98
由此可知,f(100) = 4950+100=f(99) + 100,同理,f(99) = 4851+99=f(98)+99,由此归纳可知f(n) = f(n-1)+n,当然这里是从最后的结论反向归纳的,也可以从正向归纳:
0+1 = 1
1+2 = 3
3+3 = 6
6+4 = 10
10+5 = 15,由此就有,
f(1) = 0+1 = f(0)+1,
f(2) = 1+2 = f(1)+2,
f(3) = 3+3 = f(2)+3,
f(4) = 6+4 = f(3)+4,
f(5) = 10+5 = f(4)+5,
也能得到f(n)=f(n-1)+n,其递归的结束条件这里就取 n = 0(当然也可以取n=100,只是递归的写法会 有些不太一样), 其源码如下:
int recursion_add(int n){ if (0 == n)//end condition { return n; } else { return recursion_add(n - 1) + n; }}
以上就是一般递归的实现,在前面的概述中还提到了尾递归,算是对一般递归的一种优化实现,这里也
顺便将尾递归实现1累加到100的函数贴出来:
//这里就有两个参数,后面一个acc即是一个参数,也是用来存储结算结果的,调用//的时候,acc对应的实参应该为0,即 int sum = trail_recursion_add(100, 0)int trail_recursion_add(int n, int acc) { if (0 == n)//end condition { return acc; } else { return recursion_add(n - 1, acc + n); }}
还有一个比较有趣的例子,之所以有趣是因为,只需要稍微修改,其运行结果就是相反的,这个例子就是递归方式显示数组中的元素,一般的做法就是使用循环,其代码如下:
int a[10] = {0,1,2,3,4,5,6,7,8,9}; int i; for (i=0; i<10; i++) { printf("%d ", a[i]); }
同样我们来看看递归方式的实现:
/** *这里以一个拥有10个元素的数组为例子,其调用方式为show(a, 10); */void show(int a[10], int n){ if (0 == n) //end condition,index range is: 0~9 { return; } else { show(a, n - 1); printf("%d ", a[n-1]); //先递归后打印,为顺序输出 }}
然后如果要逆序输出数组的元素的话,只需要将printf语句移动一下就可以了:
/** *这里以一个拥有10个元素的数组为例子,其调用方式为show(a, 10); */void show_reverse_order(int a[10], int n){ if (0 == n) //end condition,index range is: 0~9 { return; } else { printf("%d ", a[n-1]); //先打印后递归,为逆序输出。 show(a, n - 1); }}
4. 总结
递归的例子还有很多很多,比如计算阶乘,打印斐波那契数列,实现汉诺塔等等,这里就不再一一列举了。我觉得尹成在教程中说的好,递归是非常锻炼逻辑思维的,而且是非常直观的逻辑思维。咱们搞软件的就是需要比较严密的逻辑思维,在软件设计阶段就需要以直观的逻辑思维去对各种需求,约束条件等进行综合,然后得出方案,而不能一开始,还未得出初步的方案,就开始去考虑如何如何优化了,这样可能反而得不到较好的结果。
另外一点就是,我觉得,不管是初学者还是老鸟,都应该时不时的回顾一下一些基础知识,说不定就会有一些意想不到的收获,正所谓,温故而知新。
- c印记(四): 递归
- c印记(六): 数组与递归联合应用的小游戏
- c印记(三): my_oopc
- c印记(五):数组
- c印记(一):面向对象
- c印记(二):lw_oopc简介
- c印记(七): ini file解析
- c印记(八): ring buffer实现
- 高朋网欲洗去Groupon印记:四位外籍高管将离职
- C语言函数与递归四
- C语言函数与递归四
- c印记(十一): 单向链表 list原理与实现
- c印记(十二):队列queue原理与实现
- c印记(十四):跨平台线程封装
- 先留个印记
- 留点印记
- 印记基因
- 人生印记
- Vue+webpack+node.js实现价格监测应用Ponitor
- FZU 2140 Forever 0.5
- android的Environment类 .
- iOS category内部实现原理
- python线程池技术
- c印记(四): 递归
- 只能在执行Render()的过程中调用RegisterForEventValidation
- JSP——cooike和session
- 深入理解SVM之对偶问题
- CS-50 第三四节总结
- Struts2上传和下载 commons-fileupload-1.2.1.jar
- centOS目录结构
- AndroidStudio导入项目一直卡在Building gradle project info最快速解决方案
- leetcode 034 Search for a Range