这是我参与2022首次更文挑战的第26天,活动详情查看:2022首次更文挑战」。
我个人觉得要想进大厂,就必须学习了解算法和数据结构,经历了多次面试之后,也知道了算法和数据结构的重要性,所以,我也打算从今天开始,进入学习,下载了一下电子书,边看书,边跟着书的例子来敲一下代码,下面就开始总结一下对算法和数据结构的学习吧。
第二十六天:学习递归
递归
在学习树和图数据结构之前,我们先了解递归,让之后的学习边得更简单
递归是一个种解决问题的方法,从解决小问题开始,直到解决最初的大问题。递归通常设计函数自调用。
计算一个数的阶乘
-
如果不使用递归
function factorialIterative(number) { if(number < 0) return undefined let total = 1 for(let i = number; i > 0; i--) { total *= i } return total }首先是合法性判断,然后进行循环,循环次数是传入的参数,计算出总值。
-
使用递归
function factorial(n) { if(n === 1 || n === 0) { return 1 } return n * function(n - 1) }如果传入的是1或者是0,返回1,否则继续调用自身,参数传入n-1,乘以n。
调用栈
前面我们学了栈结构,递归就是使用它的例子,每当一个函数被调用时,该函数就会进入调用栈的顶部,每当使用时,就会从栈顶移除。
用浏览器查看调用栈的行为
在return 1 那里打上断点,可以看到Call Stack上有五个factorial函数的调用,如果一个个往下走的话,会看到Call Stack开始弹出factorial的调用。
js调用栈大小的限制
如果递归没有结束条件,就会一直执行下去,然后浏览器就会抛出错误(stack overflow error)
根据操作系统和浏览器的不同,具体执行次数会有所不同,但区别不大。
斐波那契数列
它是一个由 0、1、1、2、3、5、8、13、21、 34等数组成的序列
-
不使用递归
function fibonacciIterative(n) { if(n < 0) return 0 if(n <= 2) return 1 let f1 = 0, f2 = 1, fx for(let i = 2; i < n; i++) { fx = f1 + f2 f1 = f2 f2 = fx } return fx }先初始化前两个值,然后进入循环,fx是一个中间值,将f1和f2相加给这个中间值,然后移动f1的位置,将fx的值给f2
-
使用递归
function fibonacci(n) { if(n < 1) return 0 if(n === 1 || n === 2) { return 1 } return fibonacci(n) + fibonacci(n - 1) }可以用下图解释上面的调用过程
-
记忆化斐波那契数
可以看到上面的执行,fibonace(3)执行了两次,因此可以将它的结果存起来,当需要再次计算它的时候,直接使用它的结果
function fibonacciMemoization(n) { const memo = [0, 1]; const fibonacci = (n) => { if (memo[n] != null) return memo[n]; memo[n] = fibonacci(n - 1) + fibonacci(n - 2) return memo[n] } return fibonacci }
为什么使用递归
使用递归并不是说它执行得更快,但递归版本更容易理解,通常代码也更少。