递归

2023-10-29
2分钟阅读时长

什么是递归?

递归(recursion)是一种编程技术,通过在函数内部调用自身来解决问题。递归函数在处理问题时将其分解为更小的子问题,并通过递归调用自身来解决这些子问题。递归通常用于解决可以被分解成相同类型的更小问题的问题。

递归的基本原理和工作方式

  • 函数定义中包含对自身的调用。
  • 函数在每次调用时都会改变输入参数,以便将问题规模缩小到更小的子问题。
  • 函数定义中包含一个或多个停止条件(也称为基本情况),用于结束递归并返回结果。
  • 递归的基本原理是将复杂的问题划分为更小的、可解决的子问题,并通过递归调用自身来解决这些子问题。每次递归调用都会将问题的规模缩小,直到达到停止条件为止。

递归的优点和缺点

递归的优点:

  • 可以简化复杂的问题,使代码更加简洁和可读。
  • 可以处理需要多层嵌套的问题,使代码结构更清晰。

递归的缺点:

  • 可能导致性能问题,因为递归函数会对同一问题进行多次计算。
  • 如果停止条件不正确或缺失,可能导致无限递归,最终导致栈溢出错误。

如何正确使用递归

要正确使用递归,需要考虑以下几点:

  • 定义明确的停止条件,以确保递归在适当的时候结束。
  • 确保每次递归调用都将问题的规模缩小,以便最终达到停止条件。
  • 避免无效的递归调用,以提高性能并避免栈溢出错误。

常见的递归应用场景

递归在许多编程问题中都有应用,例如:

计算阶乘

function factorial(n) {
  // 基本情况
  if (n === 0) {
    return 1;
  }
  // 递归情况
  return n * factorial(n - 1);
}

console.log(factorial(5)); // 输出 120

遍历树或图

// 定义二叉树节点
class TreeNode {
  constructor(val) {
    this.val = val;
    this.left = null;
    this.right = null;
  }
}

// 递归遍历二叉树的函数
function traverseTree(root) {
  if (root === null) {
    return;
  }

  // 前序遍历代码位置
  console.log(root.val);

  traverseTree(root.left); // 递归遍历左子树

  // 中序遍历代码位置
  // console.log(root.val);

  traverseTree(root.right); // 递归遍历右子树

  // 后序遍历代码位置
  // console.log(root.val);
}

// 创建二叉树
const root = new TreeNode(1);
root.left = new TreeNode(2);
root.right = new TreeNode(3);
root.left.left = new TreeNode(4);
root.left.right = new TreeNode(5);

// 调用递归遍历函数
traverseTree(root);

解决分治问题

递归实现简单的归并排序算法

function divideAndConquer(problem) {
  // 判断是否需要继续分治
  if (problem.length < 2) {
    return problem;
  }

  // 分治
  const mid = Math.floor(problem.length / 2);
  const left = problem.slice(0, mid);
  const right = problem.slice(mid);

  // 递归处理左右子问题
  const leftResult = divideAndConquer(left);
  const rightResult = divideAndConquer(right);

  // 合并结果
  return merge(leftResult, rightResult);
}

function merge(left, right) {
  let result = [];

  while (left.length && right.length) {
    if (left[0] <= right[0]) {
      result.push(left.shift());
    } else {
      result.push(right.shift());
    }
  }

  return result.concat(left, right);
}