leetcode刷题(三):二叉树和动态规划

40 阅读3分钟

二叉树

226. 翻转二叉树:给你一棵二叉树的根节点 root ,翻转这棵二叉树,并返回其根节点。
思路:通常遇到树结构的题目我们应该首先考虑递归,这道题使用递归就非常简单,只需要交换左右子节点:

function invertTree(root: TreeNode | null): TreeNode | null {
  if (!root) return null;
  let leftTree = invertTree(root.left);
  let rightTree = invertTree(root.right);
  // 交换左右子节点
  root.left = rightTree;
  root.right = leftTree;
  return root;
};

使用栈结构:递归和栈的解法通常是可以相互转换的,我们声明一个栈并将根节点加入进去,while循环只要栈不为空,循环内首先取出栈顶元素,交换左右子节点,之后将左右子节点入栈:

function invertTree(root: TreeNode | null): TreeNode | null {
  if (!root) return null;
  const stack: TreeNode[] = [root];
  while (stack.length) {
    const cur = stack.pop()!;
    // 交换左右子节点
    [cur.left, cur.right] = [cur.right, cur.left];
    if (cur.left) stack.push(cur.left);
    if (cur.right) stack.push(cur.right);
  }
  return root;
}

124. 二叉树中的最大路径和:二叉树中的 路径 被定义为一条节点序列,序列中每对相邻节点之间都存在一条边。同一个节点在一条路径序列中 至多出现一次 。该路径 至少包含一个 节点,且不一定经过根节点。
路径和是路径中各节点值的总和。
给你一个二叉树的根节点 root,返回其最大路径和。
思路:首先应该意识到要使用递归,然后我们应该想要知道左右子树能提供的最大路径和,这样可以算出经过该root节点的最大路径和,然后取以所有节点为根节点并经过节点的最大值即可:

function maxPathSum(root: TreeNode | null): number {
  let res: number = -Infinity;
  // 获取经过节点的最大一侧路径和
  function getMaxSideSum(root: TreeNode | null): number {
    if (!root) return 0;
    // 如果左右路径和小于0 就取0
    const leftSideMax = Math.max(0, getMaxSideSum(root.left));
    const rightSideMax = Math.max(0, getMaxSideSum(root.right));
    // 经过root节点的最大路径和
    res = Math.max(res, (leftSideMax + root.val + leftSideMax));
    // 返回经过节点最大一侧路径和
    return Math.max(leftSideMax, rightSideMax) + root.val;
  }
  getMaxSideSum(root);
  return res;
}

动态规划

62. 不同路径:一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。 问总共有多少条不同的路径?
思路:这是一道典型的动态规划问题,状态转移方程很容易就能看出来:dp[m][n] = dp[m- 1][n] + dp[m][n-1],初始化状态为第一列和第一行的每个方格的路径皆为1,那么:

function uniquePaths(m: number, n: number): number {
  if (m === 1 || n == 1) return 1;
  // 声明数组
  const table: number[][] = Array.from({ length: m }, () =>
    new Array(n).fill(1)
  );
  // 从第二列第二行遍历即可 第一列第一行均为1
  for (let i = 1; i < m; i++) {
    for (let j = 1; j < n; j++) {
      table[i][j] = table[i - 1][j] + table[i][j - 1];
    }
  }
  return table[m - 1][n - 1];
}

剑指 Offer 47. 礼物的最大价值:在一个 m*n 的棋盘的每一格都放有一个礼物,每个礼物都有一定的价值(价值大于 0)。你可以从棋盘的左上角开始拿格子里的礼物,并每次向右或者向下移动一格、直到到达棋盘的右下角。给定一个棋盘及其上面的礼物的价值,请计算你最多能拿到多少价值的礼物?
思路:这题跟上一题非常相似,这题的状态转移方程是dp[m][n] = dp[m][n] + Math.max(dp[n - 1][m], dp[n][m - 1]),初始状态则是第一列和第一排的dp[i] = dp[i] + dp[i - 1],实现也非常简单:

function maxValue(grid: number[][]): number {
  const deep = grid.length;
  const width = grid[0].length;
  for (let i = 0; i < deep; i++) {
    for (let j = 0; j < width; j++) {
      if (!(i === 0 && j === 0)) {
        if (i === 0) {
          grid[i][j] = grid[i][j] + grid[i][j - 1];
        } else if (j === 0) {
          grid[i][j] = grid[i][j] + grid[i - 1][j];
        } else {
          grid[i][j] = grid[i][j] + Math.max(grid[i][j - 1], grid[i - 1][j]);
        }
      }
    }
  }
  return grid[deep - 1][width - 1];
}