目录
- 递归是什么?
- 什么时候使用递归?
- 递归的核心要素
- 模版写法
- 从简单到复杂的递归案例
-
- 求阶乘
- 斐波那契数列(有重复计算)
- 数组求和
- 扁平化多维数组
- 查找树形结构节点
- 树结构映射(渲染前端菜单/组件树)
- [深度遍历 DOM 节点](#深度遍历 DOM 节点)
- 查找嵌套评论(评论树展开)
- 快速排序(分治递归)
- 计算对象嵌套属性数量
- 递归vs循环
递归是什么?
递归是一种 函数自己调用自己 的编程方式,用来解决 复杂但结构重复 的问题。
什么时候使用递归?
当问题能被拆成 与原问题结构相同但更小的子问题 时,就可以使用递归。
| 场景 | 示例 |
|---|---|
| 处理树形结构 | 菜单树、组织树、文件夹结构 |
| 链式结构 | 链表、路径寻找 |
| 重复计算 | 斐波那契、阶乘 |
| 深度遍历 | DFS、查找嵌套结构 |
| 分治算法 | 快排、归并 |
递归的核心要素
| 要素 | 说明 |
|---|---|
| 🔹 终止条件 | 必须有,避免无限循环 |
| 🔹 递归调用 | 函数自己调用自己,处理子问题 |
| 🔹 返回结果 | 每层递归如何返回和组合结果 |
模版写法
javascript
function recursion(data) {
// 1. 终止条件(必须有!)
if (满足结束条件) return 结果;
// 2. 递归调用
return recursion(缩小后的数据);
}
从简单到复杂的递归案例
求阶乘
javascript
function factorial(n) {
if (n === 1) return 1; // 终止条件
return n * factorial(n - 1); // 递归调用
}
console.log(factorial(5)); // 120
斐波那契数列(有重复计算)
javascript
function fib(n) {
if (n <= 1) return n; // 终止条件
return fib(n-1) + fib(n-2); // 递归调用
}
console.log(fib(6)); // 8
斐波那契数列是什么?
0, 1, 1, 2, 3, 5, 8, 13, 21, 34, ...
从第三个数开始,每个数 = 前两个数相加。也就是:第0项:0,第1项:1,第2项 = 0 + 1 = 1,第3项 = 1 + 1 = 2,第4项 = 1 + 2 = 3,第5项 = 2 + 3 = 5,第6项 = 3 + 5 = 8
斐波那契数列的数学公式
javascript
F(0) = 0
F(1) = 1
F(n) = F(n-1) + F(n-2)
数组求和
javascript
function sumArray(arr) {
if (arr.length === 0) return 0;
return arr[0] + sumArray(arr.slice(1));
}
console.log(sumArray([1,2,3,4])); // 10
扁平化多维数组
javascript
function flatten(arr) {
let res = [];
for (const item of arr) {
if (Array.isArray(item)) {
res = res.concat(flatten(item)); // 递归展开
} else {
res.push(item);
}
}
return res;
}
console.log(flatten([1, [2, [3, [4]], 5]]));
查找树形结构节点
javascript
function findNode(tree, id) {
for (const node of tree) {
if (node.id === id) return node; // 终止条件
if (node.children) {
const found = findNode(node.children, id); // 递归调用
if (found) return found;
}
}
return null;
}
树结构映射(渲染前端菜单/组件树)
javascript
function mapTree(nodes) {
if (!nodes || nodes.length === 0) return [];
return nodes.map(node => ({
label: node.name,
value: node.id,
children: mapTree(node.children) // 递归处理子节点
}));
}
深度遍历 DOM 节点
javascript
function traverseDOM(node) {
console.log(node.tagName);
node.childNodes.forEach(child => traverseDOM(child));
}
traverseDOM(document.body);
查找嵌套评论(评论树展开)
javascript
function collectComments(comments) {
let result = [];
comments.forEach(c => {
result.push(c.text);
if (c.replies) {
result = result.concat(collectComments(c.replies));
}
});
return result;
}
快速排序(分治递归)
javascript
function quickSort(arr) {
if (arr.length <= 1) return arr; // 终止条件
const pivot = arr[0];
const left = arr.slice(1).filter(x => x <= pivot);
const right = arr.slice(1).filter(x => x > pivot);
return [...quickSort(left), pivot, ...quickSort(right)];
}
计算对象嵌套属性数量
javascript
function countKeys(obj) {
let count = 0;
for (const key in obj) {
count++;
if (typeof obj[key] === 'object' && obj[key] !== null) {
count += countKeys(obj[key]); // 递归
}
}
return count;
}
递归vs循环
| 场景 | 适合递归 | 适合循环 |
|---|---|---|
| 树结构 | ✅ | ❌ |
| 链表 | ✅ | ✅ |
| 数组遍历 | ❌ | ✅ |
| 深度搜索 DFS | ✅ | ✅ |
| 性能敏感 | ❌ | ✅ |
| 可读性要求 | ✅ | ❌ |
树 = 递归优先;线性 = 循环优先