top

Search

Software Key Tutorial

.

UpGrad

Software Key Tutorial

Top View Of Binary Tree

Introduction

We encounter different types of trees when working with data structures. Among them, binary trees are important. Now imagine standing on top of a tree and looking down. You would see the tree’s top. It is the concept of the top view of a binary tree in computer science.

A binary tree is a data structure in which each node can have a maximum of two children. These children are identified as the left and right children.

The top view is what you would see if you were to visualize a binary tree as a real tree and look down from above. As a result, you face the first node from each level from left to right.

Understanding this concept is important, as it forms the basis of many algorithms. It helps visualize how data is structured and stored in the tree.

Overview

Let's look closely at the 'top view of a binary tree'. This concept holds equal importance in many programming languages. Whether you are working with C++ or Java, understanding this concept is essential. For instance, knowing the top view of a binary tree in C++ and Java can help you develop efficient tree-based algorithms in these languages.

It is very important to keep practicing this concept to become an expert in coding. To help you master it, we will discuss in detail the top view of a binary tree example and discuss various methods to practice this concept.

What is a Binary Tree?

A binary tree is a data structure type having a unique starting point called the root, from which two other items, known as child nodes, branch off. 

Each of these child nodes can branch out into a maximum of two more nodes. This pattern continues and creates a structure that looks like a tree turned upside down - hence the name "tree".

Each node in the binary tree contains data. It has a pointer to the left child and another to the right child. When a node has no children, it's called a leaf node.

Top view of a binary tree example:


As we can see in this example, "1" is the root, and it has two children: "2" and "3". "2" and "3" are the parents of their respective children "4", "5", "6", and "7". "4", "5", "6", and "7" are leaf nodes because they don't have any children.

Top View of Binary Tree

The top view of a binary tree provides a unique perspective on the tree's structure. It represents the nodes seen while looking at the tree from the very top. This view considers the nodes in each vertical line, or depth level, in the order of their appearance.

Consider an observer that can move left or right, but only downward, starting at the root node.  As the observer travels and records the initial node at each depth level, they capture the top perspective of the tree once they have thoroughly investigated both the left and right sides.

In many branches of computer science, especially those working with data structures, understanding the top perspective is important. It facilitates a clearer understanding of the tree structure and is important for resolving many binary tree-related issues.

Key to Remember:

Whether you're working in Java, C++, or any other programming language, the theory remains the same, though the code implementation might differ.

Problem

To understand the top view of a binary tree, let's first visualize a real-world example. Picture yourself standing at the top of a tree and looking straight down. What do you see? You'll notice the highest branches and some lower ones that stick out to the side. This is similar to what we mean by the top view in a binary tree. The top view consists of the nodes that are observable when you observe the tree from its topmost point.


Solution

The top view of the above tree would include nodes

  • 4

  • 2

  • 1

  • 3

  • 7

These are the first nodes visible from the top at each level, from left to right.

There is an attached diagram of the same binary tree but with only the nodes in the top view visible to make things clear.


This shows how data is structured.

Depth First Search (DFS) / Inorder Traversal

Depth First Search (DFS), including inorder traversal,  is a way to traverse or search tree or graph data structures. In Depth-First Search (DFS), the strategy is to travel as far down a single path as you can, and only when you hit the end do you trace back and start exploring the next path. It's like walking through a maze and always choosing the next path until you hit a dead-end, then returning to choose another path.

Inorder traversal is a specific type of Depth First Search (DFS) where you first visit the left subtree, then the root node, and finally the right subtree. When performed on a binary search tree, it visits the nodes in ascending order (from the smallest to the largest).


The inorder traversal of this binary tree would be 4, 2, 5, 1, 6, 3, 7.

Algorithm to Implement the Above Approach


This algorithm represents the classes and relationships involved in creating a binary tree and performing a DFS with inorder traversal.

We have two classes here: TreeNode and BinaryTree.

TreeNode Class:

The TreeNode class represents a single node in a binary tree. Each node has three attributes:

  • data- This integer variable holds the value of the node.

  • left- This is a pointer that directs us to the left child of the node.

  • right- This pointer directs us to the right child of the node.

The TreeNode class also has a constructor method, TreeNode(int data), which is used to create a new node with a given data value.

BinaryTree Class:

The BinaryTree class represents the whole binary tree. It has two attributes:

  • Root- This is a pointer to the root node of the tree.

  • BinaryTree()- This constructor method creates a new binary tree.

The BinaryTree class also has two methods:

  • insert(int data)- This method adds a new node with a given data value to the tree.

  • dfsInorderTraversal(TreeNode node)- This method performs a DFS Inorder Traversal starting at a given node.

Relationships-

Each TreeNode instance has relationships with up to two other TreeNode instances (its children). It's depicted in the diagram with the "1 *.. '0..2'" line, showing each TreeNode can have 0 to 2 children.

The BinaryTree instance has a relationship with one TreeNode instance - its root. This relationship is depicted as "1 *-- '0..1'", showing that the BinaryTree has exactly one root.

Breadth First Search (BFS) / Level Order Traversal

Breadth First Search (BFS), also known as Level Order Traversal, is another method for exploring a tree or graph data structure. Unlike Depth First Search (DFS), BFS explores all the nodes at the current depth, or 'level', before moving on to nodes at the next depth level.

In a BFS, or Level Order traversal, on a binary tree, we start at the root, then visit all nodes at depth 1, followed by all nodes at depth 2, and so on.


If we apply the Level Order Traversal technique to this binary tree, the output would be 1, 2, 3, 4, 5, 6, 7.

Here's how it works:

  1. Start at the root node (1). Visit this node.

  2. Move to the next level and visit all nodes at this depth: (2), then (3).

  3. Move to the next level and visit all nodes at this depth: (4), (5), (6), and (7).

BFS or Level Order Traversal can be useful when you want to find the shortest path in a tree or graph or when you want to visit nodes in order of their depth. It's a fundamental method in many algorithms involving trees or graphs.

Top View Of Binary Tree In C++

In C++, we implement the top view of a binary tree by combining a level order traversal (Breadth-First Search) with a technique that tracks the horizontal distance of each node from the root.

  • Define the Node Structure-

We first define the node structure in C++. It includes the data value and pointers to the left and right child nodes.

Code-

struct Node {
    int data;
    Node* left;
    Node* right;
};
  •  Create a Function to Make New Nodes-

Next, we create a function to create new nodes easily.

Code-

Node* makeNode(int data) {
    Node* node = new Node();
    if (!node) {
        cout << "Memory error\n";
        return NULL;
    }
    node->data = data;
    node->left = node->right = NULL;
    return node;
}
  • Implement the Top View Function-

Finally, we implement the function to print the top view. We use a queue for the level order traversal and a map to store nodes along with their horizontal distances.

Code-

void topView(struct Node* root) {
    if (root == NULL) return;
    queue<pair<Node*, int>> q;
    map<int, int> m;
    q.push({ root, 0 });

    while (!q.empty()) {
        Node* temp = q.front().first;
        int h = q.front().second;
        q.pop();

        if (!m[h]) m[h] = temp->data;
        if (temp->left) q.push({ temp->left, h - 1 });
        if (temp->right) q.push({ temp->right, h + 1 });
    }

    for (auto i = m.begin(); i != m.end(); i++) {
        cout << i->second << " ";
    }
}

A simple explanation of the given code:

  • Check for an Empty Tree- The topView function first checks if the tree is empty. If the root node is NULL, the function will return immediately because there's nothing to process.

  • Queue and Map Creation- The function creates a queue (q) to store pairs of nodes and their horizontal distances and a map (m) to store the node values for each horizontal distance.

  • Root Node Processing- The function then pushes the root node into the queue with a horizontal distance of 0.

  • Queue Processing- As long as the queue isn't empty, the function removes the front node and gets its data (temp) and horizontal distance (h).

  • Map Updating- If the map doesn't already have a node for the current horizontal distance, the function adds the current node's data to the map. This means that it only keeps the first node encountered at each horizontal distance, which is what we need for the top view.

  • Child Nodes- If the current node has left or right children, the function pushes them onto the queue with their respective horizontal distances (h - 1 for left, h + 1 for right).

  • Printing the Top View- After the queue is empty (meaning all nodes have been processed), the function iterates over the map and prints the node data for each horizontal distance, giving the top view of the binary tree.

Test the Code with a Binary Tree-

Now we can test our code with a binary tree.

Code-

int main() {
    Node* root = makeNode(1);
    root->left = makeNode(2);
    root->right = makeNode(3);
    root->left->left = makeNode(4);
    root->left->right = makeNode(5);
    root->right->left = makeNode(6);
    root->right->right = makeNode(7);

    cout << "Top view of the given binary tree is: ";
    topView(root);
    return 0;
}
  •  Output-

When you run the program, the output will be:

Top View Of Binary Tree Java

In the case of Java, we'll be using a TreeMap to keep track of each node's horizontal distance and a Queue to perform a level order traversal.

Firstly, we'll define a “Node” class:

Code-

class Node {
    int data;
    Node left, right;


    public Node(int item) {
        data = item;
        left = right = null;
    }
}

Next, we'll create a BinaryTree class and implement the function to print the top view:

Code-

import java.util.*;

class BinaryTree {
    Node root;

    class QueueObj {
        Node node;
        int hd;

        QueueObj(Node node, int hd) {
            this.node = node;
            this.hd = hd;
        }
    }

    public void topView(Node root) {
        if (root == null) return;

        Queue<QueueObj> queue = new LinkedList<>();
        Map<Integer, Node> topViewMap = new TreeMap<>();

        queue.add(new QueueObj(root, 0));

        while (!queue.isEmpty()) {
            QueueObj tmpNode = queue.poll();
            if (!topViewMap.containsKey(tmpNode.hd)) {
                topViewMap.put(tmpNode.hd, tmpNode.node);
            }

            if (tmpNode.node.left != null) {
                queue.add(new QueueObj(tmpNode.node.left, tmpNode.hd - 1));
            }

            if (tmpNode.node.right != null) {
                queue.add(new QueueObj(tmpNode.node.right, tmpNode.hd + 1));
            }
        }

        for (Map.Entry<Integer, Node> entry : topViewMap.entrySet()) {
            System.out.print(entry.getValue().data + " ");
        }
    }
}

A simple explanation of the above codes:

  1. Node Creation- Firstly, a Node class is created with fields for data and left and right nodes.

  2. QueueObj Creation- A QueueObj class is also defined within the BinaryTree class, which wraps a Node and a horizontal distance (hd).

  3. Top View Method- The topView method is then defined in the BinaryTree class. This method generates the top view of a binary tree. If the binary tree is empty (root is null), the method simply returns.

  4. Queue and Map- A queue of QueueObj and a TreeMap are created to store nodes in the top view.

  5. Queue Insertion- The root node is wrapped in a QueueObj with a horizontal distance of 0 and added to the queue.

  6. Queue Processing- While the queue is not empty, nodes are dequeued. If the dequeued node's horizontal distance hasn't been seen before, it's added to the TreeMap.

  7. Child Node Processing- If the dequeued node has a left or right child, these are each wrapped in a QueueObj with their respective horizontal distances and added to the queue.

  8. Printing the Top View- Finally, the method iterates over the TreeMap and prints the data of each node, which gives the top view of the binary tree.

Now, let's test the above code with a binary tree

Code-

public class Main {
    public static void main(String[] args) {
        BinaryTree tree = new BinaryTree();
        tree.root = new Node(1);
        tree.root.left = new Node(2);
        tree.root.right = new Node(3);
        tree.root.left.left = new Node(4);
        tree.root.left.right = new Node(5);
        tree.root.right.left = new Node(6);
        tree.root.right.right = new Node(7);
        System.out.println("Top view of the given binary tree is: ");
        tree.topView(tree.root);
    }
}

Output-

Conclusion

Understanding the top view of a binary tree is vital for mastering tree data structures. With the practical examples and step-by-step instructions provided, you should now have a good grasp of how to implement it in C++ and Java. To solidify your understanding, consider undertaking some top view of binary tree practice. These will further equip you with the skills needed to tackle more complex problems involving binary trees.

FAQs

  1. What is a leaf node in a binary tree?
    A leaf node is a node without any children.

  2. Why use a binary tree over a linked list?

         Binary trees, especially balanced ones, allow faster search, insertion, and deletion operations than linked lists.

  1. How is the height of a binary tree determined?

         The height is the longest path from the root node to the leaf node.

  1. What's the difference between a binary tree and a binary search tree (BST)?

         In a BST, nodes on the left are always less than the root, and nodes on the right are always greater.

  1. How does a balanced binary tree help?

         Balanced binary trees ensure operations like search, insert, and delete run in logarithmic time, preventing worst-case scenarios.

Leave a Reply

Your email address will not be published. Required fields are marked *