前端必会数据结构与算法系列之递归(八)

234

1. 什么是递归

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

递归代码模版

recursion(level, param1, param2, ...) { 
    // 递归终止条件
    if (level > MAX_LEVEL) {
        return     
    }
    // 处理当前层逻辑   
    process(level, data...)   

    // 下探到下一层
    recursion(level + 1, p1, ...)   
    
    // 如果需要,清理当前层
}

2. 计算阶乘

使用循环

function factorialIterative(number) { 
 if (number < 0) return undefined; 
 let total = 1; 
 for (let n = number; n > 1; n--) { 
   total = total * n; 
 } 
 return total; 
} 

使用递归

function factorial(n) { 
 if (n === 1 || n === 0) { // 基线条件
   return 1; 
 } 
 return n * factorial(n - 1); // 递归调用
} 

3. 斐波那契数列

  • 位置 0 的斐波那契数是零。
  • 1 和 2 的斐波那契数是 1。
  • n(此处 n > 2)的斐波那契数是(n - 1)的斐波那契数加上(n - 2)的斐波那契数
  1. 迭代法
function fibonacciIterative(n) { 
 if (n < 1) return 0; 
 if (n <= 2) return 1; 
 let fibNMinus2 = 0; 
 let fibNMinus1 = 1; 
 let fibN = n; 
 for (let i = 2; i <= n; i++) {
   fibN = fibNMinus1 + fibNMinus2;
   fibNMinus2 = fibNMinus1; 
   fibNMinus1 = fibN; 
 } 
 return fibN; 
}
  1. 递归法
function fibonacci(n){ 
 if (n < 1) return 0;
 if (n <= 2) return 1;
 return fibonacci(n - 1) + fibonacci(n - 2);
}

以上代码执行情况

image.png

添加缓存

function fibonacciMemoization(n) { 
 const memo = [0, 1];
 const fibonacci = (n) => { 
 if (memo[n] != null) return memo[n];
   return memo[n] = fibonacci(n - 1, memo) + fibonacci(n - 2, memo);
 }; 
 return fibonacci; 
}

4. 为什么使用递归

迭代的版本比递归的版本快很多,所以这表示递归更慢。但是,递归版本更容易理解,需要的代码通常也更少。另外,对一些算法来说,迭代的解法可能不可用,而且有了尾调用优化,递归的多余消耗甚至可能被消除

它来解决问题会更简单

如何优雅地计算斐波那契数列 time.geekbang.org/dailylesson…

5. 实战题目

5.1 easy

1.爬楼梯

难度:简单

题解:爬楼梯(dp,递归多解法)

2.翻转二叉树

难度:简单

题解:翻转二叉树(递归与迭代)

翻转二叉树(分治)

3.二叉树的最大深度

难度:简单

题解:二叉树的最大深度 DFS

4.二叉树的最小深度

难度:简单

题解:二叉树的最小深度 BFS

5.二叉树的最近公共祖先

难度:简单

题解:二叉树的最近公共祖先

5.2 medium

1.括号生成

难度:中等

题解:括号生成(BFS+DFS)

2.验证二叉搜索树

难度:中等

题解:验证二叉搜索树(递归与中序遍历)

3.从前序与中序遍历序列构造二叉树

难度:中等

题解:从前序与中序遍历序列构造二叉树

4.组合

难度:中等

题解:

5.全排列

难度:中等

题解:全排列(回溯)

6.全排列 II

难度:中等

题解:

5.3 hard

1.二叉树的序列化与反序列化

难度:困难

题解:二叉树的序列化与反序列化(多解法)