For two nodes, a and b, the lowest common ancestor c is the lowest node in the binary tree that has a and b as its descendants.
Two nodes may have more than one common ancestor, however, they can have only one lowest common ancestor
There are many ways in which we can calculate the lowest common ancestor of two nodes. We will discuss two common methods.
In this method we will iterate from the node a to the root of the tree, while saving the ancestors of the node in a vector. Next, we will iterate from the node b to the root of the tree and determine whether the ancestors of a appear in the ancestors of b. The first ancestor of b which is also an ancestor of a will be the lowest common ancestor. This method will require a total of two traversals of the binary tree.
This method will require a total of two traversals of the binary tree. So the Time-Complexity will be O(2h) ≈ O(h)
where h
is the height of the tree.
This method will store the ancestors of node a in a vector. So in the worst-case scenario, a is the leaf of the tree at the greatest depth. So a would have h
ancestors.
The Space-Complexity would hence be O(h)
.
Below we have defined a binary tree in C++ and python that has the same structure as the illustration above.
#include <iostream>#include <string>#include <iomanip>#include <vector>using namespace std;struct Node{public :char data;Node *left_Node;// The left subtree of the nodeNode *right_Node; // The right subtree of the nodeNode *parent;// Constructor method;Node(char input_data){data = input_data; // Initializes the Node data to input data}};Node* return_node(char input_data){Node *newnode = new Node(input_data);return (newnode);}struct BinaryTree{public:Node* root;// Utility functionsBinaryTree(){root = NULL;}BinaryTree(char input_data){root = return_node(input_data);}};Node* lowest_common_ancestor(Node *node1,Node *node2){vector<Node*> node1_ancestors;// Initialize a vector to store information inNode* curr = node1;while(curr!=NULL){curr = curr -> parent;if(curr!=NULL){node1_ancestors.push_back(curr);// Store the ancestors of node1}}Node* lowest_ancestor = NULL;curr = node2;while(curr!=NULL){curr = curr->parent;for(int i=0;i<node1_ancestors.size();i++){if(node1_ancestors[i]==curr){ // Find the first ancestor common to both node1 and node2lowest_ancestor = curr;return lowest_ancestor;// Return THE lowest common ancestor}}}return lowest_ancestor;// Otherwise return null}int main() {// Initializing a binary treestruct BinaryTree newTree('d');// Adding left and right plus the parent for each nodenewTree.root->left_Node = return_node('c');newTree.root->left_Node->parent = newTree.root;newTree.root->right_Node = return_node('e');newTree.root->right_Node->parent = newTree.root;newTree.root->left_Node->left_Node = return_node('a');newTree.root->left_Node->left_Node->parent =newTree.root->left_Node;newTree.root->left_Node->right_Node = return_node('b');newTree.root->left_Node->right_Node->parent =newTree.root->left_Node;newTree.root->right_Node->left_Node = return_node('f');newTree.root->right_Node->left_Node->parent =newTree.root->right_Node;newTree.root->right_Node->right_Node = return_node('g');newTree.root->right_Node->right_Node->parent =newTree.root->right_Node;// Call the method of two of the nodesNode* result = lowest_common_ancestor(newTree.root->right_Node->right_Node,newTree.root->right_Node->left_Node);cout <<"Node 1: "<<newTree.root->right_Node->right_Node->data<<endl;cout<<"Node2 : "<<newTree.root->right_Node->left_Node->data<<endl;cout <<"lowest common ancestor : "<< result->data<<endl;}
In this method we will use recursion to reduce the time required to find the lowest common ancestor. We will start from the root and traverse the tree towards the leaves. This method uses four if conditions.
This method will require one traversal of the binary tree. So the Time-Complexity will be O(m+n) ≈ O(n)
where m
is the number of edges and n
is the total number of nodes.
Another advantage of this method is that it does not use any data structure to store the ancestors of the nodes a and b. Hence, the space-complexity will be O(n)
.
The problem has been solved in C++ and python. This method uses the same four conditions as described above.
#include <iostream>#include <string>#include <iomanip>#include <vector>using namespace std;struct Node{public :char data;Node *left_Node;// The left subtree of the nodeNode *right_Node; // The right subtree of the nodeNode *parent;//Constructor method;Node(char input_data){data = input_data; //initializes the Node data to input data}};Node* return_node(char input_data){Node *newnode = new Node(input_data);return (newnode);}struct BinaryTree{public:Node* root;//utility functionsBinaryTree(){root = NULL;}BinaryTree(char input_data){root = return_node(input_data);}};Node* lowest_common_ancestor(Node *curr,Node *node1,Node *node2){if(curr==NULL){// if the curr node is NULL return NULL because we have reached the leafreturn NULL;}else if(curr==node1 || curr==node2){// if the current node is either node1 or node2, return the current node.return curr;}Node *left_subtree = lowest_common_ancestor(curr->left_Node,node1,node2);Node *right_subtree = lowest_common_ancestor(curr->right_Node,node1,node2);if(left_subtree!=NULL && right_subtree!=NULL){// if the curr node has node1 and node2 in it's left subtree and right subtree. Return the current nodereturn curr;}else if(left_subtree!=NULL){//return the left subtreereturn left_subtree;}else{return right_subtree;//otherwise return the right subtree.}return NULL;//lowest common ancestor is not found}int main() {// initializing a binary treestruct BinaryTree newTree('d');// adding left and right plus the parent for each nodenewTree.root->left_Node = return_node('c');newTree.root->left_Node->parent = newTree.root;newTree.root->right_Node = return_node('e');newTree.root->right_Node->parent = newTree.root;newTree.root->left_Node->left_Node = return_node('a');newTree.root->left_Node->left_Node->parent =newTree.root->left_Node;newTree.root->left_Node->right_Node = return_node('b');newTree.root->left_Node->right_Node->parent =newTree.root->left_Node;newTree.root->right_Node->left_Node = return_node('f');newTree.root->right_Node->left_Node->parent =newTree.root->right_Node;newTree.root->right_Node->right_Node = return_node('g');newTree.root->right_Node->right_Node->parent =newTree.root->right_Node;// call the method of two of the nodesNode* result = lowest_common_ancestor(newTree.root,newTree.root->right_Node->right_Node,newTree.root->right_Node->left_Node);cout <<"lowest common ancestor for nodes "<<newTree.root->right_Node->right_Node->data<< " and "<<newTree.root->right_Node->left_Node->data<<" is : " << result->data<<endl;return 0;}