Boundary Level order traversal of a Binary Tree


In this problem, we will traverse each boundary of the given binary tree in the given order.

We will use the recursive approach to traverse each boundary of the binary tree one by one. However, we will also learn the iterative approach using a stack to traverse the binary tree's boundary and increase the code's performance.

Problem Statement

We have given a binary tree, and we need to traverse each boundary of the tree in the given order.

  • Traverse left boundary in top to bottom manner.

  • Traverse bottom boundary from left to right.

  • Traverse the right boundary from bottom to top.

Sample Examples

Input

   4
 /   \
8     9

Output

4, 8, 9

Explanation

We traverse the boundary of the tree.

Input

	 9
   /   \
   6    8
 /   \     
10    53 
     /   \
    12    3

Output

9, 6, 10, 12, 3, 8,

Explanation

We can consider the 9, 6, 10, and 12 into the left boundary. We can consider the 10, 12, and 3 as the bottom boundary. We can consider the 3, 8, and 9 as the right boundary. Here, we have printed all boundary elements once.

Approach 1

In this approach, we will write 3 recursive functions to traverse each node of the left, bottom, and right boundaries.

Algorithm

  • Step 1 − Create a treenode class containing the integer variable, left and right pointers, and constructor to initialize the node.

  • Step 2 − In the main() method, create a binary tree and execute the traverseBoundary() function.

  • Step 3 − In the traverseBoundary() function, execute the return statement if the head is null. Otherwise, print the value of the head node.

  • Step 4 − Call the showLeft() function for the left subtree to traverse the left boundary.

  • Step 4.1 − In the showLeft() function, if the head is null or doesn't contain a child node, return from the function.

  • Step 4.2 − If the left child exists, print its value, and call the showLeft() function by passing it as a parameter. Otherwise, print the value of the right child node, and execute the showLeft() function for the right child node.

  • Step 5 − Execute the showBottom() function to show the boundary of the left and right subtree.

  • Step 5.1 − In the showBottom() function, execute the return statement if the head is null.

  • Step 5.2 − Print its value if the current node is a leaf noue.

  • Step 5.3 − Execute the showBottom() function for the left and right child nodes.

  • Step 6 − Execute the showRight() function to traverse the right boundary.

  • Step 6.1 − In the showRIght() function, execute the return if the head is null or contains no child.

  • Step 6.2 − If the right node is not null, execute the showRight() function for the right subtree, and print the value of the right node. Here, we print the node value after the function call as we need to traverse the tree from bottom to top.

  • Step 6.3 − If the right child not exists but the left child exists, make a recursive call for the left child, and print its value afterward.

Example

#include <bits/stdc++.h>
using namespace std;

class treenode {
   public:
      int val;
      treenode *left, *right;
   // constructor
   treenode() {
      left = NULL;
      right = NULL;
   }
};
void showBottom(treenode *head) {
   // Base case
   if (head == NULL)
      return;
   // When head node is a leaf node
   if ((head->left) == NULL && (head->right) == NULL)
      cout << head->val << " ";
   // Traverse left subtree
   showBottom(head->left);
   // Traverse the right subtree
   showBottom(head->right);
}
void showLeft(treenode *head) {
   if (head == NULL && (head->left == NULL && head->right == NULL))
      return;
   // When the left child exists
   if (head->left != NULL) {
      cout << head->val << " ";
      // Recursive function call
      showLeft(head->left);
   } else if (head->right != NULL) {
      // When the left child not exists
      cout << head->val << " ";
      showLeft(head->right);
   }
}
void showRight(treenode *head) {
   // Base case
   if (head == NULL || (head->left == NULL && head->right == NULL))
      return;
   // When a right child is present
   if (head->right != NULL) {
      showRight(head->right);
      cout << head->val << " ";
   }
   // When the right child is not present
   else if (head->left != NULL) {
      showRight(head->left);
      cout << head->val << " ";
   }
}
void traverseBoundary(treenode *head) {
   // When the tree is null
   if (head == NULL)
      return;
   // pRoot node
   cout << head->val << " ";
   // Showing left boundary
   showLeft(head->left);
   // Showing the bottom of the left subtree
   showBottom(head->left);
   // Showing the bottom of the right subtree
   showBottom(head->right);
   // Show the right boundary
   showRight(head->right);
}
treenode *createNewNode(int val) {
   treenode *temp = new treenode();
   temp->val = val;
   return temp;
}
int main() {
   treenode *head = createNewNode(9);
   head->left = createNewNode(6);
   head->right = createNewNode(8);
   head->left->left = createNewNode(10);
   head->left->right = createNewNode(53);
   head->left->right->left = createNewNode(12);
   head->left->right->right = createNewNode(3);
   traverseBoundary(head);
}

Output

9 6 10 12 3 8
  • Time complexity − O(N) to traverse each node.

  • Space complexity − O(H) for recursion stack.

Approach 2

In this approach, we will use the stack data structure to traverse each boundary of the binary tree.

Algorithm

  • Step 1 − When the head is not null, follow the below steps.

  • Step 2 − If the head doesn't contain any child node, print its value and return from the function.

  • Step 3 − Define the l_nodes list. Traverse each left node of the tree and push into the l_nodes.

  • Step 4 − Define stack_1 and stack_2 to store all nodes and leaft nodes respectively. Insert the head node in stack1.

  • Step 5 − Now, make iterations until stack1 becomes empty.

  • Step 5.1 − In the loop, pop the node from the stack. If their child exists, insert child elements in the stack. If the child not exists, insert the node into the stack_2.

  • Step 6 − Now, insert all elements of stack_2 into the l_nodes.

  • Step 7 − Now, define the rightNode list, traverse all right nodes, and insert them into the list. Also, reverse the rightNodes list.

  • Step 8 − Append all nodes of rightNodes into the l_nodes list, and print elements of l_nodes.

Example

#include <bits/stdc++.h>
using namespace std;

class treenode {
   public:
      int val;
      treenode *left, *right;
   // constructor
   treenode() {
      left = NULL;
      right = NULL;
   }
};
void traverseBoundary(treenode *head) {
   if (head != NULL) {
      // For a tree with a single node
      if ((head->left == NULL) && (head->right == NULL)) {
         cout << head->val << endl;
         return;
      }        
      vector<treenode *> l_nodes; // To store node order
      l_nodes.push_back(head);        
      treenode *leftNode = head->left; // For left boundary traversal
      // Insert left noes in the list
      while (leftNode->left) {
         l_nodes.push_back(leftNode);
         leftNode = leftNode->left;
      }        
      stack<treenode *> stack_1; // To store all nodes        
      stack<treenode *> stack_2; // For storing leaf nodes      
      stack_1.push(head); // Insert head
      while (!stack_1.empty()) {
         treenode *temp = stack_1.top();
         stack_1.pop();
         // Insert left child in a stack
         if (temp->left)
            stack_1.push(temp->left);
            // Insert right child in the stack
            if (temp->right)
               stack_1.push(temp->right);
            // For leaf node
            else if (!temp->left && !temp->right)
               stack_2.push(temp);
      }
      // Show all leaf nodes
      while (!stack_2.empty()) {
         l_nodes.push_back(stack_2.top());
         stack_2.pop();
      }        
      vector<treenode *> rightNodes; // Right boundary
      treenode *r_node = head->right;
      while (r_node->right) {
         rightNodes.push_back(r_node);
         r_node = r_node->right;
      }        
      reverse(rightNodes.begin(), rightNodes.end()); // Revere right node order    
      l_nodes.insert(l_nodes.end(), rightNodes.begin(), rightNodes.end()); // Merge two list
      // Printing the boundary traversal
      for (auto p : l_nodes) {
         cout << p->val << " ";
      }
      cout << endl;
      return;
   }
}
treenode *createNewNode(int val) {
   treenode *temp = new treenode();
   temp->val = val;
   return temp;
}
int main() {
   treenode *head = createNewNode(9);
   head->left = createNewNode(6);
   head->right = createNewNode(8);
   head->left->left = createNewNode(10);
   head->left->right = createNewNode(53);
   head->left->right->left = createNewNode(12);
   head->left->right->right = createNewNode(3);
   traverseBoundary(head);
}

Output

9 6 10 12 3 8
  • Time complexity − O(N)

  • Space complexity − O(N)

The iterative approach using the stack is faster than the recursive approach but consumes more memory, which might not be suitable for handling large data.

Updated on: 25-Aug-2023

75 Views

Kickstart Your Career

Get certified by completing the course

Get Started
Advertisements