Given a binary tree, where each node has a binary value, design an efficient algorithm to find the maximum XOR of any two node values in the tree.
Examples:
Input:
1
/ \
2 3
/ \ /
4 5 6
Output: 7
Explanation: 6 XOR 1 = 7Input:
1
/ \
5 2
/ \
9 3Output: 12
Explanation: 9 XOR 5 = 12
Naive Approach: A simple solution is to generate all pairs, find their XOR values, and finally return the maximum XOR value. But its time and space complexity will be very high.
Time Complexity: O(N2) where N is the number of nodes in the given tree.
Auxiliary Space: O(1)
Efficient Approach: We can use a trie-based solution for this problem and utilize bit manipulation to efficiently store and retrieve values from the trie.
Trie Representation of Example 1:
(root)
/ \
0 1
/ \ / \
0 1 1 0
/ | \
0 1 0Here, each node of the Trie has two children, 0 and 1. Each level of the Trie corresponds to a bit in the binary representation of the numbers. The root node corresponds to the most significant bit, and the leaf nodes correspond to the least significant bit. The numbers in the above example have the following binary representation:
- 1 = 001
- 2 = 010
- 3 = 011
- 4 = 100
- 5 = 101
- 6 = 110
Thus, the Trie structure can be represented in the form of bits of nodes 0 and 1 as follows:
Bitwise representation of trie:
- Level 2: 0 1
- Level 1: 0 1 1 0
- Level 0: 0 1 0 1 1 0
Idea/Intuition:
- Construct a trie from the binary representation of the node values in the tree, where each node in the trie corresponds to a bit in the binary representation and the path from the root of the trie to a leaf node represents the binary value of a node in the tree.
- Starting from the most significant bit, traverse the trie to find a pair of node values that have different bits at the current position. If such a pair exists, it contributes to the maximum XOR.
- To find the pair, check if both 0 and 1 child nodes exist at the current position. If they do, continue the search in the corresponding child node with the opposite bit. If only one child node exists, continue the search in that node.
- Repeat the above step for all the bits, updating the XOR value with the corresponding bit value each time.
- Return the final XOR value as the result.
Note: The trie can be constructed efficiently using a recursive approach that traverses the tree in a post-order manner and inserts each node value into the trie as it is processed.
Steps to follow to implement the approach:
- A Trie data structure is defined, which is used to store the binary representation of each number in the tree.
- The insert() function of the Trie is used to insert the binary representation of each number in the tree into the Trie.
- The findMaxXOR() function of the Trie is used to find the maximum XOR of any two numbers in the Trie.
- A Solution class is defined, which has two member functions:
- constructTrie() function is a recursive function that constructs the Trie from the given binary tree.
- findMaximumXOR() function is used to find the maximum XOR of any two node values in the given binary tree.
- In the main() function, a binary tree is created and a Solution object is created to find the maximum XOR of any two node values in the tree.
- The findMaximumXOR() function of the Solution object is called, which first constructs the Trie from the given tree and then iterates over each node in the tree to find the maximum XOR.
- Finally, the maximum XOR of any two node values in the binary tree is printed to the console.
Below is the implementation of the above approach:
,
// CPP code for the above approach #include <bits/stdc++.h> using namespace std;
// Class definition for TrieNode class TrieNode {
public :
TrieNode* children[2];
TrieNode()
{
// Initializing children list
// with two elements as NULL.
children[0] = children[1] = NULL;
}
}; // Class definition for Trie class Trie {
public :
TrieNode* root;
Trie()
{
// Initializing root node
// of the Trie
root = new TrieNode();
}
void insert( int num)
{
// Starting from the root node
TrieNode* node = root;
// Iterating over each bit of
// the number in reverse order
for ( int i = 31; i >= 0; i--) {
// Get the ith bit
int bit = (num >> i) & 1;
// If the bit is not present
// in the children, add a
// new TrieNode
if (!node->children[bit]) {
node->children[bit] = new TrieNode();
}
// Move to the next node
node = node->children[bit];
}
}
int findMaxXOR( int num)
{
// Starting from the root node
TrieNode* node = root;
int maxXor = 0;
// Iterating over each bit of
// the number in reverse order.
for ( int i = 31; i >= 0; i--) {
// Get the ith bit
int bit = (num >> i) & 1;
// If the complement of the
// current bit is present,
// add it to the maxXor and
// move to that node.
if (node->children[bit ^ 1]) {
maxXor = (maxXor << 1) | 1;
node = node->children[bit ^ 1];
}
// If the complement is not
// present, move to the node
// with the current bit
else {
maxXor = maxXor << 1;
node = node->children[bit];
}
}
// Return the maximum XOR
return maxXor;
}
}; // Class definition for TreeNode struct TreeNode {
int val;
TreeNode* left;
TreeNode* right;
TreeNode( int x)
: val(x), left(NULL), right(NULL)
{
}
}; // Class definition for Solution class Solution {
public :
Trie trie;
void constructTrie(TreeNode* node)
{
// Recursively construct Trie
// for each node in the tree
if (!node) {
return ;
}
trie.insert(node->val);
constructTrie(node->left);
constructTrie(node->right);
}
int findMaximumXOR(TreeNode* root)
{
// Construct Trie from
// the given tree
constructTrie(root);
int maxXor = 0;
TreeNode* node = root;
// Iterate over each node
// in the tree
while (node) {
// Find the maximum XOR for
// the current node and update
// maxXor if it's greater than
// the current maxXor
maxXor
= max(maxXor, trie.findMaxXOR(node->val));
// Move to the next node,
// either to the left
// or right child
node = node->left ? node->left : node->right;
}
// Return the maximum XOR
return maxXor;
}
}; // Driver's code. int main()
{ TreeNode* root = new TreeNode(1);
root->left = new TreeNode(2);
root->right = new TreeNode(3);
root->left->left = new TreeNode(4);
root->left->right = new TreeNode(5);
root->right->left = new TreeNode(6);
Solution s;
// Function Call
int result = s.findMaximumXOR(root);
cout << "Maximum XOR: " << result << endl;
return 0;
} |
// Java code to implement the above approach. import java.util.*;
// Class definition for TrieNode class TrieNode {
TrieNode[] children;
public TrieNode()
{
// Initializing children list with two elements
// (null)
children = new TrieNode[ 2 ];
}
} // Class definition for Trie class Trie {
TrieNode root;
public Trie()
{
// Initializing root node of the Trie
root = new TrieNode();
}
public void insert( int num)
{
// Starting from the root node
TrieNode node = root;
// Iterating over each bit of the number in reverse
// order
for ( int i = 31 ; i >= 0 ; i--) {
// Get the ith bit
int bit = (num >>> i) & 1 ;
// If the bit is not present in the children,
// add a new TrieNode
if (node.children[bit] == null ) {
node.children[bit] = new TrieNode();
}
// Move to the next node
node = node.children[bit];
}
}
public int findMaxXOR( int num)
{
// Starting from the root node
TrieNode node = root;
int maxXOR = 0 ;
// Iterating over each bit of the number in reverse
// order
for ( int i = 31 ; i >= 0 ; i--) {
// Get the ith bit
int bit = (num >>> i) & 1 ;
// If the complement of the current bit is
// present, add it to the maxXOR and move to
// that node
if (node.children[bit ^ 1 ] != null ) {
maxXOR = (maxXOR << 1 ) | 1 ;
node = node.children[bit ^ 1 ];
}
// If the complement is not present,
// move to the node with the current bit
else {
maxXOR = maxXOR << 1 ;
node = node.children[bit];
}
}
// Return the maximum XOR
return maxXOR;
}
} // Class definition for TreeNode class TreeNode {
int val;
TreeNode left;
TreeNode right;
public TreeNode( int x)
{
// Value of the node
val = x;
// Left and right children
left = null ;
right = null ;
}
} // Class definition for Solution class Solution {
Trie trie;
public Solution()
{
// Initializing Trie
trie = new Trie();
}
public void constructTrie(TreeNode node)
{
// Recursively construct Trie for each node in the
// tree
if (node == null ) {
return ;
}
trie.insert(node.val);
constructTrie(node.left);
constructTrie(node.right);
}
public int findMaximumXOR(TreeNode root)
{
// Construct Trie from the given tree
constructTrie(root);
int maxXOR = 0 ;
TreeNode node = root;
// Iterate over each node in the tree
while (node != null ) {
// Find the maximum XOR for the current node and
// update max_xor if it's greater than the
// current max_xor
maxXOR = Math.max(maxXOR,
trie.findMaxXOR(node.val));
// Move to the next node, either to the left or
// right child
if (node.right != null ) {
node = node.right;
}
else {
node = node.left;
}
}
// Return the maximum XOR
return maxXOR;
}
public static void main(String[] args)
{
TreeNode root = new TreeNode( 1 );
root.left = new TreeNode( 2 );
root.right = new TreeNode( 3 );
root.left.left = new TreeNode( 4 );
root.left.right = new TreeNode( 5 );
root.right.left = new TreeNode( 6 );
Solution s = new Solution();
int result = s.findMaximumXOR(root);
System.out.println( "Maximum XOR: " + result);
}
} // This code is contributed by Susobhan Akhuli |
# Python3 code to implement the above approach. # Class definition for TrieNode class TrieNode:
def __init__( self ):
# Initializing children list with two elements (None)
self .children = [ None , None ]
# Class definition for Trie class Trie:
def __init__( self ):
# Initializing root node of the Trie
self .root = TrieNode()
def insert( self , num):
# Starting from the root node
node = self .root
# Iterating over each bit of the number in reverse order
for i in range ( 31 , - 1 , - 1 ):
# Get the ith bit
bit = (num >> i) & 1
# If the bit is not present in the children,
# add a new TrieNode
if not node.children[bit]:
node.children[bit] = TrieNode()
# Move to the next node
node = node.children[bit]
def findMaxXOR( self , num):
# Starting from the root node
node = self .root
max_xor = 0
# Iterating over each bit of the number in reverse order
for i in range ( 31 , - 1 , - 1 ):
# Get the ith bit
bit = (num >> i) & 1
# If the complement of the current bit is present,
# add it to the max_xor and move to that node
if node.children[bit ^ 1 ]:
max_xor = (max_xor << 1 ) | 1
node = node.children[bit ^ 1 ]
# If the complement is not present,
# move to the node with the current bit
else :
max_xor = max_xor << 1
node = node.children[bit]
# Return the maximum XOR
return max_xor
# Class definition for TreeNode class TreeNode:
def __init__( self , x):
# Value of the node
self .val = x
# Left and right children
self .left = None
self .right = None
# Class definition for Solution class Solution:
def __init__( self ):
# Initializing Trie
self .trie = Trie()
def constructTrie( self , node):
# Recursively construct Trie for each node in the tree
if not node:
return
self .trie.insert(node.val)
self .constructTrie(node.left)
self .constructTrie(node.right)
def findMaximumXOR( self , root):
# Construct Trie from the given tree
self .constructTrie(root)
max_xor = 0
node = root
# Iterate over each node in the tree
while node:
# Find the maximum XOR for the current node and
# update max_xor if it's greater than the current max_xor
max_xor = max (max_xor, self .trie.findMaxXOR(node.val))
# Move to the next node
node = node.left if node.right else node.right
# Return the maximum XOR
return max_xor
if __name__ = = '__main__' :
root = TreeNode( 1 )
root.left = TreeNode( 2 )
root.right = TreeNode( 3 )
root.left.left = TreeNode( 4 )
root.left.right = TreeNode( 5 )
root.right.left = TreeNode( 6 )
s = Solution()
result = s.findMaximumXOR(root)
print ( "Maximum XOR:" , result) # 7
# This code is contributed by Susobhan Akhuli |
// C# code to implement the above approach. using System;
// Class definition for TrieNode class TrieNode {
public TrieNode[] children;
public TrieNode()
{
// Initializing children list with two elements
// (null)
children = new TrieNode[2];
}
} // Class definition for Trie class Trie {
public TrieNode root;
public Trie()
{
// Initializing root node of the Trie
root = new TrieNode();
}
public void insert( int num)
{
// Starting from the root node
TrieNode node = root;
// Iterating over each bit of the number in reverse
// order
for ( int i = 31; i >= 0; i--) {
// Get the ith bit
int bit = (num >> i) & 1;
// If the bit is not present in the children,
// add a new TrieNode
if (node.children[bit] == null ) {
node.children[bit] = new TrieNode();
}
// Move to the next node
node = node.children[bit];
}
}
public int findMaxXOR( int num)
{
// Starting from the root node
TrieNode node = root;
int max_xor = 0;
// Iterating over each bit of the number in reverse
// order
for ( int i = 31; i >= 0; i--) {
// Get the ith bit
int bit = (num >> i) & 1;
// If the complement of the current bit is
// present, add it to the max_xor and move to
// that node
if (node.children[bit ^ 1] != null ) {
max_xor = (max_xor << 1) | 1;
node = node.children[bit ^ 1];
}
// If the complement is not present,
// move to the node with the current bit
else {
max_xor = max_xor << 1;
node = node.children[bit];
}
}
// Return the maximum XOR
return max_xor;
}
} // Class definition for TreeNode class TreeNode {
public int val;
public TreeNode left;
public TreeNode right;
public TreeNode( int x)
{
// Value of the node
val = x;
// Left and right children
left = null ;
right = null ;
}
} // Class definition for Solution class Solution {
public Trie trie;
public Solution()
{
// Initializing Trie
trie = new Trie();
}
public void constructTrie(TreeNode node)
{
// Recursively construct Trie for each node in the
// tree
if (node == null ) {
return ;
}
trie.insert(node.val);
constructTrie(node.left);
constructTrie(node.right);
}
// Function to find the maximum XOR of any two node
// values in the tree
public int findMaximumXOR(TreeNode root)
{
// Construct Trie from the given tree
constructTrie(root);
int max_xor = 0;
TreeNode node = root;
// Iterate over each node in the tree
while (node != null ) {
// Find the maximum XOR for the current node and
// update max_xor if it's greater than the
// current max_xor
max_xor = Math.Max(max_xor,
trie.findMaxXOR(node.val));
// Move to the next node
node = (node.left != null ) ? node.left
: node.right;
}
// Return the maximum XOR
return max_xor;
}
static void Main( string [] args)
{
TreeNode root = new TreeNode(1);
root.left = new TreeNode(2);
root.right = new TreeNode(3);
root.left.left = new TreeNode(4);
root.left.right = new TreeNode(5);
root.right.left = new TreeNode(6);
Solution s = new Solution();
int result = s.findMaximumXOR(root);
Console.WriteLine( "Maximum XOR: " + result); // 7
}
} // This code is contributed by Susobhan Akhuli |
<script> // Javascript code to implement the above approach.
// Class definition for TrieNode
class TrieNode {
constructor()
{
// Initializing children list with two elements
// (null)
this .children = [ null , null ];
}
}
// Class definition for Trie
class Trie {
constructor()
{
// Initializing root node of the Trie
this .root = new TrieNode();
}
insert(num)
{
// Starting from the root node
let node = this .root;
// Iterating over each bit of the number in reverse
// order
for (let i = 31; i >= 0; i--) {
// Get the ith bit
let bit = (num >> i) & 1;
// If the bit is not present in the children,
// add a new TrieNode
if (!node.children[bit]) {
node.children[bit] = new TrieNode();
}
// Move to the next node
node = node.children[bit];
}
}
findMaxXOR(num)
{
// Starting from the root node
let node = this .root;
let max_xor = 0;
// Iterating over each bit of the number in reverse
// order
for (let i = 31; i >= 0; i--) {
// Get the ith bit
let bit = (num >> i) & 1;
// If the complement of the current bit is
// present, add it to the max_xor and move to
// that node
if (node.children[bit ^ 1]) {
max_xor = (max_xor << 1) | 1;
node = node.children[bit ^ 1];
}
// If the complement is not present,
// move to the node with the current bit
else {
max_xor = max_xor << 1;
node = node.children[bit];
}
}
// Return the maximum XOR
return max_xor;
}
}
// Class definition for TreeNode
class TreeNode {
constructor(x)
{
// Value of the node
this .val = x;
// Left and right children
this .left = null ;
this .right = null ;
}
}
// Class definition for Solution
class Solution {
constructor()
{
// Initializing Trie
this .trie = new Trie();
}
constructTrie(node)
{
// Recursively construct Trie for each node in the
// tree
if (!node) {
return ;
}
this .trie.insert(node.val);
this .constructTrie(node.left);
this .constructTrie(node.right);
}
findMaximumXOR(root)
{
// Construct Trie from the given tree
this .constructTrie(root);
let max_xor = 0;
let node = root;
// Iterate over each node in the tree
while (node) {
// Find the maximum XOR for the current node and
// update max_xor if it's greater than the
// current max_xor
max_xor = Math.max(
max_xor, this .trie.findMaxXOR(node.val));
// Move to the next node
node = node.left ? node.left : node.right;
}
// Return the maximum XOR
return max_xor;
}
}
const root
= new TreeNode(1);
root.left = new TreeNode(2);
root.right = new TreeNode(3);
root.left.left = new TreeNode(4);
root.left.right = new TreeNode(5);
root.right.left = new TreeNode(6);
const s = new Solution();
const result = s.findMaximumXOR(root);
document.write( "Maximum XOR: " , result); // 7
// This code is contributed by Susobhan Akhuli
</script> |
Maximum XOR: 7
Time Complexity: O(N), where N is the number of nodes in the tree since each node in the tree is processed only once during the construction of the trie and the search.
Auxiliary Space: O(N), since the size of the trie is proportional to the number of nodes in the tree.