【问题标题】:Diameter of Binary Tree - Better Design二叉树的直径 - 更好的设计
【发布时间】:2012-08-10 07:18:38
【问题描述】:

我写了一个代码来查找二叉树的直径。 需要以下建议:

  1. 我可以在不使用类级别的静态变量的情况下执行此操作吗?
  2. 算法好吗/有什么建议吗?

    public class DiameterOfTree {   
    public static int diameter = 0; 
    public static int getDiameter(BinaryTreeNode root) {        
        if (root != null) {                     
            int leftCount = getDiameter(root.getLeft());
            int rightCount = getDiameter(root.getRight());
            if (leftCount + rightCount > diameter) {
                diameter = leftCount + rightCount;
                System.out.println("---diameter------------->" + diameter);
            }           
            if ( leftCount > rightCount) {
                return leftCount + 1;
            }
            return rightCount + 1;
        }
        return 0;
      }
    }
    

【问题讨论】:

  • is the algorithm fine? 是什么意思。你测试过代码吗?
  • arun,当然我已经测试了代码。我的意思是有更好的算法吗?
  • 这个问题也可以在Code Review上进行。
  • @Barth,谢谢。不知道代码审查。也会在那里尝试。
  • @Manish 哦,好的。请参考这个geeksforgeeks.org/archives/5687

标签: java algorithm recursion tree binary-tree


【解决方案1】:

在尝试找到二叉树中两个节点之间的最长路径(直径)时,需要考虑三种情况:

  1. 最长的路径通过根,
  2. 最长的路径完全包含在左子树中,
  3. 最长的路径完全包含在右子树中。

通过根的最长路径只是左右子树的高度之和(根不需要 +1,因为具有根节点和 1 个左、1 个右子树节点的树的直径将是 2),其他两个可以递归找到:

public static int getDiameter(BinaryTreeNode root) {        
    if (root == null)
        return 0;

    int rootDiameter = getHeight(root.getLeft()) + getHeight(root.getRight()); //Removing the +1
    int leftDiameter = getDiameter(root.getLeft());
    int rightDiameter = getDiameter(root.getRight());

    return Math.max(rootDiameter, Math.max(leftDiameter, rightDiameter));
}

public static int getHeight(BinaryTreeNode root) {
    if (root == null)
        return 0;

    return Math.max(getHeight(root.getLeft()), getHeight(root.getRight())) + 1;
}

【讨论】:

  • @Harshdeep,是的,它的复杂性是 O(n^2),因为它每次都会到达底部以找到高度。我正在寻找在与直径相同的遍历中计算高度的代码。
  • 您将高度与节点混合,高度是“边数”,因此只有单个节点的树的高度为 0。从技术上讲,您的代码是正确的,但不是术语。
  • 根据这段代码,这棵树的直径:a -> b -> c -> d 是4。但是唯一的叶子节点是d。因此,定义为两个叶子之间最长路径长度的直径应该是1。
  • @kirakun,在这种情况下,直径不应该为 0(或到根的长度 * 2)吗?
  • rootDiameter,应该是leftHeight和rightHeight之和。因为,只有 2 个节点的树的直径是 1。int rootDiameter = getHeight(root.getLeft()) + getHeight(root.getRight());
【解决方案2】:

这是一个 O(n) 解决方案,对已接受的答案进行了最小的更改:

public static int[] getDiameter(BinaryTreeNode root) {
    int[] result = new int[]{0,0};    //1st element: diameter, 2nd: height    
    if (root == null)  return result;
    int[] leftResult = getDiameter(root.getLeft());
    int[] rightResult = getDiameter(root.getRight());
    int height = Math.max(leftResult[1], rightResult[1]) + 1;
    int rootDiameter = leftResult[1] + rightResult[1] + 1;
    int leftDiameter = leftResult[0];
    int rightDiameter = rightResult[0];
    result[0] = Math.max(rootDiameter, Math.max(leftDiameter, rightDiameter));
    result[1] = height;

    return result;
}

它只是同时计算高度和直径。由于 Java 没有传递引用,我定义了一个 int[] 来返回结果。

【讨论】:

  • int[] rightResult = getDiameter(root.getLeft()); 这行应该是:int[] rightResult = getDiameter(root.getRight());?
【解决方案3】:

这是一个具有 O(N) 时间复杂度的 Java 解决方案。 它在计算直径时以相同的递归方式计算高度。 参考Link

private class HeightWrapper {
    int height = 0;
}

private int getDiameter_helper(BinaryTreeNode root, HeightWrapper wrapper) {
    if (root == null) {
        return 0; // diameter and height are 0
    }

    /* wrappers for heights of the left and right subtrees */
    HeightWrapper lhWrapper = new HeightWrapper();
    HeightWrapper rhWrapper = new HeightWrapper();

    /* get heights of left and right subtrees and their diameters */
    int leftDiameter = getDiameter_helper(root.left, lhWrapper);
    int rightDiameter = getDiameter_helper(root.right, rhWrapper);

    /* calculate root diameter */
    int rootDiameter = lhWrapper.height + rhWrapper.height + 1;

    /* calculate height of current node */
    wrapper.height = Math.max(lhWrapper.height, rhWrapper.height) + 1;

    /* calculate the diameter */
    return Math.max(rootDiameter, Math.max(leftDiameter, rightDiameter));
}

public int getDiameter(BinaryTreeNode root) {
    HeightWrapper wrapper = new HeightWrapper();
    return getDiameter_helper(root, wrapper);
}

【讨论】:

  • +1 .. 感谢您的解决方案。为什么我们应该将它们保存在包装类中?我已经看到了link,但我无法在 Java 中实现它。但是您的解决方案似乎运行良好。你能编辑上面的答案并解释为什么我们需要一个包装类吗?
  • 嗨@Che,您需要一个包装器类的原因是因为Java 是Pass By Value,这意味着在您的数据周围使用对象包装器是通过递归级别正确维护值的最简单方法。这里是another useful link
  • @nem035 :对于只有 1 个节点的树,您的代码将给出 1 作为直径,这是错误的。对于这种情况,它应该是 0。
  • 嗯,1 个节点是您可以争辩 0 或 1 都可能是有效答案的情况。这完全取决于,在您考虑的直径定义中,您是否在计算起始节点时计算它。
【解决方案4】:

您不需要将结果存储在静态字段直径中。只需像这样使用静态方法:

public class DiameterOfTree {

    public static long getDiameter(BinaryTreeNode root) {
        if (root != null) {
            long leftDiameter = getDiameter(root.getLeft());
            long rightDiameter = getDiameter(root.getRight());
            long leftHeight = getHeight(root.getLeft());
            long rightHeight = getHeight(root.getRight());
            return Math.max(leftHeight + rightHeight + 1, Math.max(leftDiameter, rightDiameter));
        }
        return 0;
    }

    public static long getHeight(BinaryTreeNode root) {
        if (root != null) {
            long leftHeight = getHeight(root.getLeft());
            long rightHeight = getHeight(root.getRight());
            return  1 + Math.max(leftHeight, rightHeight);
        }
        return 0;
    }
}

【讨论】:

  • 这不是给你直径,而是给你深度。
  • @Xavier 你能提供优化版本的代码,它以与直径相同的递归方式计算高度,这样它就不必一直到底部来计算高度?这样它的复杂性将从 O(n^2) 降到 O(n)
  • @AKS 您可以用时间换取空间,并为线性时间复杂度添加记忆(因为每个节点的高度都可以从其子树在O(1) time 的结果中计算出来)。或者,您可以使用一个后序遍历(可以迭代完成,而不是使用递归)并存储这些结果。
【解决方案5】:

与接受的答案相比,有一个最小的 O(n) 答案。

int DiameterTree(BinaryTreeNode root, int diameter) {
    int left, right;
    if (!root) return 0;

    left  = DiameterTree(root.getLeft(), diameter);
    right = DiameterTree(root.getRight(), diameter);
    if (left + right > diameter) diameter = left + right;

    return Math.max(left, right) + 1;
}

假设diameter 是类中的静态变量。

【讨论】:

  • 你为什么在方法参数中使用diameter作为普通变量?
  • 你能解释一下这个算法吗?我无法理解在 return 语句中添加 1 的概念..
【解决方案6】:

一棵树T的直径是

直径(T) = max( 直径(T.left), 直径(T.right), 高度(T.left)+高度(T.right)+1)

 private class Data {  
   public int height;  
   public int diameter;  
 }  

 private void diameter(TreeNode root, Data d) {  
   if (root == null) {  
     d.height = 0; d.diameter = 0; return;  
   }  
   diameter(root.left, d); // get data in left subtree  
   int hLeft = d.height;  
   int dLeft = d.diameter;  
   diameter(root.right, d); // overwrite with data in right tree  
   d.diameter = Math.max(Math.max(dLeft, d.diameter), hLeft+d.height+1);  
   d.height = Math.max(hLeft, d.height) + 1;  
 }  

 public int diameter(TreeNode root) {  
   Data data = new Data();  
   diameter(root, data);  
   return data.diameter;  
 }  

【讨论】:

    【解决方案7】:
    public class NodeWrap{
        int height = 0;
        int maxLength = 0;
        public NodeWrap(int h, int m){
            height = s;
            maxLength = m;
        }
    }
    
    
    public NodeWrap getDiameter(BinaryNode root){
        if(root == null){
            return new NodeWrap(0, 0);
        }
    
        NodeWrap left = getDiameter(root.left);
        NodeWrap right = getDiameter(root.right);
    
        int height = Math.max(left.height + right.height) + 1;
    
        int maxLength = Math.max(left.maxLength, right.maxLength);
        if(left.height != 0 && right.height != 0){
            maxLength = Math.max(left.height + right.height + 1, maxLength);
        }
        return new NodeWrap(singleLength, maxLength);
    }
    

    【讨论】:

    • 你能回答 OP 发布的问题吗?他没有要求备用代码
    【解决方案8】:
    One more O(n) solution in python,
    code is self explanatory, only issue with this code is it returns tuple containing both height and diameter of the tree. 
    
    def diameter(node, height):
      if node is None:
        return 0, 0
      leftheight  = 0
      rightheight = 0
      leftdiameter,  leftheight = diameter(node.left, leftheight)
      rightdiameter, rightheight = diameter(node.right, rightheight)
      rootheight = 1 + max(leftheight, rightheight ) 
      rootdiameter = ( leftheight + rightheight + 1 )
      return max( rootdiameter, leftdiameter, rightdiameter ), rootheight
    

    【讨论】:

      【解决方案9】:

      干净整洁的解决方案:

      // way to use below util function:
      prop p = new prop();
      diameterUtil(root, p);
      System.out.println(p.d);
      
      class prop {
          int h;
          int d;
      }
      
      private void diameterUtil(Node n, prop p) {
          if (n == null) {
              p.h = 0;
              p.d = 0;
              return;
          }
          prop lp = new prop();
          prop rp = new prop();
          diameterUtil(n.left, lp);
          diameterUtil(n.right, rp);
          p.h = Math.max(lp.h, rp.h) + 1;
          p.d = Math.max((lp.h + rp.h + 1), Math.max(lp.d, rp.d));
      }
      

      【讨论】:

        【解决方案10】:

        这是 C++ 中的一种递归解决方案,它为您提供二叉树的高度和直径。

        struct tree
        {
            int height = -1;
            int diameter = 0;
        };
        
        struct tree BSTDiameter(struct node *root)
        {
            struct tree currentTree, leftTree, rightTree;
            if (root == NULL)
            {
                currentTree.height = -1;
                currentTree.diameter = 0;
                return currentTree;
            }
            leftTree = BSTDiameter(root->left);
            rightTree = BSTDiameter(root->right);
            currentTree.height = ((leftTree.height > rightTree.height) ? leftTree.height : rightTree.height) + 1;
            if (leftTree.height == -1 || rightTree.height == -1)
                currentTree.diameter = 0;
            else
                currentTree.diameter = (leftTree.height + rightTree.height + 3) > (rightTree.diameter > leftTree.diameter ? rightTree.diameter : leftTree.diameter) ? (leftTree.height + rightTree.height + 3) : (rightTree.diameter > leftTree.diameter ? rightTree.diameter : leftTree.diameter);
            return currentTree;
        }
        

        它的时间复杂度是 O(h),其中 h 是树的高度。 希望对你有所帮助。

        【讨论】:

          【解决方案11】:

          最有效的方法是计算直径和高度以达到 O(n),这是最简单的方法 [Python3,PyPy3]

          for this definition for a binary tree node,
          class TreeNode:
               def __init__(self, x):
                   self.val = x
                   self.left = None
                   self.right = None
          
          class Solution:
          def diameterOfBinaryTree(self, root: TreeNode) -> int:
              self.height = 1
          
              def height(node):
                  if node is None:
                      return 0
                  l_height = height(node.left)
                  r_height = height(node.right)
                  self.height = max(self.height,l_height+r_height+1)
                  return max(l_height,r_height) + 1
          
              height(root)
              return self.height-1
          

          最简单且经济实惠的解决方案,运行时间更快,复杂性更低。

          【讨论】:

            猜你喜欢
            • 2019-08-12
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2017-08-19
            相关资源
            最近更新 更多