递归的递与归

来源:互联网 发布:Linux 线程 sched_fifo 编辑:程序博客网 时间:2024/05/16 01:51

陆小凤原创


小白:陆大侠,你总算出现了,你在海边捉到了一只海龟?

海归

递归,不是海归。递归是一种很重要的结构与设计思想。

本文企图帮助你理解递归的设计与实现。

(一)递归的表现

有递,有归,才为递归。

日常生活中,有很多递归或不完整递归的表现。

从前有座山,山里有座庙,庙里有个老和尚,老和尚给小和尚讲故事,讲的是从前有座山。。。。A:沙堆是怎么形成的?B:沙堆是一个沙堆加上一粒沙。A:那这一个沙堆是怎么形成的?B:这一个沙堆是一个沙堆加上一粒沙。A:...

递归结构1

f(x) = g(f(x-1))盗梦空间,下去,下去,...,上来,上来,...我回到实现世界了吗?等我转下陀螺!

递归结构2

上海一男子因造谣称自己因造谣而被拘留15日而被拘留15日。一个洋葱就是带着一层洋葱皮的洋葱。

递归结构3

(二)设计与理解递归

小白:递归不就是自己调用自己吗?

递归的表现是调用自己。

但是,要明白,之所以能调用自己,是因为子问题也能用原问题的解决办法。

递归的设计,就是把问题分解成更小的问题,而且更小的问题也能用原问题的解决办法。在问题规模足够小的时候,把它解决掉,再层层返回,层层组合。

要设计或理解好递归,个人需要有较强的能力。

(1)大局观

设计递归算法,需要能从大到小、从全局到局部地去想问题。

(2)乐观

要相信,只要方向对,就一定可以到达终点。

(3)勇气

要有空手套白狼的勇气,先假设这个算法已经能解决此类问题,再让同构的问题大胆调用这个算法。

小白:陆大侠,你怎么说得像心灵鸡汤啊?

陆小凤:对的,这篇文章其实是心灵鸡汤。

(三)实现递归

实现递归时,有几个要点:

(1)只考虑第一层与第二层

请参考“大局观与勇气”一说,你只需要让第二层调用第一层就成功了一半。

(2)什么时候结束递归

把这个实现,就成功了另一半的二分之一。

(3)收尾工作

在每一层递归返回时,要处理什么?想好这个就全部成功了。

(四)示例

递归的使用,到处可见,后续的算法设计上也会不断上演。

先不考虑性能的问题。

这里举两三个简单的例子。

问题1:用递归的设计,输入数字n,打印出1到n的所有数字。

主体:要打印1到n,那先打印1到n-1,再打印一个n就可以了。这个就是主体,只考虑n跟n-1。结束:在n为1时就打印并结束递归。收尾:没什么好收尾的。

代码示例:

void pr(int n) {    if (n == 1) {        printf("1\n");        return;    }    pr(n-1);    printf("%d\n", n);}

效果:

打印1到n

问题2:输出“我当然知道 我知道 我知道 我知道 …我是个sb 这件事 这件事…这件事”,输入n来控制次数。

主体:把“我当然知道”放在递归函数外,因为它不符合同构的原则。先输出“我知道”,再递归到下一层即可。结束:在递减到0时,结束递归。收尾:在递归返回后,输出“这件事”。

代码示例:

#include <stdio.h>void pr(int n) {    if (n == 0) {        printf("[我是sb]");        return;    }    printf("我知道[");    pr(n-1);    printf("这件事]");}int main(int argc, char *argv[]){    printf("我当然知道{");    pr(10);    printf("}\n");    return 0;}

效果:

我知道

问题3:求二叉树的高度(最长路径)。

主体:左子树与右子树的高度的最大值,加1就是当前树的高度。结束:没有子树了。收尾:没有什么好收尾的。

代码示例:

int treeHeight(struct TreeNode* root) {    if (!root) return 0;    int left = treeHeight(root->left);    int right = treeHeight(root->right);    return MAX(left, right) + 1;}

小白:很好理解啊,这些例子只有一个问题,就是:小凤,你是不是把叶孤城KO掉了,怎么不见他出场了?