欢迎来真孝善网,为您提供真孝善正能量书籍故事!

编程基础:深入浅出递归入门教程

时间:11-21 名人轶事 提交错误

大家好,今天来为大家解答编程基础:深入浅出递归入门教程这个问题的一些问题点,包括也一样很多人还不知道,因此呢,今天就来为大家分析分析,现在让我们一起来看看吧!如果解决了您的问题,还望您关注下本站哦,谢谢~

简介

此卡的目的是回答以下问题:

什么是递归?递归是如何工作的?如何递归地解决问题?如何分析递归算法的时间复杂度和空间复杂度?如何更好地应用递归?

递归的原理

每次递归函数调用自身时,都会将给定问题转化为子问题。递归过程一直持续到子问题可以直接求解而无需进一步递归为止。

递归函数避免无限递归的必要属性:

递归结束条件(base cases) 一组规则(recurrence relation),可以将所有其他情况还原为基本情况。递归函数可以从多个位置调用自身。

例子 翻转字符串

无效printReverse(const char *str) {

if (!*str) 返回; //基本情况

printReverse(str + 1);

putchar(*str);

}

递归函数

如果一个问题有递归解法,我们可以按照以下步骤来实现。

我们定义这个问题可以通过函数F(x) 来实现,其中X 是函数的输入,代表问题的范围。

在函数F(X)中,我们实现了以下步骤:

将问题拆分为更小的范围,。递归调用函数F(x_1),F(x_2),F(x_n)来求解X的子问题,最后对子问题的解进行处理,组合成X对应的解.

Recurrence Relation

定义:问题的解决方案与其子问题的解决方案之间的关系。

例子:Pascal"s Triangle

定义:阳辉三角形是一系列数字组成的三角形。在杨辉的三角形中,每行最左边和最右边的数字总是1。对于其余的数字,每个数字是上一行中它正上方的2个数字之和。

用数学公式来表达,递归关系是

基本情况是:

其中,代表第i行第j个数字。

Memoization 备忘录

递归过程中重复的计算

递归解通常非常符合直觉容易编码。但大多数时候,在递归过程中,重复计算会导致性能损失。

备忘录法(Memoization)是避免重复计算的通用技术。

是的,这个词拼写正确,它不是Memorization。

定义:为了避免重复计算,我们可以将中间子问题的结果存储在缓存中,以便再次使用时不需要再次计算。

memo的实现可以通过hashmap来实现。特别是在OOP中,一般的Memoization可以使用装饰器来实现。

例子 斐波那契数

斐波那契数的解法有很多种,包括Binets法公式法,时间复杂度为O(log n),非常令人印象深刻。

Complexity Analysis 复杂度分析

递归算法的复杂度有时并不明显,需要通过一些例程来分析。

尾递归是一种特殊的技术,可以减少递归堆栈的使用并优化空间复杂度,使其与迭代算法相同。

Time Complexity 时间复杂度

递归算法的时间复杂度为:

O(T)=R * O(s),

其中,R为递归调用次数,O(s)为每次递归调用产生的计算复杂度。

一般来说,R比较难计算,O(s)的计算和非递归算法的时间复杂度分析是一样的。

借助执行树技术,我们可以更好地分析递归调用的次数。

执行树是展示特定情况下递归调用流程的树。每个节点代表一次调用,节点上的值代表调用的参数。

这棵树的节点数为R。

使用Memoization技术时需要特别注意执行树的变化。

Space Complexity 空间复杂度

递归算法的空间占用主要分为2部分:

递归相关非递归相关

recursion related

我们研究过编译原理的人都知道,每次函数调用都要压入栈:

函数的返回地址、函数参数和函数的局部变量。递归算法的函数调用栈的深度是从初始情况到基本情况。

non-recursion related

全局变量使用的空间主要分配在堆上。例如,用于记忆的哈希图。

Tail Recursion 尾递归

尾递归是一种递归调用,是递归函数的最后一条指令,并且函数中只有一次递归调用。

尾递归的一个很好的例子。请注意,non_tail_recursion 在最后一次递归调用后仍会计算。

公共类主要{

私有静态int helper_non_tail_recursion(int start, int [] ls) {

if (开始=ls.length) {

返回0;

}

//不是尾递归,因为它在递归调用返回后进行一些计算。

返回ls[start] + helper_non_tail_recursion(start+1, ls);

}

公共静态int sum_non_tail_recursion(int [] ls) {

if (ls==null || ls.length==0) {

返回0;

}

返回helper_non_tail_recursion(0, ls);

}

//------------------------------------------------

私有静态int helper_tail_recursion(int start, int [] ls, int acc) {

if (开始=ls.length) {

返回帐户;

}

//这是尾递归,因为最后的指令是递归调用。

返回helper_tail_recursion(start+1, ls, acc+ls[start]);

}

公共静态int sum_tail_recursion(int [] ls) {

if (ls==null || ls.length==0) {

返回0;

}

返回helper_tail_recursion(0, ls, 0);

}

}尾递归消除递归栈的原理:

编译器知道从被调用者返回后,会立即返回,不需要重用函数调用堆栈中保存的数据。只需要一个堆栈帧。所有层共享一个堆栈帧,因此返回时可以跳过整个递归堆栈。

并非所有语言编译器都支持尾递归优化。例如,支持C和C++,但不支持Python和Java。

尾递归通常也不那么容易实现。需要

递归调用只出现在最后一条指令中。如果需要调用多个函数或者计算返回值,尾递归是不可能的。

而且细心的同学可以发现尾递归和迭代(循环)的相似之处。事实上,有些函数式编程语言甚至不支持循环,只有递归、迭代才能完全实现。因为我们平时用循环居多,尾递归很少。如果需要写尾递归(通常是一些面试需求),可以先写循环版本的代码。然后尝试将局部变量更新更改为尾递归中的参数,通常可以编写优雅(但对大多数人来说可读性不太好)的尾递归代码。

用户评论

一别经年

递归?听得挺高端的,不过有点害怕深入了解呢。

    有16位网友表示赞同!

蹂躏少女

终于有机会学习一下递归了,一直听别人说很有用的概念

    有6位网友表示赞同!

单身i

想做一些算法设计,感觉递归是必备技能啊!

    有11位网友表示赞同!

闲肆

刚开始学编程,不知道递归是什么,希望能解释得通俗易懂。

    有14位网友表示赞同!

发型不乱一切好办

之前看到过递归的例子,还挺神奇的感觉

    有10位网友表示赞同!

心安i

感觉递归像是在玩层层剥壳一样,很有趣

    有8位网友表示赞同!

寻鱼水之欢

希望这篇文章能让我彻底明白递归的原理,而不是只是知道它是做什么用

    有7位网友表示赞同!

爱你的小笨蛋

我以前一直搞不懂为什么要学习递归,现在看来确实很强大,可以解决很多问题的。

    有18位网友表示赞同!

男神大妈

看了一些递归的例子,感觉应用场景蛮广的啊!

    有12位网友表示赞同!

(り。薆情海

准备好好学习一下递归,希望能写出像大神们一样优秀的代码

    有10位网友表示赞同!

来自火星的我

这篇文章选题刚好,我最近也想入门递归。

    有20位网友表示赞同!

秒淘你心窝

以前在考试的时候总看到递归,却从来没认真学过...

    有19位网友表示赞同!

莫飞霜

期待一篇深入浅出的介绍递归的文章,把概念解释清楚!

    有11位网友表示赞同!

妄灸

学习编程就 gotta learn recursion!

    有8位网友表示赞同!

花花世界总是那么虚伪﹌

想把递归应用到我的项目中去。

    有16位网友表示赞同!

沐晴つ

感觉递归是一种很有逻辑思维挑战的编程方式。

    有20位网友表示赞同!

一样剩余

希望入门学习能够变得轻松愉快!

    有12位网友表示赞同!

颜洛殇

感谢提供这个主题的文章,我很需要它!

    有20位网友表示赞同!

【编程基础:深入浅出递归入门教程】相关文章:

1.蛤蟆讨媳妇【哈尼族民间故事】

2.米颠拜石

3.王羲之临池学书

4.清代敢于创新的“浓墨宰相”——刘墉

5.“巧取豪夺”的由来--米芾逸事

6.荒唐洁癖 惜砚如身(米芾逸事)

7.拜石为兄--米芾逸事

8.郑板桥轶事十则

9.王献之被公主抢亲后的悲惨人生

10.史上真实张三丰:在棺材中竟神奇复活