largest complete subtree in a binary tree

允我心安 提交于 2021-02-07 14:49:06

问题


I am defining a complete subtree as a tree with all levels full and the last level left justified i.e. all nodes are as far left as possible, and I want to find the largest subtree in a tree that is complete.

One method is to do the method outlined here for every node as root, which would take O(n^2) time.

Is there a better approach?


回答1:


Since there isn't a C++ solution above, I have added my solution. Let me know if you feel there is anything incorrect or any improvements that can be made.

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



回答2:


This is my solution in Python. It's working on the cases that I came up with. The meaning of the return values are as follow: [x,y,z]

  • x = size of the largest complete subtree up to this node
  • y = height of the subtree
  • z: 0 - complete subtree, 1 - there is a node with a left child only in this subtree, 2 - not a complete subtree

    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]
    



回答3:


Define a rank of tree node, as the height of max complete subtree if this node is root. Define a width of node as number of nodes in last level of max complete subtree if this node is root. So for each node in tree we have two number (r, w). And w <= 2^r.

If node has zero or only one child, then node has (r, w) = (1, 1).

If node has two children (r1, w1) and (r2, w2), we have several cases:

  1. r1 > r2 when node will have (r2 + 1, 2^r2 + w2)
  2. r1 == r2 and w1 == 2^r1 when node will have (r1 + 1, w1 + w2)
  3. r1 == r2 and w1 < 2^r1 when node will have (r1 + 1, w1) Example:

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

Max complete subtree is


          m     
         ....
    /  \     /   \
   m    m    m    m
  /\   /    /\    /
  m m  m    r r  r
  1. r1 < r2 and w1 == 2^r1 when node will have (r1 + 1, 2 * w1) Example:

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

Max complete subtree is


          m     
         ....
    /  \      /   \
   m    m     m    m
  /\   / \    /\    /\
 m  m  m  m   m m  m  m
             /
            r
  1. r1 < r2 and w1 < 2^r1 when node will have (r1 + 1, w1)

Example:


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

Max complete subtree is


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

Based on this rules you can calculate (r, w) for each node using a recursion. It will take O(n). When you find a node with max rank r among this nodes find node with max w and this node should be a solution.




回答4:


I came across this post while I was working on a variant of Elements of Programming Interviews. And I would like to share my idea and code.

Any comments are welcomed.

I am using recursion to solve this problem. max is used to store the maximum size ever occurred ( I used an array since java is by value). the return value info contains information about whether the tree passed in is a complete tree or not. Only return the tree size when it is complete, otherwise return (-1, false). If a subtree T' is not complete, its size will never be selected to compose a larger complete tree. And the size of all T's subtrees will always recorded in max, so we will never miss any values.

Below is how it works

  • Base case: root == null or root is leaf
  • Recursively handle left child and right child.
  • Process current tree based on return values of left/right child - leftInfo and rightInfo.

  • If neither is complete, the tree is not complete, no need to update max. If either is complete, the tree is not complete, update max to the greater size of left and right. If both are complete, the tree is possible to be complete. First check if the left is perfect, and right satisfy the height requirement. If they are, then return (true, newSize). Otherwise, the tree is not complete, update max to be greater value of left and right.

Below is my code.It should be time O(n) and space O(h) where h is the height of the tree.(If it is balanced, otherwise the worst case will be 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;
        }
    }
}



回答5:


Met this task in 'Elements of Programming Interviews' book, and it seems that I found a quite easy solution, still not sure if it's correct, but tested it on a couple of cases and it worked:

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

        }



回答6:


Here is my suggested solution: the gist is to keep track of the subtree's current number of nodes, current height and maximum height until that point.

With the current number of nodes and height, one can calculate the root's number of nodes and height via its direct childs respective information, taking into to consideration the relation between the child heights and if they are perfect subtrees or not.

Solution is O(n) time complexity and O(h) space complexity (function call stack corresponds from the root through the unique path to the current node).

Here is the Python code for this solution, and you can find the complete gist with examples 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



回答7:


I arrived at a solution similar to Mikhail's above (encountered as I go though the EPI book). I've tested it against a few permutations of a complete tree, a perfect tree, a full tree and trees with sub trees that are complete .. but not exhaustively.

/**
 * 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);
    }
}



回答8:


A simple recursive approach in python. I've tested for a few trees, worked so far.

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


来源:https://stackoverflow.com/questions/33842493/largest-complete-subtree-in-a-binary-tree

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!