Tree Isomorphism Problem In C++

The enigma of tree isomorphism in computer science captivates with its task of discerning whether two given trees share an isomorphic relationship. It entails investigating if one tree can be transformed into the other by swapping the left and right children of certain nodes. In this blog post, we will delve into the intricacies of the tree isomorphism problem, outline its relevance, and present a C++ implementation, complete with a detailed example.

What is a Tree Isomorphism?

The tree isomorphism problem stands as a pivotal concept in algorithmic reasoning, frequently encountered across diverse realms such as data structures, graph theory, and pattern recognition. It constitutes a classical problem amenable to efficient resolution through depth-first search (DFS) or alternative tree traversal methodologies.

In simple terms, two trees, denoted as T1 and T2, establish an isomorphic relationship if a one-to-one mapping can be established between their nodes, facilitating a structure-preserving transformation achievable by swapping left and right children for specific nodes.

C++ Implementation:

Embarking on a C++ implementation, we employ a recursive strategy for traversing the trees and validating their isomorphic nature.

Example

// Include directives
#include <iostream>
#include <unordered_map>
#include <vector>
 
// Namespace declaration
using namespace std;
 
// Tree Node Definition
struct TreeNode {
    int data;
    TreeNode* left;
    TreeNode* right;
 
    TreeNode(int val) : data(val), left(nullptr), right(nullptr) {}
};
 
// Function for checking tree isomorphism
bool isIsomorphic(TreeNode* root1, TreeNode* root2) {
    // Base cases
    if (!root1 && !root2)
        return true;
    if (!root1 || !root2)
        return false;
 
    // Verify root node equality
    if (root1->data != root2->data)
        return false;
 
    // Recursively check isomorphism
    return (isIsomorphic(root1->left, root2->left) && isIsomorphic(root1->right, root2->right)) ||
           (isIsomorphic(root1->left, root2->right) && isIsomorphic(root1->right, root2->left));
}
 
// Main function
int main() {
    // Example trees
    TreeNode* tree1 = new TreeNode(1);
    tree1->left = new TreeNode(2);
    tree1->right = new TreeNode(3);
    tree1->left->left = new TreeNode(4);
    tree1->left->right = new TreeNode(5);
 
    TreeNode* tree2 = new TreeNode(1);
    tree2->left = new TreeNode(3);
    tree2->right = new TreeNode(2);
    tree2->right->left = new TreeNode(5);
    tree2->right->right = new TreeNode(4);
 
    // Check isomorphism
    if (isIsomorphic(tree1, tree2))
        cout << "The trees are isomorphic.\n";
    else
        cout << "The trees are not isomorphic.\n";
 
    return 0;
}

Output:

Output

The trees are isomorphic.

Explanation:

  • In this example, we define a TreeNode structure to represent tree nodes.
  • The isIsomorphic function employs recursion to validate if two trees are isomorphic.
  • In the main function, two example trees are created, and the isIsomorphic function determines their isomorphism.

While the preceding C++ code effectively determines the isomorphism of two trees, there exists an opportunity for optimization and refinement. By incorporating additional data structures, we can enhance the code's efficiency and readability. Specifically, employing a hash table to store mappings between nodes can mitigate redundant comparisons.

Example:

Example

#include <iostream>
#include <unordered_map>
#include <vector>
 
using namespace std;
 
// Definition of Tree Node
struct TreeNode {
    int data;
    TreeNode* left;
    TreeNode* right;
 
    TreeNode(int val) : data(val), left(nullptr), right(nullptr) {}
};
 
// Function to ascertain whether two trees are isomorphic
bool isIsomorphic(TreeNode* root1, TreeNode* root2, unordered_map<TreeNode*, TreeNode*>& mapping) {
    // Base cases
    if (!root1 && !root2)
        return true;
    if (!root1 || !root2)
        return false;
 
    // Confirm equality of root node values
    if (root1->data != root2->data)
        return false;
 
    // Check for existing mapping
    if (mapping.find(root1) != mapping.end() && mapping[root1] != root2)
        return false;
 
    // Establish mapping
    mapping[root1] = root2;
 
    // Recursively validate isomorphism
    return (isIsomorphic(root1->left, root2->left, mapping) && isIsomorphic(root1->right, root2->right, mapping)) ||
           (isIsomorphic(root1->left, root2->right, mapping) && isIsomorphic(root1->right, root2->left, mapping));
}
 
int main() {
    // Sample trees
    TreeNode* tree1 = new TreeNode(1);
    tree1->left = new TreeNode(2);
    tree1->right = new TreeNode(3);
    tree1->left->left = new TreeNode(4);
    tree1->left->right = new TreeNode(5);
 
    TreeNode* tree2 = new TreeNode(1);
    tree2->left = new TreeNode(3);
    tree2->right = new TreeNode(2);
    tree2->right->left = new TreeNode(5);
    tree2->right->right = new TreeNode(4);
 
    // Confirm isomorphism of the trees
    unordered_map<TreeNode*, TreeNode*> mapping;
    if (isIsomorphic(tree1, tree2, mapping))
        cout << "The trees are isomorphic.\n";
    else
        cout << "The trees are not isomorphic.\n";
 
    // Memory cleanup
    delete tree1;
    delete tree2;
 
    return 0;
}

Output:

Output

The trees are isomorphic.

Conclusion:

In conclusion, the exploration of the tree isomorphism problem reveals its intricate nature and its crucial role in algorithmic reasoning. While the initial C++ implementation demonstrated functionality, it also highlighted opportunities for optimization. The refined version introduced a hash table, enhancing both efficiency and readability by minimizing redundant comparisons. This approach proves particularly advantageous for larger trees, prioritizing performance considerations. Through these implementations, we not only validated isomorphism but also showcased the ongoing refinement of solutions. This journey underscores the dynamic aspect of algorithmic problem-solving, emphasizing the continual pursuit of efficiency and elegance in crafting well-designed code.

Input Required

This code uses input(). Please provide values below: