第九章 递归

  • A+
所属分类:Web前端
摘要

本篇文章的测试用例及调试方法见前言递归是一种解决问题的方法,他从解决问题的各个小部分开始,直到解决最初的大问题.递归通常涉及函数调用自身.


自我测试

本篇文章的测试用例及调试方法见前言

说明

递归是一种解决问题的方法,他从解决问题的各个小部分开始,直到解决最初的大问题.递归通常涉及函数调用自身.

const test = (i) => {     console.log(i)     test(++i);   } 

这是一种明显的递归,自己调用自己

function test2(i){     console.log("test2: " + i++)     test(i) }  function test(i){     console.log("test: " + i++)     test2(i) }   

这个也是一种递归,通过两个方法之间的相互调用,然后实现递归.但是这种代码我们一般是不会直接拿去运行的.为什么呢???

相信你也看出来了,上面的递归写法有问题,我调用我自己,然后自己调用自己,然后自己又调用了自己......,形成了一个闭环,所以递归的最重要的一个点出现了 基线条件(跳出循环的条件)

第九章 递归

调用栈(这个对后面分析递归很重要)

每当一个函数被一个算法调用时,该函数会进入调用栈的顶部.当使用递归的时候,每个函数调用都会堆叠在调用栈的顶部,这是因为每个调用都可能依赖前一个调用的结果

第九章 递归

尾部调用优化(ES2015新知识点)

案例

let i = 0; function recursiveFn(){     i++; 	recursiveFn(); } try{     recursiveFn(); }catch(ex){     console.log(ex) } 

如果函数内的最后一个操作是调用函数(如案例),会通过"跳转指令"(jump)而不是"子程序调用"(subroutine call)来控制.也就是说,在ES2015中,这里的代码可以一直执行下去.因此,具有停止递归的基线条件非常重要尾调用优化更多信息

练习

阶乘

说明:

5的阶乘: 5 * 4 * 3 * 2 * 1

9的阶乘: 9 * 8 * 7 * 6 * 5 * 4 * 3 * 2 * 1

代码

function recursionFactorial(num: number): number {     //找到结束条件     if (num === 1) {         return 1;     }     return num * recursionFactorial(num - 1); } 

还是一定要找到 基线条件,这个是递归的必要条件,不然你的程序会死循环,然后卡死

图解

第九章 递归

是否还喜欢这种图解,如果喜欢,可以进入下一篇章第十章 树,带你走进树的遍历

斐波那契数列

说明

0 1 1 2 3 5 8 13 21 34 ....

第1个数: 0

第2个数: 1

第3个数 = 第2个数 + 第1个数

第4个数 = 第2个数 + 第3个数

第5个数 = 第4个数 + 第3个数

........

图解

第九章 递归

代码

/*求n个斐波那契数的和*/ function newRecursionSequence(n: number): number {     if (n === 0) return 0;     if (n <= 2) return 1     return newRecursionSequence(n - 1) + newRecursionSequence(n - 2); } 

优化版代码

function newNewRecursionSequence(n: number) {     let memo: Array<number> = [0, 1];     let fbnc = (n: number): number => {         if (memo[n] != null) return memo[n];         //新算出的值保存在memo数组里         return memo[n] = fbnc(n - 1) + fbnc(n - 2);     }     return fbnc(n); } 

优化版对比第一版好在对数据进行了缓存,这样就不用反复的计算数据(图解中可以看到我们反复计算了n为 3 ,2等时的值)