返回

从算法原理剖析 LeetCode.31 组合总数:巧用 DFS + 剪枝优化解题效率

前端

在 LeetCode 这片技术竞技场上,组合总数 (LeetCode.31)是一道备受推崇的经典算法题。它不仅考验算法思维的灵活性,更蕴藏着巧妙运用剪枝优化的手段,以提升解题效率。本文将深入剖析这道题目的算法原理,并以 JavaScript 代码示例加以佐证,带领你领略算法之美。

1. 题目描述

给定一个候选元素集合 candidates 和一个目标值 target,求出 candidates 中所有可以和为 target 的组合。

示例 1:

输入:candidates = [2, 3, 6, 7], target = 7
输出:[[2, 2, 3], [7]]

2. DFS 算法详解

2.1 DFS 算法的基本思想

深度优先搜索(DFS)是一种递归算法,它通过遍历所有可能的组合来寻找解。DFS 的核心思想是从一个起点出发,沿着一条路径尽可能深入,直到无法继续为止,然后回溯到上一个节点,继续探索其他路径。

2.2 结束条件

在 DFS 算法中,结束条件至关重要。对于组合总数问题,结束条件有两个:

  1. 当递归的数组都被递归完了。
  2. 当路径内的元素总和大于目标值。

2.3 剪枝优化

为了优化 DFS 的效率,我们可以使用剪枝技术。当路径内的元素总和已经大于目标值时,我们可以直接剪枝,避免不必要的递归。这种优化手段可以显著减少递归调用的次数,从而提升算法的运行速度。

3. JavaScript 代码示例

/**
 * 给定一个候选元素集合candidates和一个目标值target,求出candidates中所有可以和为target的组合。
 *
 * @param {number[]} candidates 候选元素集合
 * @param {number} target 目标值
 * @return {number[][]} 满足条件的所有组合
 */
const combinationSum = (candidates, target) => {
  const result = [];
  const path = [];

  /**
   * @param {number} startIndex 开始递归的索引
   * @param {number} currentSum 当前路径上的元素和
   */
  const dfs = (startIndex, currentSum) => {
    // 结束条件
    if (startIndex >= candidates.length || currentSum > target) {
      return;
    }

    // 处理当前元素
    const element = candidates[startIndex];
    const newSum = currentSum + element;

    // 尝试加入当前元素
    path.push(element);
    if (newSum === target) {
      result.push([...path]);
    } else if (newSum < target) {
      // 剪枝:如果当前元素和已大于目标值,则不再继续递归
      dfs(startIndex, newSum);
      // 尝试不加入当前元素
      dfs(startIndex + 1, currentSum);
    }

    // 回溯:从当前元素返回
    path.pop();
  };

  // 从第一个元素开始递归
  dfs(0, 0);

  return result;
};

3.1 代码解析

  1. 初始化:定义 result 数组用于存储所有满足条件的组合,定义 path 数组用于存储当前的路径。
  2. DFS 函数:定义 dfs 函数,接受两个参数:startIndex 表示当前递归的起始索引,currentSum 表示当前路径上的元素和。
  3. 结束条件:在 dfs 函数中,首先判断是否满足结束条件。如果 startIndex 超过了 candidates 的长度,或者 currentSum 大于 target,则直接返回。
  4. 处理当前元素:将当前元素加入 path,并计算新的元素和 newSum
  5. 递归调用:如果 newSum 等于 target,则将当前路径加入 result。如果 newSum 小于 target,则继续递归调用 dfs 函数,尝试加入当前元素和不加入当前元素两种情况。
  6. 回溯:递归调用结束后,从 path 中移除当前元素,进行回溯。

4. 总结

通过深入分析 LeetCode.31 组合总数这道题目,我们了解了 DFS 算法和剪枝技术的应用。通过剪枝,我们可以有效地优化 DFS 的效率,从而提升算法的运行速度。这道题目不仅锻炼了我们的算法思维,更让我们领悟了算法优化技巧的重要性。

在算法设计中,理解问题的本质并灵活运用各种优化手段是提升解题效率的关键。希望本文能为读者在算法学习和实践中提供一些有益的启示。

相关资源链接