Binary Trees | Types | BFS | DFS

Binary Trees | Types | BFS | DFS

Photo by K8 on Unsplash

Introduction

  • Arrays and linked lists are flat structures, whereas a tree is a hierarchical structure.

  • If a tree has only 2 children, then it is called a binary tree.

Terminology

  • Root: The head of the tree is called the root node.

  • Leaf node: A node that doesn’t have any children.

  • Subtree: If we pick any node, we can consider it as a root node for its children, grandchildren, and so on. This subpart is called a subtree.

  • Ancestors: All the nodes that are above the root parent node are called the ancestors of the current node.

Types of Binary Tree

There are 5 types of binary tree

1. Full Binary tree

Either has 0 or 2 children.

2. Complete Binary tree

  • All the levels are filled except the last level.

  • The last level has all nodes as left as possible.

3. Perfect Binary Tree

All leaf nodes are at the same level.

4. Balanced Binary Tree

Height of tree at maximum can be log(N) base2. Where N is number of nodes in the tree.

5. Degenerate Tree

Every node has single child.

Application 1

Problem

Given an integer i. Print the maximum number of nodes on level i of a binary tree. (link)

Example 1:

Input: 5
Output: 16

Example 2:

Input: 1
Output: 1

Solution


class Solution {
    static int countNodes(int i) {
        return (int)Math.pow(2,i-1);
    }
}

Binary Tree Representation

Code 1

class Node{
    int data;

    Node left;

    Node right;

    public Node(int data){

        this.data = data;
    }
}

Code 2 (Leetcode version)

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */

Problem

You are given an array nodes. It contains 7 integers, which represents the value of nodes of the binary tree in level order traversal. You are also given a root of the tree which has a value equal to nodes[0].

Your task to construct a binary tree by creating nodes for the remaining 6 nodes.

Example:

Input: 
nodes = [1 2 3 4 5 6 7]
Output: 
         1
       /   \
     2       3
   /  \     /  \
   4  5    6   7
Explanation: 
The 7 node binary tree is represented above.

Constraints:

vec.length = 7

1<=vec[i]<=100

Solution

Approach 1 (For 7 nodes)

class Solution{

    public static void createTree(Node root0, ArrayList<Integer> v ){

        Node node2 = new Node(v.get(1));
        Node node3 = new Node(v.get(2));
        Node node4 = new Node(v.get(3));
        Node node5 = new Node(v.get(4));
        Node node6 = new Node(v.get(5));
        Node node7 = new Node(v.get(6));

        root0.left = node2;
        root0.right = node3;

        node2.left = node4;
        node2.right = node5;


        node3.left = node6;
        node3.right = node7;
    }
}

Approach 2 (For n nodes)

class Solution{

    private static void createSubTree(Node temp, ArrayList<Integer> v, int index){
        if(index>=v.size()) return;
        int leftIndex = 2*index+1;
        int rightIndex = 2*index+2;
        if(leftIndex<v.size()) {
            temp.left = new Node(v.get(leftIndex));
            createSubTree(temp.left, v, leftIndex);
        }
        if(rightIndex<v.size()) {
            temp.right = new Node(v.get(rightIndex));
            createSubTree(temp.right, v, rightIndex);
        }

    }
    public static void createTree(Node root0, ArrayList<Integer> v ){
        // Code here
        createSubTree(root0, v, 0);
    }
}

Traversals

There are 2 types of traversals

  • DFS - Depth First Search

  • BFS - Breadth First Search

DFS

There are 3 types of DFS traversals

  • Inorder traversal (Root in between Left and Right)

  • Pre-order traversal (Root before Left and right)

  • Post-order traversal (Root after Left and right)

Inorder traversal (Left-Root-Right)

Problem

Given the root of a binary tree, return the inorder traversal of its nodes' values.(link)

Example 1:

Input: root = [1,null,2,3]

Output: [1,3,2]

Solution

class Solution {
    private void inorder(TreeNode root, List<Integer> ans){
        if(root==null) return;

        inorder(root.left,ans);
        ans.add(root.val);
        inorder(root.right,ans);
    }
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> ans = new ArrayList<>();
        inorder(root,ans);
        return ans;
    }
}

Pre-order traversal (Root-Left-Right)

Problem

Given the root of a binary tree, return the preorder traversal of its nodes' values. (link)

Example 1:

Input: root = [1,null,2,3]

Output: [1,2,3]

Solution

class Solution {
    private void preorder(TreeNode root, List<Integer> ans){
        if(root==null) return;

        ans.add(root.val);
        preorder(root.left,ans);
        preorder(root.right,ans);
    }

    public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> ans = new ArrayList<>();
        preorder(root,ans);
        return ans;
    }
}

Post-order traversal (Left-Right-Root)

Problem

Given the root of a binary tree, return the postorder traversal of its nodes' values. (link)

Example 1:

Input: root = [1,null,2,3]

Output: [3,2,1]

Solution

class Solution {
    private void postorder(TreeNode root, List<Integer> ans){
        if(root==null) return;

        postorder(root.left,ans);
        postorder(root.right,ans);
        ans.add(root.val);
    }
    public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> ans = new ArrayList<>();
        postorder(root,ans);
        return ans;
    }
}

Time and Space Complexties

Time - O(n)

Space - O(n) Auxilary stack space

If the tree has n nodes, then our code has to go through all the nodes, hence the time complexity is O(n).

For space, if the tree is a degenerate binary tree, then all the nodes are attached just like a linked list (single path), hence O(n) for auxiliary space. Or we can say the space is length of the maximum path in the tree.

BFS - Breadth Fast Search

BFS is a level order traversal. It traveses the binary tree level-wise.

Problem

Given the root of a binary tree, return the level order traversal of its nodes' values. (i.e., from left to right, level by level). (link)

Example 1:

Input: root = [3,9,20,null,null,15,7]
Output: [[3],[9,20],[15,7]]

Solution

The main idea in BFS is to store the current level nodes in the queue, pop each one from the front, and add all its children to the end of the queue. We continue this process of popping and adding children until the level size is reached. After that, the nodes in the queue move to the next level iteration. When we pop the nodes, we also store the elements in the current level list. Once the level size is finished, we add the current level elements list to the all-level list array, which stores all the level lists.

class Solution {
    public List<List<Integer>> levelOrder(TreeNode root) {

        List<List<Integer>> ans = new ArrayList<>();

        if(root==null) return ans;

        Queue<TreeNode> queue = new ArrayDeque<>();
        queue.add(root);

        while(!queue.isEmpty()){
            List<Integer> level = new ArrayList<>();
            int size = queue.size();

            for(int i=0; i<size; i++){
                TreeNode currNode = queue.poll();
                level.add(currNode.val);
                if(currNode.left!=null) queue.add(currNode.left);
                if(currNode.right!=null) queue.add(currNode.right);
            }
            ans.add(level);
        }

        return ans;
    }
}

Time and Space Complexity

Time - O(n)

Space - O(n/2)

Since there are ( n ) nodes, visiting each node indicates a time complexity of ( O(n) ).

For space complexity:

  • O(1) for a degenerate binary tree.

  • O(n/2) for a complete binary tree with the last level completely filled.