【问题标题】:How find the longest consecutive path in a binary tree如何在二叉树中找到最长的连续路径
【发布时间】:2019-02-19 03:02:51
【问题描述】:

我在一次采访中被问到这个问题。考虑一棵二叉树,我们需要打印最长路径的长度,其中每个元素相差 1。

EG:

          6
        /   \  
      5       7
     / \     / \
    2   4   8   9

答案:5 (4,5,6,7,8)

如何做到这一点? 我开发了一种算法来打印从根到叶的增加路径,但我不打算开发一个跟踪两个子树上的路径的算法。

EDIT:修改后需要取回原来的树。

【问题讨论】:

  • 修改树并跟踪它以恢复原始树是愚蠢的。相反,通过检查是否Math.Abs(node.value - parent.value) > 1 来跳过树中差异大于一的部分。如果这是真的,那么沿着这条路走下去就没有意义了。

标签: algorithm data-structures tree binary-tree


【解决方案1】:

正如@qwertyman 在 cmets 中所建议的那样

  1. 删除所有无效边,即差值大于 1 的边

  2. 现在我们有一个森林,为每个森林计算在@Filip Kočica 解决方案中给出的直径

  3. 答案是所有森林中的最大直径

【讨论】:

  • 如何找回原来的树
  • 只需将无效边存储在数据结构中,稍后再将它们添加回来
【解决方案2】:

对于每个子树,您可以计算从子树根开始向下的最长递增路径、向下的最长递减路径以及由从子树中任何位置的同一节点向下递增和递减路径组成的最长内部路径。

如果您已经为节点的所有子节点计算了这些值,则很容易计算它们,因此您可以将其作为任何后序遍历的一部分。

答案是整棵树中最长的内部路径。

【讨论】:

    【解决方案3】:

    longest_desc[a] 是从 a 向下的最长的 1×1 下降路径 同样longest_asc[a],最长的 1×1 增量路径从

    对于固定根 R,答案是 longest_desc[R] + longest_asc[R] - 1

    蛮力解决方案将从每个节点 X 执行 2 次 dfs/bfs 遍历以计算 longest_asc[X] and longest_desc[X],然后将它们合并在一起。生成的运行时复杂度将为O(n^2)

    但我们实际上可以使用动态规划做得更好:

    longest_asc[X] = max(longest_asc[Y in children[X]] with Y = X + 1)
    longest_desc[X] = max(longest_desc[Y in children[X]] with Y = X - 1)
    

    然后我们可以计算单个 DFS 遍历中的所有值 => O(n) 解决方案。

    【讨论】:

      【解决方案4】:

      答案不正确 - 另一个用户正确地指出了一个错误。我下面的解决方案仅在最大长度路径通过根时才有效。例如,如果最大长度路径完全在左子树中并且不通过根,则此答案失败。随意进一步阅读以熟悉递归解决方案......以及其中的错误。


      我假设路径必须具有 +1 的差异并不重要,如您的示例所示。 -1 的差异,导致类似4 -> 5 -> 4 -> 3 -> 4 -> 5 的路径也可以。

      public int getLongestConsecutivePath(TreeNode root) {
          return root == null
              ? 0
              : getLength(root.left, root.value) + getLength(root.right, root.value);
      }
      
      private int getLength(TreeNode node, int prevVal) {
          return node == null || Math.abs(node.value - prevVal) > 1
              ? 0
              : Math.max(getLength(node.left, node.value), getLength(node.right, node.value)) + 1;
      }
      

      说明:

      • 如果根不为空,我们得到左右子树的最大长度并求和。
      • 为了获得子树的最大长度,我们递归地获得子树的左右子树的最大长度。
        • 如果我们到达了叶子节点或者如果我们到达了一个值差大于 1 的节点,我们返回 0。
        • 否则,我们递归地从左右子树中获取最大长度并将其加 1 以适应此节点本身。

      【讨论】:

      • 您正在添加根 getLength 左侧和右侧,但假设根节点值与左右节点没有相差 1,那么您的解决方案将是错误的。
      • @nomanpouigt:如果左(和/或右)子节点的值与根节点的值相差 1,则 getLength() 将返回 0,这是正确的,因为唯一可用的路径在这种情况下,从根到其子级的遍历是无效的。 (您是否假设单个节点意味着长度为 1 的路径?我假设对于长度为 1 的路径,您需要 2 个节点。)
      • 在 root 和 root.left 没有差异 1 但最大连续路径位于 root.left 但在您的代码中您将完全忽略它的情况下会发生什么,因为您将通过getLength(root.right).
      • @nomanpouigt:你是对的。最大长度路径可能不通过根。我将编辑我的答案并提及错误。谢谢。
      猜你喜欢
      • 2018-11-26
      • 1970-01-01
      • 2016-07-02
      • 1970-01-01
      • 2015-09-11
      • 2022-01-16
      • 1970-01-01
      • 1970-01-01
      • 2021-12-30
      相关资源
      最近更新 更多