2015-11-21 136 views
2

我将一棵完整的子树定义为一棵树,所有级别都已满,最后一级左对齐,即所有节点都尽可能地离开,我想在树中找到最大的子树这是完整的。二叉树中最大的完整子树

一种方法是对每个节点以root身份执行概述为here的方法,这将需要O(n^2)个时间。

有没有更好的方法?

+1

这感觉就像功课,所以这里有一个提示:假设有深度至少k为顶点v为根的一棵完整的树想想什么属性v的孩子必须:必须有零个或更多孩子至少有K个满级,其次是至少1个孩子,至少有K-1个满级和1个部分级,其次是零个或更多的至少有K-1个满级的孩子。 –

+0

请指出您是指二元树还是任意树。 –

+0

您对子树的定义不同于子树的实际定义,即“T中由T中的一个节点组成的树和T中所有后代的树”。这是否意味着,因为EPI似乎是指实际的定义? – jjkim

回答

1

定义树节点的等级,如果此节点是root,则为最大完整子树的高度。 如果此节点是root,则将节点的宽度定义为max complete subtree的最后一级中的节点数。 因此对于树中的每个节点,我们有两个数字(r, w)。和w <= 2^r

如果节点有零个或只有一个孩子,那么节点有(r, w) = (1, 1)

如果节点有两个孩子(r1, w1)(r2, w2),我们有几种情况:

  1. r1 > r2当节点有(r2 + 1, 2^r2 + w2)
  2. r1 == r2w1 == 2^r1当节点有(r1 + 1, w1 + w2)
  3. r1 == r2w1 < 2^r1当节点会有(r1 + 1, w1)例如:

     root  
     .... 
    /\ / \ 
    l l r r 
    /\ / /\ /
    l l l r r r 

最大完整子树是


      m  
     .... 
    /\ / \ 
    m m m m 
    /\ / /\ /
    m m m r r r 
  • r1 < r2w1 == 2^r1当节点将具有(r1 + 1, 2 * w1)实施例:
  • 
         root  
         .... 
        /\ / \ 
        l l  r r 
        /\ /\ /\ /\ 
        l l l l r r r r 
          /
          r 
    

    最大完整子树是

    
          m  
         .... 
        /\ / \ 
        m m  m m 
        /\ /\ /\ /\ 
    m m m m m m m m 
          /
          r 
    
  • r1 < r2w1 < 2^r1当节点将具有(r1 + 1, w1)
  • 实施例:

    
         root  
         .... 
        /\ / \ 
        l l  r r 
        /\ / /\ /\ 
        l l l  r r r r 
          /
          r 
    

    在此基础上的规则最大完整子树是

    
          m  
         .... 
        /\ / \ 
        m m  m m 
        /\ / /\ /\ 
    m m m  r r r r 
          /
          r 
    

    您可以使用递归计算每个节点的(r, w)。这将需要O(n)。当您在此节点中找到最大等级r的节点时,找到最大节点为w的节点,并且此节点应该是解决方案。

    +0

    这并没有考虑到左对齐条件,例如,如果一个节点有2个子节点,并且两个节点都是完整的,这并不意味着以节点为根的树是完整的,因为最后一级可能存在间隙 – suyash

    +0

    两个孩子suibtrees之间还请注意,这个问题是一个完整的子树,而不是一个完整的子树,并注意一个完整的子树的定义问题 – suyash

    +0

    如果因为某些节点,我们从这个节点找到一个最大满树,那么最大如果最后一级的左节点中有子节点,则完整的树应该具有相同的高度或者更大。 –

    1

    我在编写元素编程访谈的变体时遇到了这篇文章。我想分享我的想法和代码。

    任何意见都欢迎。

    我使用递归来解决这个问题。 max用于存储有史以来发生的最大尺寸(我使用的数组,因为java是按值计算的)。 返回值信息包含有关树 传入的树是否为完整树的信息。完成时只返回树大小,否则返回(-1,false)。 如果一个子树T'不完整,它的大小永远不会被选中来组成一个更大的完整树。所有T的子树的大小将始终以最大值记录,所以我们绝不会错过任何值。

    下面是它如何工作的

    • 基本情况:根== null或根叶
    • 递归处理左孩子和右孩子。 leftInfo和rightInfo - 基于左/右孩子的返回值
    • 处理当前的树。

    • 如果两者都不是完整的,树是不完整的,不需要更新 最大。如果其中任何一个都完成,树不完整,请将最大更新为 左右较大的大小。如果两者都完成,树 可能会完成。首先检查左边是否完美,并且 正确满足高度要求。如果是,则返回(true, newSize)。否则,树不完整,更新最大值为 更大的左右值。

    下面是我code.It应及时O(n)和空间O(h),其中h是树的高度。(如果是平衡的,否则,最坏的情况将是为O(n ))。

    public class Solution { 
    
        public static void main(String[] args){ 
         TreeNode[] trees = new TreeNode[10]; 
         for(int i = 0; i < 10; i++){ 
          trees[i].val = i; 
         } 
        } 
    
        public int largestCompleteTree(TreeNode root){ 
         int[] max = new int[1]; 
         helper(root, max); 
         return max[0]; 
        } 
    
        private Info helper(TreeNode root, int[] max){ 
         //Base case: 
         if(root == null){ 
          return new Info(0, true); 
         } 
    
         if(root.left == null && root.right == null){ 
          max[0] = Math.max(max[0], 1); 
          return new Info(1, true); 
         } 
    
         //Recursion 
         Info leftInfo = helper(root.left, max); 
         Info rightInfo = helper(root.right, max); 
    
         //Process based on left subtree and right subtree. 
         //Neither is complete. 
         if(!leftInfo.isComplete && !rightInfo.isComplete){ 
          //Do not need to update the max value. 
          return new Info(-1, false); 
         } 
         //One of the subtree is complete, the current tree is not complete 
         else if(!leftInfo.isComplete || !rightInfo.isComplete){ 
          if(leftInfo.isComplete){ 
           max[0] = Math.max(max[0], leftInfo.size); 
           return new Info(-1, false);//the value has been recorded 
          }else{ 
           max[0] = Math.max(max[0], rightInfo.size); 
           return new Info(-1, false); 
          } 
         } 
         //Both subtrees are complete,   
         else{ 
          int size = 0; 
          if(((rightInfo.size & (rightInfo.size + 1)) == 0 && 
           leftInfo.size >= rightInfo.size && 
           leftInfo.size <= rightInfo.size*2 + 1)|| 
           ((leftInfo.size & (leftInfo.size + 1)) == 0 && 
             rightInfo.size >= (leftInfo.size - 1)/2 && 
             rightInfo.size <= leftInfo.size)) 
           { 
            size = leftInfo.size + rightInfo.size + 1; 
            max[0] = Math.max(max[0], size); 
            return new Info(size, true); 
           } 
          else{ //find the subtree with the greater size 
           size = leftInfo.size > rightInfo.size ? leftInfo.size : rightInfo.size; 
           max[0] = Math.max(max[0], size); 
           return new Info(0, false); 
          } 
         } 
        } 
        class Info { 
         boolean isComplete; 
         int size; 
    
         public Info(int size, boolean isComplete){ 
          this.isComplete = isComplete; 
          this.size = size; 
         } 
        } 
    } 
    
    +0

    这也是行不通的,因为严格要求左子树包含严格2的幂的节点,例如考虑一个简单树,其中根有2个孩子,左孩子有另一个左孩子,而右孩子没有孩子,在这种情况下,在根目录下,我们检查左边的孩子包含4个节点,但它只包含2个,所以我们不会认为它是一棵完整的树,但它又是 – suyash

    +0

    ,试着看看我的定义一棵完整的树,除了最后的所有级别都是满的,可以包含任意数量的节点 – suyash

    +0

    好点。对于树节点根,其左右两侧可能都是完整的。确定它们是否可以形成一棵完整的树:1)如果右边的树是完美的,那么leftSize需要满足leftSize> = rightSize && leftSize <= 2 * rightSize + 1. 2)或者如果左树是完美的,那么rightSize需要满足rightSize <= leftSize && rightSize> = leftSize/2 -1。如果满足任一条件,那么我们可以计算新的大小并更新最大值。 – marinama

    0

    会见了著作“编程采访的元素”这一任务,它似乎是我发现了一个很简单的解决方案,仍然不知道这是否是正确的,但测试它的一对夫妇的情况下,它的工作:

    private struct str 
         { 
          public bool isComplete; 
          public int height, size; 
          public str(bool isComplete, int height, int size) 
          { 
           this.isComplete = isComplete; 
           this.height = height; 
           this.size = size; 
          } 
         } 
    
         public int SizeOfLargestComplete() 
         { 
          return SizeOfLargestComplete(root).size; 
         } 
    
         private str SizeOfLargestComplete(Node n) 
         { 
          if (n == null) 
           return new str(true, -1, 0); 
          str l = SizeOfLargestComplete(n.left); 
          str r = SizeOfLargestComplete(n.right); 
    
          if (!l.isComplete || !r.isComplete) 
           return new str(false, 0, Math.Max(l.size, r.size)); 
    
          int numberOfLeftTreeLeafes; 
          if (l.height == -1) 
           numberOfLeftTreeLeafes = 0; 
          else 
           numberOfLeftTreeLeafes = l.size - ((1 << l.height) - 1); 
    
          bool leftTreeIsPerfect = (1 << (l.height + 1)) - 1 - l.size == 0; 
    
          //if left subtree is perfect, right subtree can have leaves on last level 
          if (leftTreeIsPerfect) 
           if (l.size - r.size >= 0 && l.size - r.size <= numberOfLeftTreeLeafes) 
            return new str(true, l.height + 1, l.size + r.size + 1); 
           else 
            return new str(false, 0, Math.Max(l.size, r.size)); 
          //if left subtree is not perfect, right subtree can't have leaves on last level 
          //so size of right subtree must be the same as left without leaves 
          else 
           if (r.size == l.size - numberOfLeftTreeLeafes) 
           return new str(true, l.height + 1, l.size + r.size + 1); 
          else 
           return new str(false, 0, Math.Max(l.size, r.size)); 
    
         } 
    
    2

    由于没有上述一个C++溶液,我已经加入我的溶液。如果您觉得有任何不正确或可以做出的改进,请告诉我。

    struct CompleteStatusWithHeight { 
    bool isComplete; 
    int height; 
    }; 
    
    int FindLargestCompletetSubTreeSize(const unique_ptr<BinaryTreeNode<int>>& tree) 
    { 
        return CheckComplete(tree).height; 
    } 
    
    CompleteStatusWithHeight CheckComplete(const unique_ptr<BinaryTreeNode<int>>& tree) 
    { 
    if (tree == nullptr) { 
         return {true, -1}; // Base case. 
    } 
    
    auto left_result = CheckComplete(tree->left); 
    if (!left_result.isComplete) { 
        return {false, 0}; // Left subtree is not balanced. 
    } 
    auto right_result = CheckComplete(tree->right); 
    if (!right_result.isComplete) { 
        return {false, 0}; // Right subtree is not balanced. 
    } 
    
    bool is_balanced = abs(left_result.height - right_result.height) == 0; 
    bool is_left_aligned = (left_result.height - right_result.height) == 1; 
    bool is_leaf = left_result.height == -1 && right_result.height ==-1; 
    bool is_complete = is_balanced || is_left_aligned || is_leaf; 
    
    int height = max(left_result.height, right_result.height) + 1; 
    return {is_complete, height}; 
    } 
    
    0

    这是我在Python中的解决方案。它正在研究我提出的案例。 返回值的含义如下: [X,Y,Z]

    • X =尺寸最大完整子树的直到该节点
    • Y =子树的高度
    • Z:0 - 完整的子树,1 - 有一个左子节点只有在这个子树,2 - 没有一个完整的子树

      def largest_complete_tree(root): 
      
          result = traverse_complete(root) 
          print('largest complete subtree: {}'.format(result[0])) 
      
      def traverse_complete(root): 
          if root: 
           left = traverse_complete(root.left) 
           right = traverse_complete(root.right) 
           max_complete = max(left[0], right[0]) 
           max_height = max(left[1], right[1]) 
           left_child_only = 1 if (left[2] == 1 and right[0] == 0) or (left[0] == 1 and right[0] == 0) else 0 
      
           # 5 conditions need to pass before left and right can be joined by this node 
           # to create a complete subtree. 
           if left[0] < right[0]: 
            return [max_complete, 0, 2] 
           if left[2] == 2 or right[2] == 2: 
            return [max_complete, 0, 2] 
           if abs(left[1]-right[1]) > 1: 
            return [max_complete, 0, 2] 
           if (left[2] == 1 and right[2] == 1) or (left[2] == 0 and right[2] == 1): 
            return [max_complete, 0, 2] 
           if left[0] == right[0] and left[0] != 2**left[0] - 1: 
            return [max_complete, 0, 2] 
           return [left[0] + right[0] + 1, max_height + 1, left_child_only] 
          else: 
           return [0,0,0] 
      
    0

    我到达一个类似于米哈伊尔的解决方案的解决方案(在我阅读EPI书籍时遇到)。我已经测试了一棵完整的树,一棵完美的树,一棵完整的树以及带有完整子树的树,但没有穷尽。

    /** 
    * Returns the largest complete subtree of a binary tree given by the input node 
    * 
    * @param root the root of the tree 
    */ 
    public int getLargestCompleteSubtree(INode<TKeyType, TValueType> root) { 
        max = 0; 
        calculateLargestCompleteSubtree(root); 
    
        return max; 
    } 
    
    /** 
    * Returns the largest complete subtree of a binary tree given by the input node 
    * 
    * @param root the root of the tree 
    */ 
    public TreeInfo<TKeyType, TValueType> calculateLargestCompleteSubtree(INode<TKeyType, TValueType> root) { 
        int size = 0; 
    
        // a complete subtree must have the following attributes 
        // 1. All leaves must be within 1 of each other. 
        // 2. All leaves must be as far left as possible, i.e, L(child).count() > R(child).count() 
        // 3. A complete subtree may have only one node (L child) or two nodes (L, R). 
    
        if (root == null) 
        { 
         return new TreeInfo<>(true, 0); 
        } 
        else if (!root.hasLeftChild() && !root.hasRightChild()) 
        { 
         return new TreeInfo<>(true, 1); 
        } 
    
        // have children 
        TreeInfo<TKeyType, TValueType> leftInfo = calculateLargestCompleteSubtree(root.getLeft()); 
        TreeInfo<TKeyType, TValueType> rightInfo = calculateLargestCompleteSubtree(root.getRight()); 
    
        // case 1: not a complete tree. 
        if (!leftInfo.isComplete || !rightInfo.isComplete) 
        { 
         // Only one subtree is complete. Set it as the max and return false. 
         if(leftInfo.isComplete) { 
          max = Math.max(max, leftInfo.size); 
         } 
         else if(rightInfo.isComplete) 
         { 
          max = Math.max(max, rightInfo.size); 
         } 
    
         return new TreeInfo<>(false, -1); 
        } 
    
        // case 2: both subtrees complete 
        int delta = Math.abs(leftInfo.size - rightInfo.size); 
        if (delta <= 1) 
        { 
         // both are complete but R could be 1 greater...use L tree. 
         size = leftInfo.size + 1; 
         max = Math.max(max, size); 
         return new TreeInfo<>(true, size); 
        } 
        else 
        { 
         // limit to size of R + 1 if L - R > 1, otherwise L 
         if(leftInfo.size > rightInfo.size) 
         { 
          max = Math.max(max, leftInfo.size); 
          size = rightInfo.size + 1; 
         } 
         else 
         { 
          max = Math.max(max, rightInfo.size); 
          size = leftInfo.size; 
         } 
    
         return new TreeInfo<>(true, size + 1); 
        } 
    }