递归(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
性能敏感
可读性要求

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

相关推荐
虹科网络安全7 分钟前
艾体宝干货|数据复制详解:类型、原理与适用场景
java·开发语言·数据库
axng pmje32 分钟前
Java语法进阶
java·开发语言·jvm
老前端的功夫1 小时前
【Java从入门到入土】28:Stream API:告别for循环的新时代
java·开发语言·python
qq_435287921 小时前
第9章 夸父逐日与后羿射日:死循环与进程终止?十个太阳同时值班的并行冲突
java·开发语言·git·死循环·进程终止·并行冲突·夸父逐日
止语Lab1 小时前
从手动到框架:Go DI 演进的三个拐点
开发语言·后端·golang
yaoxin5211231 小时前
397. Java 文件操作基础 - 创建常规文件与临时文件
java·开发语言·python
小短腿的代码世界1 小时前
Qt日志系统深度解析:从qDebug到企业级日志框架
开发语言·qt
前端摸鱼匠2 小时前
Vue 3 的v-bind合并行为:讲解v-bind与普通属性合并的规则
前端·javascript·vue.js·前端框架·ecmascript
REDcker2 小时前
浏览器端Web程序性能分析与优化实战 DevTools指标与工程清单
开发语言·前端·javascript·vue·ecmascript·php·js
我命由我123454 小时前
Kotlin 开发 - lateinit 关键字
android·java·开发语言·kotlin·android studio·android-studio·android runtime