返回

初探前端刷题:揭秘「最小路径和」的神奇算法

前端

引子

在「前端刷题」的浩瀚海洋中,一道经典的题目浮出水面,它宛如一面镜子,映照出前端开发者们的算法功底和解题思维。它就是大名鼎鼎的「最小路径和」问题。今天,我们就潜入这道题目的深处,探寻隐藏其中的算法奥秘。

题意解析

「最小路径和」题目本质上是一个动态规划问题。它给定一个由非负整数组成的矩阵,代表从矩阵左上角到右下角的每一步移动的代价。我们的任务是找到一条从左上角到右下角的路径,使得路径上的代价之和最小。

算法原理

解决「最小路径和」问题的核心在于动态规划。动态规划是一种自底向上的算法,它将问题分解为一系列重叠的子问题,然后逐步解决这些子问题,最终得到整个问题的解。

在这个问题中,我们可以将矩阵的每个元素看作一个子问题。对于矩阵中的元素 dp[i][j], 它表示从左上角到点 (i, j) 的最小路径和。我们可以通过以下公式递推计算 dp[i][j] 的值:

dp[i][j] = min(dp[i-1][j], dp[i][j-1]) + grid[i][j]

其中,grid[i][j] 表示点 (i, j) 的代价。

代码实现

function minPathSum(grid) {
  const m = grid.length;
  const n = grid[0].length;
  const dp = new Array(m).fill(0).map(() => new Array(n).fill(0));

  // 初始化
  dp[0][0] = grid[0][0];

  // 计算第一行
  for (let i = 1; i < n; i++) {
    dp[0][i] = dp[0][i - 1] + grid[0][i];
  }

  // 计算第一列
  for (let i = 1; i < m; i++) {
    dp[i][0] = dp[i - 1][0] + grid[i][0];
  }

  // 计算其余元素
  for (let i = 1; i < m; i++) {
    for (let j = 1; j < n; j++) {
      dp[i][j] = Math.min(dp[i - 1][j], dp[i][j - 1]) + grid[i][j];
    }
  }

  // 返回最小路径和
  return dp[m - 1][n - 1];
}

分析与优化

上述代码的时间复杂度为 O(mn), 空间复杂度为 O(mn). 为了进一步优化,我们可以考虑使用滚动数组的技巧,将空间复杂度优化到 O(n).

function minPathSumOptimized(grid) {
  const m = grid.length;
  const n = grid[0].length;
  const dp = new Array(n).fill(0);

  // 初始化
  dp[0] = grid[0][0];

  // 计算第一行
  for (let i = 1; i < n; i++) {
    dp[i] = dp[i - 1] + grid[0][i];
  }

  // 计算其余行
  for (let i = 1; i < m; i++) {
    // 更新当前行
    for (let j = 1; j < n; j++) {
      dp[j] = Math.min(dp[j], dp[j - 1]) + grid[i][j];
    }
  }

  // 返回最小路径和
  return dp[n - 1];
}

总结

「最小路径和」问题是前端刷题中一道经典的题目,它考验着前端开发者们的算法功底和解题思维。通过动态规划的算法,我们可以高效地找到从矩阵左上角到右下角的最小路径和。通过深入理解算法原理和代码实现,我们可以不断提升自己的算法能力,为前端开发的实际应用奠定坚实的基础。

SEO优化