递归(Recursion)快速上手指南【JS例子】

目录

递归是什么?

递归是一种 函数自己调用自己 的编程方式,用来解决 复杂但结构重复 的问题。

什么时候使用递归?

当问题能被拆成 与原问题结构相同但更小的子问题 时,就可以使用递归。

场景 示例
处理树形结构 菜单树、组织树、文件夹结构
链式结构 链表、路径寻找
重复计算 斐波那契、阶乘
深度遍历 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 ✅ ✅
性能敏感 ❌ ✅
可读性要求 ✅ ❌

树 = 递归优先;线性 = 循环优先

相关推荐
时间的拾荒人3 小时前
Qt 文件操作详解:从基础类到读写实战
开发语言·qt·面试
Wang's Blog3 小时前
Java 项目实战: 外卖平台优化-YApi接口管理平台与文档导入导出
java·开发语言·yapi
专业程序开发源3 小时前
django新闻推荐系统70655-计算机课程设计、毕业设计
java·javascript·spring boot·后端·python·django·课程设计
jimy13 小时前
基类没有虚析构函数,基类指针删除派生类对象,行为是未定义的
开发语言·c++
迅猛龙办公室3 小时前
python获取星期字符串
开发语言·python
acd120094 小时前
前端随笔:数据明明变了,Vue 页面就是不更新
前端·javascript·vue.js
泡海椒4 小时前
JQuick-Excel dateFormat 转换实战:日期值与 FORMAT 显示格式的边界
开发语言·python·excel
白杨尚青5 小时前
C++入门篇(十三):vector(上)——动态数组:构造、空间增长与迭代器失效
开发语言·c++·笔记·算法·stl
江江爱编程5 小时前
java零基础保姆级教学01
java·开发语言
传人5 小时前
悬停旋转放大和位移效果如何写
前端·javascript·css