递归算法
利用递归实现斐波那契数列
function fib(n) {
if (n <= 2) { return n > 0 ? 1 : 0 }
return fib(n - 2) + fib(n - 1)
}
// 1 1 2 3 5
// 等价表达式: n = (n - 2) + (n - 1)
通过函数调用栈理解递归
递归是一个逆推导的过程,实际上利用了数据结构中的栈结构。在JavaScript调用函数时会创建该函数执行上下文并将其推入一个栈中,当该函数执行完成且内部再无其他调用函数再按照先进后出的顺序出栈。
// 来看一个斐波那契数列的例子,其中fib(1)和fib(2)都等于1
fib(5) // n = 5,函数首次调用入栈
fib(5) = fib(3) + fib(4) // 第二次入栈
fib(1) + fib(2) | fib(2) + fib(3) // 第三次入栈
fib(1) + fib(2) // 第四次入栈
+ 1 + 1 // 第一次出栈
1 + 1 | 1 + 2 // 第二次出栈
fib(5) = 2 + 3 // 第三次出栈
fib(5) => 5 // 最后一次出栈,fib(5)返回结果5
构成递归算法的必要条件
-
等价表达式:可以根据递归函数返回的结果构建等价表达式。
-
递归结束条件:必须存在一个终止递归条件,否则会出现溢栈问题。
分析方法:基于最小的递归单元,通过递归函数构造等价关系式即可。
迭代算法
利用迭代算法实现斐波那契数列。代码如下:
function fib(n) {
if (n <= 3) { // 以下三元表达式是一个递进的关系
return n === 3 ? 2 : n <= 2 && n > 0 ? 1 : 0
}
let [x, y, z] = [1, 1, 2] // 初始迭代变量
for (let i = 0; i < n - 3; i++) {
;[x, y] = [y, z] // 注意代码风格问题,一般加个分号
z = x + y
}
return z
}
// x y z // 上一步,旧值
// 1 1 2 3 5
// x y z // 下一步,新值
// 迭代变量:x, y, z
// 迭代关系式: x = y; y = z; z = x + y;
再来看一个查找二叉树最大值的例子。代码如下:
function maxNode(node) {
while (node.right !== null) {
node = node.right
}
return node.value
}
// node node.right // 上一步,旧值
// 6 7 8
// node node.right // 下一步,新值
// 迭代变量: node, node.right(当前节点的右节点)
// 迭代关系式: node = node.right
构建迭代算法的必要条件
-
存在迭代变量,至少存在一个间接或间接地不断由旧值递推出新值的变量。
-
存在迭代关系式,即如何从变量的前一个值推出其下一个值的公式。
-
存在迭代终止条件,当迭代过程中某一迭代变量达到目的时应该终止迭代。
分析方法:基于较小的迭代单元,从0到1或从0到2。找出迭代变量和迭代关系式即可。