Lowest Common Ancestor in a Binary Tree
Last Updated :
17 Apr, 2023
What is Lowest Common Ancestor in Binary Tree?
The lowest common ancestor is the lowest node in the tree that has both n1 and n2 as descendants, where n1 and n2 are the nodes for which we wish to find the LCA. Hence, the LCA of a binary tree with nodes n1 and n2 is the shared ancestor of n1 and n2 that is located farthest from the root.
Application of Lowest Common Ancestor(LCA):
To determine the distance between pairs of nodes in a tree: the distance from n1 to n2 can be computed as the distance from the root to n1, plus the distance from the root to n2, minus twice the distance from the root to their lowest common ancestor.
Lowest Common Ancestor in Binary Tree
Lowest Common Ancestor in a Binary Tree By Storing paths from root to n1 and root to n2:
The idea of this approach is to store the path from the root to n1 and root to n2 in two separate data structures. Then look simultaneously into the values stored in the data structure, and look for the first mismatch.
Illustration:
Find the LCA of 5 and 6
Path from root to 5 = { 1, 2, 5 }
Path from root to 6 = { 1, 3, 6 }
- We start checking from 0 index. As both of the value matches( pathA[0] = pathB[0] ), we move to the next index.
- pathA[1] not equals to pathB[1], there’s a mismatch so we consider the previous value.
- Therefore the LCA of (5,6) = 1
Follow the steps below to solve the problem:
- Find a path from the root to n1 and store it in a vector or array.
- Find a path from the root to n2 and store it in another vector or array.
- Traverse both paths till the values in arrays are the same. Return the common element just before the mismatch.
Following is the implementation of the above algorithm:
C++
#include <bits/stdc++.h>
using namespace std;
struct Node {
int key;
struct Node *left, *right;
};
Node* newNode( int k)
{
Node* temp = new Node;
temp->key = k;
temp->left = temp->right = NULL;
return temp;
}
bool findPath(Node* root, vector< int >& path, int k)
{
if (root == NULL)
return false ;
path.push_back(root->key);
if (root->key == k)
return true ;
if ((root->left && findPath(root->left, path, k))
|| (root->right && findPath(root->right, path, k)))
return true ;
path.pop_back();
return false ;
}
int findLCA(Node* root, int n1, int n2)
{
vector< int > path1, path2;
if (!findPath(root, path1, n1)
|| !findPath(root, path2, n2))
return -1;
int i;
for (i = 0; i < path1.size() && i < path2.size(); i++)
if (path1[i] != path2[i])
break ;
return path1[i - 1];
}
int main()
{
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);
root->right->right = newNode(7);
cout << "LCA(4, 5) = " << findLCA(root, 4, 5);
cout << "\nLCA(4, 6) = " << findLCA(root, 4, 6);
cout << "\nLCA(3, 4) = " << findLCA(root, 3, 4);
cout << "\nLCA(2, 4) = " << findLCA(root, 2, 4);
return 0;
}
|
Java
import java.util.ArrayList;
import java.util.List;
class Node {
int data;
Node left, right;
Node( int value)
{
data = value;
left = right = null ;
}
}
public class BT_NoParentPtr_Solution1 {
Node root;
private List<Integer> path1 = new ArrayList<>();
private List<Integer> path2 = new ArrayList<>();
int findLCA( int n1, int n2)
{
path1.clear();
path2.clear();
return findLCAInternal(root, n1, n2);
}
private int findLCAInternal(Node root, int n1, int n2)
{
if (!findPath(root, n1, path1)
|| !findPath(root, n2, path2)) {
System.out.println((path1.size() > 0 )
? "n1 is present"
: "n1 is missing" );
System.out.println((path2.size() > 0 )
? "n2 is present"
: "n2 is missing" );
return - 1 ;
}
int i;
for (i = 0 ; i < path1.size() && i < path2.size();
i++) {
if (!path1.get(i).equals(path2.get(i)))
break ;
}
return path1.get(i - 1 );
}
private boolean findPath(Node root, int n,
List<Integer> path)
{
if (root == null ) {
return false ;
}
path.add(root.data);
if (root.data == n ||
findPath(root.left, n, path) ||
findPath(root.right, n, path)) {
return true ;
}
path.remove(path.size() - 1 );
return false ;
}
public static void main(String[] args)
{
BT_NoParentPtr_Solution1 tree
= new BT_NoParentPtr_Solution1();
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( "LCA(4, 5) = "
+ tree.findLCA( 4 , 5 ));
System.out.println( "LCA(4, 6) = "
+ tree.findLCA( 4 , 6 ));
System.out.println( "LCA(3, 4) = "
+ tree.findLCA( 3 , 4 ));
System.out.println( "LCA(2, 4) = "
+ tree.findLCA( 2 , 4 ));
}
}
|
Python3
class Node:
def __init__( self , key):
self .key = key
self .left = None
self .right = None
def findPath(root, path, k):
if root is None :
return False
path.append(root.key)
if root.key = = k:
return True
if ((root.left ! = None and findPath(root.left, path, k)) or
(root.right ! = None and findPath(root.right, path, k))):
return True
path.pop()
return False
def findLCA(root, n1, n2):
path1 = []
path2 = []
if ( not findPath(root, path1, n1) or not findPath(root, path2, n2)):
return - 1
i = 0
while (i < len (path1) and i < len (path2)):
if path1[i] ! = path2[i]:
break
i + = 1
return path1[i - 1 ]
if __name__ = = '__main__' :
root = Node( 1 )
root.left = Node( 2 )
root.right = Node( 3 )
root.left.left = Node( 4 )
root.left.right = Node( 5 )
root.right.left = Node( 6 )
root.right.right = Node( 7 )
print ( "LCA(4, 5) = %d" % (findLCA(root, 4 , 5 ,)))
print ( "LCA(4, 6) = %d" % (findLCA(root, 4 , 6 )))
print ( "LCA(3, 4) = %d" % (findLCA(root, 3 , 4 )))
print ( "LCA(2, 4) = %d" % (findLCA(root, 2 , 4 )))
|
C#
using System.Collections;
using System;
class Node {
public int data;
public Node left, right;
public Node( int value)
{
data = value;
left = right = null ;
}
}
public class BT_NoParentPtr_Solution1 {
Node root;
private ArrayList path1 = new ArrayList();
private ArrayList path2 = new ArrayList();
int findLCA( int n1, int n2)
{
path1.Clear();
path2.Clear();
return findLCAInternal(root, n1, n2);
}
private int findLCAInternal(Node root, int n1, int n2)
{
if (!findPath(root, n1, path1)
|| !findPath(root, n2, path2)) {
Console.Write((path1.Count > 0)
? "n1 is present"
: "n1 is missing" );
Console.Write((path2.Count > 0)
? "n2 is present"
: "n2 is missing" );
return -1;
}
int i;
for (i = 0; i < path1.Count && i < path2.Count;
i++) {
if (( int )path1[i] != ( int )path2[i])
break ;
}
return ( int )path1[i - 1];
}
private bool findPath(Node root, int n, ArrayList path)
{
if (root == null ) {
return false ;
}
path.Add(root.data);
if (root.data == n) {
return true ;
}
if (root.left != null
&& findPath(root.left, n, path)) {
return true ;
}
if (root.right != null
&& findPath(root.right, n, path)) {
return true ;
}
path.RemoveAt(path.Count - 1);
return false ;
}
public static void Main(String[] args)
{
BT_NoParentPtr_Solution1 tree
= new BT_NoParentPtr_Solution1();
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);
Console.Write( "LCA(4, 5) = " + tree.findLCA(4, 5));
Console.Write( "\nLCA(4, 6) = "
+ tree.findLCA(4, 6));
Console.Write( "\nLCA(3, 4) = "
+ tree.findLCA(3, 4));
Console.Write( "\nLCA(2, 4) = "
+ tree.findLCA(2, 4));
}
}
|
Javascript
<script>
class Node
{
constructor(value) {
this .left = null ;
this .right = null ;
this .data = value;
}
}
let root;
let path1 = [];
let path2 = [];
function findLCA(n1, n2) {
path1 = [];
path2 = [];
return findLCAInternal(root, n1, n2);
}
function findLCAInternal(root, n1, n2) {
if (!findPath(root, n1, path1) || !findPath(root, n2, path2))
{
document.write((path1.length > 0) ?
"n1 is present" : "n1 is missing" );
document.write((path2.length > 0) ?
"n2 is present" : "n2 is missing" );
return -1;
}
let i;
for (i = 0; i < path1.length && i < path2.length; i++) {
if (path1[i] != path2[i])
break ;
}
return path1[i-1];
}
function findPath(root, n, path)
{
if (root == null ) {
return false ;
}
path.push(root.data);
if (root.data == n) {
return true ;
}
if (root.left != null && findPath(root.left, n, path)) {
return true ;
}
if (root.right != null && findPath(root.right, n, path)) {
return true ;
}
path.pop();
return false ;
}
root = new Node(1);
root.left = new Node(2);
root.right = new Node(3);
root.left.left = new Node(4);
root.left.right = new Node(5);
root.right.left = new Node(6);
root.right.right = new Node(7);
document.write( "LCA(4, 5) = " + findLCA(4,5) + "</br>" );
document.write( "LCA(4, 6) = " + findLCA(4,6) + "</br>" );
document.write( "LCA(3, 4) = " + findLCA(3,4) + "</br>" );
document.write( "LCA(2, 4) = " + findLCA(2,4));
</script>
|
Output
LCA(4, 5) = 2
LCA(4, 6) = 1
LCA(3, 4) = 1
LCA(2, 4) = 2
Time Complexity: O(N). The tree is traversed twice, and then path arrays are compared.
Auxiliary Space: O(N). Extra Space for path1 and path2.
Lowest Common Ancestor in a Binary Tree By Single Traversal:
The idea is to traverse the tree starting from the root. If any of the given keys (n1 and n2) matches with the root, then the root is LCA (assuming that both keys are present). If the root doesn’t match with any of the keys, we recur for the left and right subtree.
- The node which has one key present in its left subtree and the other key present in the right subtree is the LCA.
- If both keys lie in the left subtree, then the left subtree has LCA also,
- Otherwise, LCA lies in the right subtree.
Illustration:
Find the LCA of 5 and 6
Root is pointing to the node with value 1, as its value doesn’t match with { 5, 6 }. We look for the key in left subtree and right subtree.
- Left Subtree :
- New Root = { 2 } ≠5 or 6, hence we will continue our recursion
- New Root = { 4 } , it’s left and right subtree is null, we will return NULL for this call
- New Root = { 5 } , value matches with 5 so will return the node with value 5
- The function call for root with value 2 will return a value of 5
- Right Subtree :
- Root = { 3 } ≠5 or 6 hence we continue our recursion
- Root = { 6 } = 5 or 6 , we will return the this node with value 6
- Root = { 7 } ≠5 or 6, we will return NULL
- So the function call for root with value 3 will return node with value 6
- As both the left subtree and right subtree of the node with value 1 is not NULL, so 1 is the LCA
Follow the steps below to solve the problem:
- We pass the root to a helper function and check if the value of the root matches any of n1 and n2.
- If YES, return the root
- else recursive call on the left and right subtree
- Basically, we do pre-order traversal, at first we check if the root->value matches with n1 or n2. Then traverse on the left and right subtree.
- If there is any root that returns one NULL and another NON-NULL value, we shall return the corresponding NON-NULL value for that node.
- The node that returns both NON-NULL values for both the left and right subtree, is our Lowest Common Ancestor.
Below is the implementation of the above approach.
C++
#include <bits/stdc++.h>
using namespace std;
struct Node {
struct Node *left, *right;
int key;
};
Node* newNode( int key)
{
Node* temp = new Node;
temp->key = key;
temp->left = temp->right = NULL;
return temp;
}
struct Node* findLCA( struct Node* root, int n1, int n2)
{
if (root == NULL)
return NULL;
if (root->key == n1 || root->key == n2)
return root;
Node* left_lca = findLCA(root->left, n1, n2);
Node* right_lca = findLCA(root->right, n1, n2);
if (left_lca && right_lca)
return root;
return (left_lca != NULL) ? left_lca : right_lca;
}
int main()
{
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);
root->right->right = newNode(7);
cout << "LCA(4, 5) = " << findLCA(root, 4, 5)->key;
cout << "\nLCA(4, 6) = " << findLCA(root, 4, 6)->key;
cout << "\nLCA(3, 4) = " << findLCA(root, 3, 4)->key;
cout << "\nLCA(2, 4) = " << findLCA(root, 2, 4)->key;
return 0;
}
|
C
#include <stdio.h>
#include <stdlib.h>
typedef struct Node {
struct Node *left, *right;
int key;
} Node;
Node* newNode( int key)
{
Node* temp = (Node*) malloc ( sizeof (Node));
temp->key = key;
temp->left = temp->right = NULL;
return temp;
}
Node* findLCA(Node* root, int n1, int n2)
{
if (root == NULL)
return NULL;
if (root->key == n1 || root->key == n2)
return root;
Node* left_lca = findLCA(root->left, n1, n2);
Node* right_lca = findLCA(root->right, n1, n2);
if (left_lca && right_lca)
return root;
return (left_lca != NULL) ? left_lca : right_lca;
}
int main()
{
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);
root->right->right = newNode(7);
printf ( "LCA(4, 5) = %d" , findLCA(root, 4, 5)->key);
printf ( "\nLCA(4, 6) = %d" , findLCA(root, 4, 6)->key);
printf ( "\nLCA(3, 4) = %d" , findLCA(root, 3, 4)->key);
printf ( "\nLCA(2, 4) = %d" , findLCA(root, 2, 4)->key);
return 0;
}
|
Java
class Node {
int data;
Node left, right;
public Node( int item)
{
data = item;
left = right = null ;
}
}
public class BinaryTree {
Node root;
Node findLCA( int n1, int n2)
{
return findLCA(root, n1, n2);
}
Node findLCA(Node node, int n1, int n2)
{
if (node == null )
return null ;
if (node.data == n1 || node.data == n2)
return node;
Node left_lca = findLCA(node.left, n1, n2);
Node right_lca = findLCA(node.right, n1, n2);
if (left_lca != null && right_lca != null )
return node;
return (left_lca != null ) ? left_lca : right_lca;
}
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( "LCA(4, 5) = "
+ tree.findLCA( 4 , 5 ).data);
System.out.println( "LCA(4, 6) = "
+ tree.findLCA( 4 , 6 ).data);
System.out.println( "LCA(3, 4) = "
+ tree.findLCA( 3 , 4 ).data);
System.out.println( "LCA(2, 4) = "
+ tree.findLCA( 2 , 4 ).data);
}
}
|
Python3
class Node:
def __init__( self , key):
self .key = key
self .left = None
self .right = None
def findLCA(root, n1, n2):
if root is None :
return None
if root.key = = n1 or root.key = = n2:
return root
left_lca = findLCA(root.left, n1, n2)
right_lca = findLCA(root.right, n1, n2)
if left_lca and right_lca:
return root
return left_lca if left_lca is not None else right_lca
if __name__ = = '__main__' :
root = Node( 1 )
root.left = Node( 2 )
root.right = Node( 3 )
root.left.left = Node( 4 )
root.left.right = Node( 5 )
root.right.left = Node( 6 )
root.right.right = Node( 7 )
print ( "LCA(4, 5) = " , findLCA(root, 4 , 5 ).key)
print ( "LCA(4, 6) = " , findLCA(root, 4 , 6 ).key)
print ( "LCA(3, 4) = " , findLCA(root, 3 , 4 ).key)
print ( "LCA(2, 4) = " , findLCA(root, 2 , 4 ).key)
|
C#
using System;
public class Node {
public int data;
public Node left, right;
public Node( int item)
{
data = item;
left = right = null ;
}
}
class BinaryTree {
Node root;
Node findLCA( int n1, int n2)
{
return findLCA(root, n1, n2);
}
Node findLCA(Node node, int n1, int n2)
{
if (node == null )
return null ;
if (node.data == n1 || node.data == n2)
return node;
Node left_lca = findLCA(node.left, n1, n2);
Node right_lca = findLCA(node.right, n1, n2);
if (left_lca != null && right_lca != null )
return node;
return (left_lca != null ) ? left_lca : right_lca;
}
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);
Console.WriteLine( "LCA(4, 5) = "
+ tree.findLCA(4, 5).data);
Console.WriteLine( "LCA(4, 6) = "
+ tree.findLCA(4, 6).data);
Console.WriteLine( "LCA(3, 4) = "
+ tree.findLCA(3, 4).data);
Console.WriteLine( "LCA(2, 4) = "
+ tree.findLCA(2, 4).data);
}
}
|
Javascript
<script>
class Node
{
constructor(item) {
this .left = null ;
this .right = null ;
this .data = item;
}
}
let root;
function findlCA(n1, n2)
{
return findLCA(root, n1, n2);
}
function findLCA(node, n1, n2)
{
if (node == null )
return null ;
if (node.data == n1 || node.data == n2)
return node;
let left_lca = findLCA(node.left, n1, n2);
let right_lca = findLCA(node.right, n1, n2);
if (left_lca!= null && right_lca!= null )
return node;
return (left_lca != null ) ? left_lca : right_lca;
}
root = new Node(1);
root.left = new Node(2);
root.right = new Node(3);
root.left.left = new Node(4);
root.left.right = new Node(5);
root.right.left = new Node(6);
root.right.right = new Node(7);
document.write( "LCA(4, 5) = " +
findlCA(4, 5).data + "</br>" );
document.write( "LCA(4, 6) = " +
findlCA(4, 6).data + "</br>" );
document.write( "LCA(3, 4) = " +
findlCA(3, 4).data + "</br>" );
document.write( "LCA(2, 4) = " +
findlCA(2, 4).data + "</br>" );
</script>
|
Output
LCA(4, 5) = 2
LCA(4, 6) = 1
LCA(3, 4) = 1
LCA(2, 4) = 2
Time Complexity: O(N) as the method does a simple tree traversal in a bottom-up fashion.
Auxiliary Space: O(H), where H is the height of the tree.
Note: The above method assumes that keys are present in Binary Tree. If one key is present and the other is absent, then it returns the present key as LCA (Ideally should have returned NULL). We can extend this method to handle all cases by checking if n1 and n2 are present in the tree first and then finding the LCA of n1 and n2. To check whether the node is present in the binary tree or not then traverse on the tree for both n1 and n2 nodes separately.
C++
#include <iostream>
using namespace std;
struct Node {
struct Node *left, *right;
int key;
};
Node* newNode( int key)
{
Node* temp = new Node;
temp->key = key;
temp->left = temp->right = NULL;
return temp;
}
struct Node* findLCAUtil( struct Node* root, int n1, int n2)
{
if (root == NULL)
return NULL;
if (root->key == n1 || root->key == n2)
return root;
Node* left_lca = findLCAUtil(root->left, n1, n2);
Node* right_lca = findLCAUtil(root->right, n1, n2);
if (left_lca and right_lca)
return root;
return (left_lca != NULL) ? left_lca : right_lca;
}
bool find(Node* root, int k)
{
if (root == NULL)
return false ;
if (root->key == k || find(root->left, k)
|| find(root->right, k))
return true ;
return false ;
}
Node* findLCA(Node* root, int n1, int n2)
{
if (find(root, n1) and find(root, n2))
return findLCAUtil(root, n1, n2);
return NULL;
}
int main()
{
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);
root->right->right = newNode(7);
Node* lca = findLCA(root, 4, 5);
if (lca != NULL)
cout << "LCA(4, 5) = " << lca->key;
else
cout << "Keys are not present " ;
lca = findLCA(root, 4, 10);
if (lca != NULL)
cout << "\nLCA(4, 10) = " << lca->key;
else
cout << "\nKeys are not present " ;
return 0;
}
|
Java
class Node {
int data;
Node left, right;
public Node( int item)
{
data = item;
left = right = null ;
}
}
public class BinaryTree {
Node root;
static boolean v1 = false , v2 = false ;
Node findLCAUtil(Node node, int n1, int n2)
{
if (node == null )
return null ;
Node temp = null ;
if (node.data == n1) {
v1 = true ;
temp = node;
}
if (node.data == n2) {
v2 = true ;
temp = node;
}
Node left_lca = findLCAUtil(node.left, n1, n2);
Node right_lca = findLCAUtil(node.right, n1, n2);
if (temp != null )
return temp;
if (left_lca != null && right_lca != null )
return node;
return (left_lca != null ) ? left_lca : right_lca;
}
Node findLCA( int n1, int n2)
{
v1 = false ;
v2 = false ;
Node lca = findLCAUtil(root, n1, n2);
if (v1 && v2)
return lca;
return null ;
}
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 );
Node lca = tree.findLCA( 4 , 5 );
if (lca != null )
System.out.println( "LCA(4, 5) = " + lca.data);
else
System.out.println( "Keys are not present" );
lca = tree.findLCA( 4 , 10 );
if (lca != null )
System.out.println( "LCA(4, 10) = " + lca.data);
else
System.out.println( "Keys are not present" );
}
}
|
Python3
class Node:
def __init__( self , key):
self .key = key
self .left = None
self .right = None
def findLCAUtil(root, n1, n2, v):
if root is None :
return None
if root.key = = n1:
v[ 0 ] = True
return root
if root.key = = n2:
v[ 1 ] = True
return root
left_lca = findLCAUtil(root.left, n1, n2, v)
right_lca = findLCAUtil(root.right, n1, n2, v)
if left_lca and right_lca:
return root
return left_lca if left_lca is not None else right_lca
def find(root, k):
if root is None :
return False
if (root.key = = k or find(root.left, k) or
find(root.right, k)):
return True
return False
def findLCA(root, n1, n2):
v = [ False , False ]
lca = findLCAUtil(root, n1, n2, v)
if (v[ 0 ] and v[ 1 ] or v[ 0 ] and find(lca, n2) or v[ 1 ] and
find(lca, n1)):
return lca
return None
root = Node( 1 )
root.left = Node( 2 )
root.right = Node( 3 )
root.left.left = Node( 4 )
root.left.right = Node( 5 )
root.right.left = Node( 6 )
root.right.right = Node( 7 )
lca = findLCA(root, 4 , 5 )
if lca is not None :
print ( "LCA(4, 5) = " , lca.key)
else :
print ( "Keys are not present" )
lca = findLCA(root, 4 , 10 )
if lca is not None :
print ( "LCA(4,10) = " , lca.key)
else :
print ( "Keys are not present" )
|
C#
using System;
public class Node {
public int data;
public Node left, right;
public Node( int item)
{
data = item;
left = right = null ;
}
}
public class BinaryTree {
public Node root;
public static bool v1 = false , v2 = false ;
public virtual Node findLCAUtil(Node node, int n1,
int n2)
{
if (node == null ) {
return null ;
}
Node temp = null ;
if (node.data == n1) {
v1 = true ;
temp = node;
}
if (node.data == n2) {
v2 = true ;
temp = node;
}
Node left_lca = findLCAUtil(node.left, n1, n2);
Node right_lca = findLCAUtil(node.right, n1, n2);
if (temp != null ) {
return temp;
}
if (left_lca != null && right_lca != null ) {
return node;
}
return (left_lca != null ) ? left_lca : right_lca;
}
public virtual Node findLCA( int n1, int n2)
{
v1 = false ;
v2 = false ;
Node lca = findLCAUtil(root, n1, n2);
if (v1 && v2) {
return lca;
}
return null ;
}
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);
Node lca = tree.findLCA(4, 5);
if (lca != null ) {
Console.WriteLine( "LCA(4, 5) = " + lca.data);
}
else {
Console.WriteLine( "Keys are not present" );
}
lca = tree.findLCA(4, 10);
if (lca != null ) {
Console.WriteLine( "LCA(4, 10) = " + lca.data);
}
else {
Console.WriteLine( "Keys are not present" );
}
}
}
|
Javascript
<script>
class Node
{
constructor(item)
{
this .data = item;
this .left = null ;
this .right = null ;
}
}
class BinaryTree{
constructor()
{
this .root = null ;
this .v1 = false ;
this .v2 = false ;
}
findLCAUtil(node, n1, n2)
{
if (node == null )
{
return null ;
}
var temp = null ;
if (node.data == n1)
{
this .v1 = true ;
temp = node;
}
if (node.data == n2)
{
this .v2 = true ;
temp = node;
}
var left_lca = this .findLCAUtil(node.left, n1, n2);
var right_lca = this .findLCAUtil(node.right, n1, n2);
if (temp != null )
{
return temp;
}
if (left_lca != null && right_lca != null )
{
return node;
}
return left_lca != null ? left_lca : right_lca;
}
findLCA(n1, n2)
{
this .v1 = false ;
this .v2 = false ;
var lca = this .findLCAUtil( this .root, n1, n2);
if ( this .v1 && this .v2)
{
return lca;
}
return null ;
}
}
var 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);
var lca = tree.findLCA(4, 5);
if (lca != null )
{
document.write( "LCA(4, 5) = " +
lca.data + "<br>" );
} else
{
document.write( "Keys are not present" + "<br>" );
}
lca = tree.findLCA(4, 10);
if (lca != null )
{
document.write( "LCA(4, 10) = " +
lca.data + "<br>" );
}
else
{
document.write( "Keys are not present" + "<br>" );
}
</script>
|
Output
LCA(4, 5) = 2
Keys are not present
Time Complexity: O(N) as the method does a simple tree traversal in a bottom-up fashion.
Auxiliary Space: O(H), where h is the height of the tree.
Using an Auxiliary data structure (hash table):
The basic idea behind the "Using an auxiliary data structure" approach for finding the lowest common ancestor of two nodes in a binary tree is to use a hash table or a map to store the parent pointers of each node. Once we have the parent pointers, we can traverse up from the first node and add all its ancestors to a set or a list. Then we can traverse up from the second node and check if each ancestor is already in the set or the list. The first ancestor that is already in the set or the list is the lowest common ancestor.
Follow the steps to implement the above approach:
- Create a hash table or a map to store the parent pointers of each node in the binary tree.
- Traverse the binary tree and populate the hash table or the map with the parent pointers for each node.
- Starting from the first node, traverse up the tree and add each ancestor to a set or a list.
- Starting from the second node, traverse up the tree and check if each ancestor is already in the set or the list. The first ancestor that is already in the set or the list is the lowest common ancestor.
- If no common ancestor is found, return null or any other value that indicates the absence of a common ancestor.
Below is the implementation for the above approach:
C++
#include <iostream>
#include <set>
#include <unordered_map>
#include <vector>
using namespace std;
struct Node {
int data;
Node* left;
Node* right;
};
Node* newNode( int data)
{
Node* node = new Node;
node->data = data;
node->left = NULL;
node->right = NULL;
return (node);
}
unordered_map<Node*, Node*> buildParentMap(Node* root)
{
unordered_map<Node*, Node*> parentMap;
parentMap[root] = NULL;
vector<Node*> queue = { root };
while (!queue.empty()) {
Node* node = queue.front();
queue.erase(queue.begin());
if (node->left) {
parentMap[node->left] = node;
queue.push_back(node->left);
}
if (node->right) {
parentMap[node->right] = node;
queue.push_back(node->right);
}
}
return parentMap;
}
int findLCA(Node* root, int n1, int n2)
{
unordered_map<Node*, Node*> parentMap
= buildParentMap(root);
Node* p = NULL;
Node* q = NULL;
vector<Node*> queue = { root };
while (!queue.empty()) {
Node* node = queue.front();
queue.erase(queue.begin());
if (node->data == n1) {
p = node;
}
if (node->data == n2) {
q = node;
}
if (node->left) {
queue.push_back(node->left);
}
if (node->right) {
queue.push_back(node->right);
}
}
set<Node*> ancestors;
while (p) {
ancestors.insert(p);
p = parentMap[p];
}
while (q) {
if (ancestors.find(q) != ancestors.end()) {
return q
->data;
}
q = parentMap[q];
}
return -1;
}
int main()
{
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);
root->right->right = newNode(7);
cout << "LCA(4, 5) = " << findLCA(root, 4, 5) << endl;
cout << "LCA(4, 6) = " << findLCA(root, 4, 6) << endl;
cout << "LCA(3,4) = " << findLCA(root, 3, 4) << endl;
cout << "LCA(2, 4) = " << findLCA(root, 2, 4) << endl;
return 0;
}
|
Java
import java.util.*;
class Node {
int data;
Node left, right;
public Node( int item)
{
data = item;
left = right = null ;
}
}
class Main {
static Map<Node, Node> buildParentMap(Node root)
{
Map<Node, Node> parentMap = new HashMap<>();
parentMap.put(root, null );
Queue<Node> queue = new LinkedList<>();
queue.add(root);
while (!queue.isEmpty()) {
Node node = queue.poll();
if (node.left != null ) {
parentMap.put(node.left, node);
queue.add(node.left);
}
if (node.right != null ) {
parentMap.put(node.right, node);
queue.add(node.right);
}
}
return parentMap;
}
static int findLCA(Node root, int n1, int n2)
{
Map<Node, Node> parentMap = buildParentMap(root);
Node p = null , q = null ;
Queue<Node> queue = new LinkedList<>();
queue.add(root);
while (!queue.isEmpty()) {
Node node = queue.poll();
if (node.data == n1) {
p = node;
}
if (node.data == n2) {
q = node;
}
if (node.left != null ) {
queue.add(node.left);
}
if (node.right != null ) {
queue.add(node.right);
}
}
Set<Node> ancestors = new HashSet<>();
while (p != null ) {
ancestors.add(p);
p = parentMap.get(p);
}
while (q != null ) {
if (ancestors.contains(q)) {
return q.data;
}
q = parentMap.get(q);
}
return - 1 ;
}
public static void main(String[] args)
{
Node root = new Node( 1 );
root.left = new Node( 2 );
root.right = new Node( 3 );
root.left.left = new Node( 4 );
root.left.right = new Node( 5 );
root.right.left = new Node( 6 );
root.right.right = new Node( 7 );
System.out.println( "LCA(4, 5) = "
+ findLCA(root, 4 , 5 ));
System.out.println( "LCA(4, 6) = "
+ findLCA(root, 4 , 6 ));
System.out.println( "LCA(3, 4) = "
+ findLCA(root, 3 , 4 ));
System.out.println( "LCA(3, 4) = "
+ findLCA(root, 2 , 4 ));
}
}
|
Python3
from collections import deque
class Node:
def __init__( self , data):
self .data = data
self .left = None
self .right = None
def buildParentMap(root):
parentMap = {}
parentMap[root] = None
queue = deque([root])
while queue:
node = queue.popleft()
if node.left:
parentMap[node.left] = node
queue.append(node.left)
if node.right:
parentMap[node.right] = node
queue.append(node.right)
return parentMap
def findLCA(root, n1, n2):
parentMap = buildParentMap(root)
p, q = None , None
queue = deque([root])
while queue:
node = queue.popleft()
if node.data = = n1:
p = node
if node.data = = n2:
q = node
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
ancestors = set ()
while p:
ancestors.add(p)
p = parentMap[p]
while q:
if q in ancestors:
return q.data
q = parentMap[q]
return - 1
if __name__ = = '__main__' :
root = Node( 1 )
root.left = Node( 2 )
root.right = Node( 3 )
root.left.left = Node( 4 )
root.left.right = Node( 5 )
root.right.left = Node( 6 )
root.right.right = Node( 7 )
print ( "LCA(4, 5) = " , findLCA(root, 4 , 5 ))
print ( "LCA(4, 6) = " , findLCA(root, 4 , 6 ))
print ( "LCA(3, 4) = " , findLCA(root, 3 , 4 ))
print ( "LCA(2, 4) = " , findLCA(root, 2 , 4 ))
|
C#
using System;
using System.Collections.Generic;
class Node
{
public int data;
public Node left, right;
public Node( int item)
{
data = item;
left = right = null ;
}
}
class MainClass
{
static Dictionary<Node, Node> BuildParentMap(Node root)
{
Dictionary<Node, Node> parentMap = new Dictionary<Node, Node>();
parentMap.Add(root, null );
Queue<Node> queue = new Queue<Node>();
queue.Enqueue(root);
while (queue.Count != 0)
{
Node node = queue.Dequeue();
if (node.left != null )
{
parentMap.Add(node.left, node);
queue.Enqueue(node.left);
}
if (node.right != null )
{
parentMap.Add(node.right, node);
queue.Enqueue(node.right);
}
}
return parentMap;
}
static int FindLCA(Node root, int n1, int n2)
{
Dictionary<Node, Node> parentMap = BuildParentMap(root);
Node p = null , q = null ;
Queue<Node> queue = new Queue<Node>();
queue.Enqueue(root);
while (queue.Count != 0)
{
Node node = queue.Dequeue();
if (node.data == n1)
{
p = node;
}
if (node.data == n2)
{
q = node;
}
if (node.left != null )
{
queue.Enqueue(node.left);
}
if (node.right != null )
{
queue.Enqueue(node.right);
}
}
HashSet<Node> ancestors = new HashSet<Node>();
while (p != null )
{
ancestors.Add(p);
p = parentMap[p];
}
while (q != null )
{
if (ancestors.Contains(q))
{
return q.data;
}
q = parentMap[q];
}
return -1;
}
public static void Main()
{
Node root = new Node(1);
root.left = new Node(2);
root.right = new Node(3);
root.left.left = new Node(4);
root.left.right = new Node(5);
root.right.left = new Node(6);
root.right.right = new Node(7);
Console.WriteLine( "LCA(4, 5) = " + FindLCA(root, 4, 5));
Console.WriteLine( "LCA(4, 6) = " + FindLCA(root, 4, 6));
Console.WriteLine( "LCA(3, 4) = " + FindLCA(root, 3, 4));
Console.WriteLine( "LCA(2, 4) = " + FindLCA(root, 2, 4));
}
}
|
Javascript
class Node {
constructor(item) {
this .data = item;
this .left = null ;
this .right = null ;
}
}
function buildParentMap(root) {
const parentMap = new Map();
parentMap.set(root, null );
const queue = [];
queue.push(root);
while (queue.length > 0) {
const node = queue.shift();
if (node.left != null ) {
parentMap.set(node.left, node);
queue.push(node.left);
}
if (node.right != null ) {
parentMap.set(node.right, node);
queue.push(node.right);
}
}
return parentMap;
}
function findLCA(root, n1, n2) {
const parentMap = buildParentMap(root);
let p = null , q = null ;
const queue = [];
queue.push(root);
while (queue.length > 0) {
const node = queue.shift();
if (node.data === n1) {
p = node;
}
if (node.data === n2) {
q = node;
}
if (node.left != null ) {
queue.push(node.left);
}
if (node.right != null ) {
queue.push(node.right);
}
}
const ancestors = new Set();
while (p != null ) {
ancestors.add(p);
p = parentMap.get(p);
}
while (q != null ) {
if (ancestors.has(q)) {
return q.data;
}
q = parentMap.get(q);
}
return -1;
}
const root = new Node(1);
root.left = new Node(2);
root.right = new Node(3);
root.left.left = new Node(4);
root.left.right = new Node(5);
root.right.left = new Node(6);
root.right.right = new Node(7);
console.log( "LCA(4, 5) = " + findLCA(root, 4, 5));
console.log( "LCA(4, 6) = " + findLCA(root, 4, 6));
console.log( "LCA(3, 4) = " + findLCA(root, 3, 4));
console.log( "LCA(2, 4) = " + findLCA(root, 2, 4));
|
Output
LCA(4, 5) = 2
LCA(4, 6) = 1
LCA(3,4) = 1
LCA(2, 4) = 2
Time Complexity: O(n),
The time complexity of the given code is O(n), where n is the number of nodes in the binary tree.
Building the parent map for each node in the tree requires visiting each node once, which takes O(n) time. Finding the nodes with values n1 and n2 requires visiting each node once, which also takes O(n) time. Traversing up from the second node and checking if each ancestor is already in the set or the list takes O(h) time, where h is the height of the binary tree.
In the worst case, the height of the binary tree is O(n), if the binary tree is skewed. Therefore, the overall time complexity of the given code is O(n) + O(n) + O(n) = O(n).
Space Complexity: O(n),
The space complexity of the given code is O(n) in the worst case. This is because the size of the parent map built for each node in the tree is O(n). Additionally, the set of ancestors can also contain all the nodes in the binary tree in the worst case, which also takes O(n) space. Finally, the queue used for traversing the binary tree takes O(n) space. Therefore, the overall space complexity of the given code is O(n) + O(n) + O(n) = O(n).
We have discussed an efficient solution to find LCA in Binary Search Tree. In Binary Search Tree, using BST properties, we can find LCA in O(h) time where h is the height of the tree. Such an implementation is not possible in Binary Tree as keys Binary Tree nodes don’t follow any order.
You may like to see the below articles as well :
LCA using Parent Pointer
Lowest Common Ancestor in a Binary Search Tree.
Find LCA in Binary Tree using RMQ
Please Login to comment...