Open In App

Find the largest Perfect Subtree in a given Binary Tree

Given a Binary Tree, the task is to find the size of largest Perfect sub-tree in the given Binary Tree. 

Perfect Binary Tree – A Binary tree is Perfect Binary Tree in which all internal nodes have two children and all leaves are at the same level.

Examples: 

Input: 
      1
    /   \
   2     3
 /  \   /
4    5 6
Output:
Size : 3
Inorder Traversal : 4 2 5
The following sub-tree is the maximum size Perfect sub-tree 
   2  
 /  \
4    5

Input:
         50
      /      \
   30         60
  /   \      /    \ 
 5    20   45      70
          /  \     /  \
         10   85  65  80
Output:
Size : 7
Inorder Traversal : 10 45 85 60 65 70 80

Approach: Simply traverse the tree in bottom up manner. Then on coming up in recursion from child to parent, we can pass information about sub-trees to the parent. The passed information can be used by the parent to do the Perfect Tree test (for parent node) only in constant time. A left sub-tree need to tell the parent whether it is a Perfect Binary Tree or not and also need to pass max height of the Perfect Binary Tree coming from left child. Similarly, the right sub-tree also needs to pass max height of Perfect Binary Tree coming from right child. 

The sub-trees need to pass the following information up the tree for finding the largest Perfect sub-tree so that we can compare the maximum height with the parent’s data to check the Perfect Binary Tree property.  

  1. There is a bool variable to check whether the left child or the right child sub-tree is Perfect or not.
  2. From left and right child calls in recursion we find out if parent sub-tree if Perfect or not by following 2 cases: 
    • If both left child and right child are perfect binary tree and have same heights then parent is also a Perfect Binary Tree with height plus one of its child.
    • If the above case is not true then parent cannot be perfect binary tree and simply returns max size Perfect Binary Tree coming from left or right sub-tree by comparing their heights.

Below is the implementation of the above approach: 




// C++ implementation of the approach
#include <bits/stdc++.h>
using namespace std;
 
// Node structure of the tree
struct node {
    int data;
    struct node* left;
    struct node* right;
};
 
// To create a new node
struct node* newNode(int data)
{
    struct node* node = (struct node*)malloc(sizeof(struct node));
    node->data = data;
    node->left = NULL;
    node->right = NULL;
    return node;
};
 
// Structure for return type of
// function findPerfectBinaryTree
struct returnType {
 
    // To store if sub-tree is perfect or not
    bool isPerfect;
 
    // Height of the tree
    int height;
 
    // Root of biggest perfect sub-tree
    node* rootTree;
};
 
// Function to return the biggest
// perfect binary sub-tree
returnType findPerfectBinaryTree(struct node* root)
{
 
    // Declaring returnType that
    // needs to be returned
    returnType rt;
 
    // If root is NULL then it is considered as
    // perfect binary tree of height 0
    if (root == NULL) {
        rt.isPerfect = true;
        rt.height = 0;
        rt.rootTree = NULL;
        return rt;
    }
 
    // Recursive call for left and right child
    returnType lv = findPerfectBinaryTree(root->left);
    returnType rv = findPerfectBinaryTree(root->right);
 
    // If both left and right sub-trees are perfect and
    // there height is also same then sub-tree root
    // is also perfect binary subtree with height
    // plus one of its child sub-trees
    if (lv.isPerfect && rv.isPerfect && lv.height == rv.height) {
        rt.height = lv.height + 1;
        rt.isPerfect = true;
        rt.rootTree = root;
        return rt;
    }
 
    // Else this sub-tree cannot be a perfect binary tree
    // and simply return the biggest sized perfect sub-tree
    // found till now in the left or right sub-trees
    rt.isPerfect = false;
    rt.height = max(lv.height, rv.height);
    rt.rootTree = (lv.height > rv.height ? lv.rootTree : rv.rootTree);
    return rt;
}
 
// Function to print the inorder traversal of the tree
void inorderPrint(node* root)
{
    if (root != NULL) {
        inorderPrint(root->left);
        cout << root->data << " ";
        inorderPrint(root->right);
    }
}
 
// Driver code
int main()
{
    // Create tree
    struct node* root = newNode(1);
    root->left = newNode(2);
    root->right = newNode(3);
    root->left->left = newNode(4);
    root->left->right = newNode(5);
    root->right->left = newNode(6);
 
    // Get the biggest sizes perfect binary sub-tree
    struct returnType ans = findPerfectBinaryTree(root);
 
    // Height of the found sub-tree
    int h = ans.height;
 
    cout << "Size : " << pow(2, h) - 1 << endl;
 
    // Print the inorder traversal of the found sub-tree
    cout << "Inorder Traversal : ";
    inorderPrint(ans.rootTree);
 
    return 0;
}




// Java implementation of the approach
import java.util.*;
 
class GFG
{
     
// Node structure of the tree
static class node
{
    int data;
    node left;
    node right;
};
 
// To create a new node
static node newNode(int data)
{
    node node = new node();
    node.data = data;
    node.left = null;
    node.right = null;
    return node;
};
 
// Structure for return type of
// function findPerfectBinaryTree
static class returnType
{
 
    // To store if sub-tree is perfect or not
    boolean isPerfect;
 
    // Height of the tree
    int height;
 
    // Root of biggest perfect sub-tree
    node rootTree;
};
 
// Function to return the biggest
// perfect binary sub-tree
static returnType findPerfectBinaryTree(node root)
{
 
    // Declaring returnType that
    // needs to be returned
    returnType rt = new returnType();
 
    // If root is null then it is considered as
    // perfect binary tree of height 0
    if (root == null)
    {
        rt.isPerfect = true;
        rt.height = 0;
        rt.rootTree = null;
        return rt;
    }
 
    // Recursive call for left and right child
    returnType lv = findPerfectBinaryTree(root.left);
    returnType rv = findPerfectBinaryTree(root.right);
 
    // If both left and right sub-trees are perfect and
    // there height is also same then sub-tree root
    // is also perfect binary subtree with height
    // plus one of its child sub-trees
    if (lv.isPerfect && rv.isPerfect &&
        lv.height == rv.height)
    {
        rt.height = lv.height + 1;
        rt.isPerfect = true;
        rt.rootTree = root;
        return rt;
    }
 
    // Else this sub-tree cannot be a perfect binary tree
    // and simply return the biggest sized perfect sub-tree
    // found till now in the left or right sub-trees
    rt.isPerfect = false;
    rt.height = Math.max(lv.height, rv.height);
    rt.rootTree = (lv.height > rv.height ?
                             lv.rootTree : rv.rootTree);
    return rt;
}
 
// Function to print the
// inorder traversal of the tree
static void inorderPrint(node root)
{
    if (root != null)
    {
        inorderPrint(root.left);
        System.out.print(root.data + " ");
        inorderPrint(root.right);
    }
}
 
// Driver code
public static void main(String[] args)
{
    // Create tree
    node root = newNode(1);
    root.left = newNode(2);
    root.right = newNode(3);
    root.left.left = newNode(4);
    root.left.right = newNode(5);
    root.right.left = newNode(6);
 
    // Get the biggest sizes perfect binary sub-tree
    returnType ans = findPerfectBinaryTree(root);
 
    // Height of the found sub-tree
    int h = ans.height;
 
    System.out.println("Size : " +
                      (Math.pow(2, h) - 1));
 
    // Print the inorder traversal of the found sub-tree
    System.out.print("Inorder Traversal : ");
    inorderPrint(ans.rootTree);
}
}
 
// This code is contributed by 29AjayKumar




# Python3 implementation of above approach
 
# Tree node
class Node:
    def __init__(self, data):
        self.data = data
        self.left = None
        self.right = None
 
# To create a new node
def newNode(data):
 
    node = Node(0)
    node.data = data
    node.left = None
    node.right = None
    return node
 
# Structure for return type of
# function findPerfectBinaryTree
class returnType:
 
    def __init__(self):
         
        # To store if sub-tree is perfect or not
        isPerfect = 0
 
        # Height of the tree
        height = 0
 
        # Root of biggest perfect sub-tree
        rootTree = 0
 
# Function to return the biggest
# perfect binary sub-tree
def findPerfectBinaryTree(root):
 
    # Declaring returnType that
    # needs to be returned
    rt = returnType()
 
    # If root is None then it is considered as
    # perfect binary tree of height 0
    if (root == None) :
        rt.isPerfect = True
        rt.height = 0
        rt.rootTree = None
        return rt
     
    # Recursive call for left and right child
    lv = findPerfectBinaryTree(root.left)
    rv = findPerfectBinaryTree(root.right)
 
    # If both left and right sub-trees are perfect and
    # there height is also same then sub-tree root
    # is also perfect binary subtree with height
    # plus one of its child sub-trees
    if (lv.isPerfect and rv.isPerfect and
        lv.height == rv.height) :
        rt.height = lv.height + 1
        rt.isPerfect = True
        rt.rootTree = root
        return rt
     
    # Else this sub-tree cannot be a perfect binary tree
    # and simply return the biggest sized perfect sub-tree
    # found till now in the left or right sub-trees
    rt.isPerfect = False
    rt.height = max(lv.height, rv.height)
    if (lv.height > rv.height ):
        rt.rootTree = lv.rootTree
    else :
        rt.rootTree = rv.rootTree
    return rt
 
# Function to print the inorder traversal of the tree
def inorderPrint(root):
 
    if (root != None) :
        inorderPrint(root.left)
        print (root.data, end = " ")
        inorderPrint(root.right)
     
# Driver code
 
# Create tree
root = newNode(1)
root.left = newNode(2)
root.right = newNode(3)
root.left.left = newNode(4)
root.left.right = newNode(5)
root.right.left = newNode(6)
 
# Get the biggest sizes perfect binary sub-tree
ans = findPerfectBinaryTree(root)
 
# Height of the found sub-tree
h = ans.height
 
print ("Size : " , pow(2, h) - 1)
 
# Print the inorder traversal of the found sub-tree
print ("Inorder Traversal : ", end = " ")
inorderPrint(ans.rootTree)
 
# This code is contributed by Arnab Kundu




// C# implementation of the approach
using System;
 
class GFG
{
     
// Node structure of the tree
public class node
{
    public int data;
    public node left;
    public node right;
};
 
// To create a new node
static node newNode(int data)
{
    node node = new node();
    node.data = data;
    node.left = null;
    node.right = null;
    return node;
}
 
// Structure for return type of
// function findPerfectBinaryTree
public class returnType
{
 
    // To store if sub-tree is perfect or not
    public bool isPerfect;
 
    // Height of the tree
    public int height;
 
    // Root of biggest perfect sub-tree
    public node rootTree;
};
 
// Function to return the biggest
// perfect binary sub-tree
static returnType findPerfectBinaryTree(node root)
{
 
    // Declaring returnType that
    // needs to be returned
    returnType rt = new returnType();
 
    // If root is null then it is considered as
    // perfect binary tree of height 0
    if (root == null)
    {
        rt.isPerfect = true;
        rt.height = 0;
        rt.rootTree = null;
        return rt;
    }
 
    // Recursive call for left and right child
    returnType lv = findPerfectBinaryTree(root.left);
    returnType rv = findPerfectBinaryTree(root.right);
 
    // If both left and right sub-trees are perfect and
    // there height is also same then sub-tree root
    // is also perfect binary subtree with height
    // plus one of its child sub-trees
    if (lv.isPerfect && rv.isPerfect &&
        lv.height == rv.height)
    {
        rt.height = lv.height + 1;
        rt.isPerfect = true;
        rt.rootTree = root;
        return rt;
    }
 
    // Else this sub-tree cannot be a perfect binary tree
    // and simply return the biggest sized perfect sub-tree
    // found till now in the left or right sub-trees
    rt.isPerfect = false;
    rt.height = Math.Max(lv.height, rv.height);
    rt.rootTree = (lv.height > rv.height ?
                             lv.rootTree : rv.rootTree);
    return rt;
}
 
// Function to print the
// inorder traversal of the tree
static void inorderPrint(node root)
{
    if (root != null)
    {
        inorderPrint(root.left);
        Console.Write(root.data + " ");
        inorderPrint(root.right);
    }
}
 
// Driver code
public static void Main(String[] args)
{
    // Create tree
    node root = newNode(1);
    root.left = newNode(2);
    root.right = newNode(3);
    root.left.left = newNode(4);
    root.left.right = newNode(5);
    root.right.left = newNode(6);
 
    // Get the biggest sizes perfect binary sub-tree
    returnType ans = findPerfectBinaryTree(root);
 
    // Height of the found sub-tree
    int h = ans.height;
 
    Console.WriteLine("Size : " +
                     (Math.Pow(2, h) - 1));
 
    // Print the inorder traversal of the found sub-tree
    Console.Write("Inorder Traversal : ");
    inorderPrint(ans.rootTree);
}
}
 
// This code is contributed by Princi Singh




<script>
 
    // JavaScript program to print postorder
    // traversal iteratively
     
    // Node structure of the tree
    class node
    {
        constructor(data) {
           this.left = null;
           this.right = null;
           this.data = data;
        }
    }
     
    // To create a new node
    function newNode(data)
    {
        let Node = new node(data);
        return Node;
    }
     
    // Structure for return type of
    // function findPerfectBinaryTree
    class returnType
    {
        constructor(data) {
           this.isPerfect;
           this.height;
           this.rootTree;
        }
    }
 
    // Function to return the biggest
    // perfect binary sub-tree
    function findPerfectBinaryTree(root)
    {
 
        // Declaring returnType that
        // needs to be returned
        let rt = new returnType();
 
        // If root is null then it is considered as
        // perfect binary tree of height 0
        if (root == null)
        {
            rt.isPerfect = true;
            rt.height = 0;
            rt.rootTree = null;
            return rt;
        }
 
        // Recursive call for left and right child
        let lv = findPerfectBinaryTree(root.left);
        let rv = findPerfectBinaryTree(root.right);
 
        // If both left and right sub-trees are perfect and
        // there height is also same then sub-tree root
        // is also perfect binary subtree with height
        // plus one of its child sub-trees
        if (lv.isPerfect && rv.isPerfect &&
            lv.height == rv.height)
        {
            rt.height = lv.height + 1;
            rt.isPerfect = true;
            rt.rootTree = root;
            return rt;
        }
 
        // Else this sub-tree cannot be a perfect binary tree
        // and simply return the biggest sized perfect sub-tree
        // found till now in the left or right sub-trees
        rt.isPerfect = false;
        rt.height = Math.max(lv.height, rv.height);
        rt.rootTree = (lv.height > rv.height ?
                                 lv.rootTree : rv.rootTree);
        return rt;
    }
 
    // Function to print the
    // inorder traversal of the tree
    function inorderPrint(root)
    {
        if (root != null)
        {
            inorderPrint(root.left);
            document.write(root.data + " ");
            inorderPrint(root.right);
        }
    }
     
    // Create tree
    let root = newNode(1);
    root.left = newNode(2);
    root.right = newNode(3);
    root.left.left = newNode(4);
    root.left.right = newNode(5);
    root.right.left = newNode(6);
   
    // Get the biggest sizes perfect binary sub-tree
    let ans = findPerfectBinaryTree(root);
   
    // Height of the found sub-tree
    let h = ans.height;
   
    document.write("Size : " + (Math.pow(2, h) - 1) + "</br>");
   
    // Print the inorder traversal of the found sub-tree
    document.write("Inorder Traversal : ");
    inorderPrint(ans.rootTree);
 
</script>

Output: 
Size : 3
Inorder Traversal : 4 2 5

 

Time Complexity: O(n)
We are traversing the entire tree once.

Space Complexity: O(h)
For a given tree, we are storing the height of the tree in the returnType.


Article Tags :