二叉树中最大的完整子树

时间:2015-11-21 10:57:48

标签: algorithm

我将一个完整的子树定义为一个树,其中所有级别都已满,最后一级左对齐,即所有节点都尽可能地离开,我希望在树中找到最大的完整子树。

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

有更好的方法吗?

8 个答案:

答案 0 :(得分:4)

由于上面没有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};
}

答案 1 :(得分:3)

这是我在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]
    

答案 2 :(得分:2)

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

如果节点为零或只有一个子节点,则节点具有(r, w) = (1, 1)

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

    当节点有r1 > r2 时,
  1. (r2 + 1, 2^r2 + w2) 当节点有r1 == r2
  2. 时,
  3. w1 == 2^r1(r1 + 1, w1 + w2) 当节点有r1 == r2时,
  4. w1 < 2^r1(r1 + 1, w1)示例:
  5. 
             root     
             ....
        /  \     /   \
       l    l    r    r
      /\   /    /\    /
      l l  l    r r  r
    

    最大完整子树

    
              m     
             ....
        /  \     /   \
       m    m    m    m
      /\   /    /\    /
      m m  m    r r  r
    
      当节点有r1 < r2时,
    1. w1 == 2^r1(r1 + 1, 2 * w1)示例:
    2. 
               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 < r2 时,
      1. w1 < 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的节点,并且此节点应该是一个解决方案。

答案 3 :(得分:2)

我正在编写一个“编程访谈元素”的变体时遇到过这篇文章。我想分享我的想法和代码。

欢迎任何评论。

我正在使用递归来解决这个问题。 max用于存储有史以来发生的最大大小(我使用了一个数组,因为java是按值计算的)。 返回值信息包含有关树的信息 传入的是一棵完整的树。仅在完成时返回树大小,否则返回(-1,false)。 如果一个子树T&#39;如果不完整,则永远不会选择其大小来组成更大的完整树。并且所有T子树的大小总是以最大值记录,所以我们永远不会错过任何值。

以下是它的工作原理

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

  • 如果两者都不完整,则树不完整,无需更新 最大。如果其中任何一个完成,则表示树未完成,请将max更新为 左右两侧的尺寸越大。如果两者都完整,那么树 可能是完整的。首先检查左边是否完美,然后 正确满足高度要求。如果是,那么回来(真的, 新尺寸)。否则,树不完整,更新max为 更大的左右价值。

下面是我的代码。它应该是时间O(n)和空间O(h),其中h是树的高度。(如果它是平衡的,否则最坏的情况将是O(n))。< / p>

 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;
        }
    }
}

答案 4 :(得分:1)

在“编程访谈元素”一书中遇到了这个任务,似乎我发现了一个非常简单的解决方案,但仍然不确定它是否正确,但在几个案例中进行了测试并且它有效:

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));

        }

答案 5 :(得分: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);
    }
}

答案 6 :(得分:0)

python中的一种简单的递归方法。我已经测试了几棵树,到目前为止工作正常。

# return (is_complete, max_height_so_far, is_perfect)
def is_complete_tree(node):
    # null
    if not node:
        return (True, -1, True)

    left_subtree = is_complete_tree(node.left_child)
    right_subtree = is_complete_tree(node.right_child)

    # if any of subtrees isn't complete, current tree is not complete
    if not left_subtree[0] or not right_subtree[0]:
        return (False, max(left_subtree[1], right_subtree[1]), False)

    # if both subtrees are complete, there are 2 cases in order for current tree to be complete
    # case 1: subtrees with same height
    # left subtree must be perfect
    if left_subtree[1] == right_subtree[1] and left_subtree[2]:
        return (True, left_subtree[1] + 1, right_subtree[2])

    # case 2: left subtree taller by 1
    # right subtree must be perfect
    if left_subtree[1] == right_subtree[1] + 1 and right_subtree[2]:
        return (True, left_subtree[1] + 1, False)

    # otherwise not complete
    return (False, max(left_subtree[1], right_subtree[1]), False)

答案 7 :(得分:0)

这是我建议的解决方案:要点是跟踪子树的当前节点数,当前高度和最大高度,直到该点为止。

利用当前的节点数和高度,可以通过直接子节点各自的信息来计算根的节点数和高度,同时考虑子节点的高度之间的关系以及它们是否为完美的子树。

解决方案是O(n)时间复杂度和O(h)空间复杂度(函数调用堆栈从根到通过唯一路径到当前节点相对应)。

这是此解决方案的Python代码,您可以找到带有示例here的完整摘要:

from collections import namedtuple


class BTN():
    def __init__(self, data=None, left=None, right=None):
        self.data = data
        self.left = left
        self.right = right

# number of nodes for a perfect tree of the given height
def max_nodes_per_height(height: int) -> int:
    return 2**(height + 1) - 1


def height_largest_complete_subtree(root: BTN) -> int:
    CompleteInformation = namedtuple('CompleteInformation', ['height', 'num_nodes', 'max_height'])

    def height_largest_complete_subtree_aux(root: BTN) -> CompleteInformation:
        if (root is None):
            return CompleteInformation(-1, 0, 0)

        left_complete_info = height_largest_complete_subtree_aux(root.left)
        right_complete_info = height_largest_complete_subtree_aux(root.right)

        left_height = left_complete_info.height
        right_height = right_complete_info.height

        if (left_height == right_height):
            if (left_complete_info.num_nodes == max_nodes_per_height(left_height)):
                new_height = left_height + 1
                new_num_nodes = left_complete_info.num_nodes + right_complete_info.num_nodes + 1
                return CompleteInformation(new_height,
                                           new_num_nodes,
                                           max(new_height, max(left_complete_info.max_height, right_complete_info.max_height))
                                          )
            else:
                new_height = left_height
                new_num_nodes = max_nodes_per_height(left_height)
                return CompleteInformation(new_height,
                                           new_num_nodes,
                                           max(new_height, max(left_complete_info.max_height, right_complete_info.max_height))
                                          )
        elif (left_height > right_height):
            if (max_nodes_per_height(right_height) == right_complete_info.num_nodes):
                new_height = right_height + 2
                new_num_nodes = min(left_complete_info.num_nodes, max_nodes_per_height(right_height + 1)) + right_complete_info.num_nodes + 1
                return CompleteInformation(new_height,
                               new_num_nodes,
                               max(new_height, max(left_complete_info.max_height, right_complete_info.max_height))
                              )
            else:
                new_height = right_height + 1
                new_num_nodes = max_nodes_per_height(right_height) + right_complete_info.num_nodes + 1
                return CompleteInformation(new_height,
                               new_num_nodes,
                               max(new_height, max(left_complete_info.max_height, right_complete_info.max_height))
                              )

        elif (left_height < right_height):
            if (left_complete_info.num_nodes == max_nodes_per_height(left_height)):
                new_height = left_height + 1
                new_num_nodes = left_complete_info.num_nodes + max_nodes_per_height(left_height) + 1
                return CompleteInformation(new_height,
                           new_num_nodes,
                           max(new_height, max(left_complete_info.max_height, right_complete_info.max_height))
                          )
            else:
                new_height = left_height
                new_num_nodes = (max_nodes_per_height(left_height - 1) * 2) + 1
                return CompleteInformation(new_height,
                           new_num_nodes,
                           max(new_height, max(left_complete_info.max_height, right_complete_info.max_height))
                          )

    return height_largest_complete_subtree_aux(root).max_height