Open In App

Isomorphism in N-ary Trees

Improve
Improve
Improve
Like Article
Like
Save Article
Save
Share
Report issue
Report

Given two N-ary trees having M nodes each. Also, given their edges and their roots respectively. The task is to check if they are isomorphic trees or not. If both the trees are isomorphic then print “Yes” else print “No”.

Examples: 

Input: M = 9, Root Node of tree-1: 1, Root Node of tree-2: 3 
Edges of Tree-1: { (1, 3), (3, 4), (3, 5), (1, 8), (8, 9), (1, 2), (2, 6), (2, 7) } 
Edges of Tree-2: { (3, 1), (1, 2), (1, 5), (3, 6), (6, 7), (3, 4), (4, 8), (4, 9) } 
 

Output: YES
Input: M = 9, Root Node of tree-1: 6, Root Node of tree-2: 7 
Edges of Tree-1: {(1, 3),(1, 2), (1, 8), (3, 4), (3, 5), (8, 9), (2, 6), (2, 7)} 
Edges of Tree-2: {(1, 2), (1, 5), (3, 1), (3, 4), (4, 8), (4, 9), (6, 3), (7, 6)} 
 

Output: NO 

 

Approach: 
The idea is to find out the canonical form of both the trees and comparing them. The leaf node will return “()” to its subsequent upper levels. 
Below is the example showing the process of finding the canonical form. 
 

Below is the implementation of the above approach: 
 

CPP




// C++ program for the above approach
#include <bits/stdc++.h>
using namespace std;
 
// To create N-ary tree
map<int, vector<int> > tree;
 
// Function which will accept the root
// of the tree and its parent (which
// is initially "-1") and will return
// the canonical form of the tree
string ConvertCanonical(int vertex,
                        int parent)
{
    // In this string vector we will
    // store canonical form of out
    // current node and its subtree
    vector<string> child;
 
    // Loop to the neighbours of
    // current vertex
    for (auto neighbour : tree[vertex]) {
 
        // This is to prevent us from
        // visiting the parent again
        if (neighbour == parent)
            continue;
 
        // DFS call neighbour of our
        // current vertex & it will
        // pushback the subtree-structure
        // of this neighbour into our
        // child vector.
        child.push_back(ConvertCanonical(
            neighbour, vertex));
    }
 
    // These opening and closing
    // brackets are for the
    // current node
    string str = "(";
 
    // Sorting function will re-order
    // the structure of subtree of
    // the current vertex in a
    // shortest-subtree-first manner.
    // Hence we can
    // now compare the two tree
    // structures effectively
    sort(child.begin(), child.end());
 
    for (auto j : child)
        str += j;
 
    // Append the subtree structure
    // and enclose it with "(" <subtree>
    // ")" the opening and closing
    // brackets of current vertex
    str += ")";
 
    // return the subtree-structure
    // of our Current vertex
    return str;
}
 
// Function to add edges
void addedge(int a, int b)
{
    tree[a].push_back(b);
    tree[b].push_back(a);
}
 
// Driver code
int main()
{
    // Given N-ary Tree 1
    addedge(1, 3);
    addedge(1, 2);
    addedge(1, 5);
    addedge(3, 4);
    addedge(4, 8);
    addedge(4, 9);
    addedge(3, 6);
    addedge(6, 7);
 
    // Function Call to convert Tree 1
    // into canonical with 3 is the root
    // and the parent of root be "-1"
    string tree1 = ConvertCanonical(3, -1);
 
    // Clearing our current tree
    // before taking input of
    // next tree
    tree.clear();
 
    // Given N-ary Tree 2
    addedge(1, 3);
    addedge(3, 4);
    addedge(3, 5);
    addedge(1, 8);
    addedge(8, 9);
    addedge(1, 2);
    addedge(2, 6);
    addedge(2, 7);
 
    // Function Call to convert Tree 2
    // into canonical
    string tree2 = ConvertCanonical(1, -1);
 
    // Check if canonical form of both
    // tree are equal or not
    if (tree1 == tree2)
        cout << "YES" << endl;
    else
        cout << "NO" << endl;
 
    return 0;
}


Java




// Java program for the above approach
 
import java.io.*;
import java.util.*;
 
class GFG {
 
  // To create N-ary tree
  static Map<Integer, List<Integer> > tree
    = new HashMap<>();
 
  // Function which will accept the root
  // of the tree and its parent (which
  // is initially "-1") and will return
  // the canonical form of the tree
  static String ConvertCanonical(int vertex, int parent)
  {
    // In this string vector we will
    // store canonical form of out
    // current node and its subtree
    List<String> child = new ArrayList<>();
 
    // Loop to the neighbours of
    // current vertex
    for (int neighbour :
         tree.getOrDefault(vertex, new ArrayList<>())) {
      // This is to prevent us from
      // visiting the parent again
      if (neighbour == parent) {
        continue;
      }
      // DFS call neighbour of our
      // current vertex & it will
      // pushback the subtree-structure
      // of this neighbour into our
      // child vector.
      child.add(ConvertCanonical(neighbour, vertex));
    }
 
    // These opening and closing
    // brackets are for the
    // current node
    String str = "(";
 
    // Sorting function will re-order
    // the structure of subtree of
    // the current vertex in a
    // shortest-subtree-first manner.
    // Hence we can
    // now compare the two tree
    // structures effectively
    Collections.sort(child);
    for (String j : child) {
      str += j;
    }
 
    // Append the subtree structure
    // and enclose it with "(" <subtree>
    // ")" the opening and closing
    // brackets of current vertex
    str += ")";
 
    // return the subtree-structure
    // of our Current vertex
    return str;
  }
 
  // Function to add edges
  static void addEdge(int a, int b)
  {
    if (!tree.containsKey(a)) {
      tree.put(a, new ArrayList<>());
    }
    tree.get(a).add(b);
 
    if (!tree.containsKey(b)) {
      tree.put(b, new ArrayList<>());
    }
    tree.get(b).add(a);
  }
 
  public static void main(String[] args)
  {
    // Given N-ary Tree 1
    addEdge(1, 3);
    addEdge(1, 2);
    addEdge(1, 5);
    addEdge(3, 4);
    addEdge(4, 8);
    addEdge(4, 9);
    addEdge(3, 6);
    addEdge(6, 7);
 
    // Function Call to convert Tree 1
    // into canonical with 3 is the root
    // and the parent of root be "-1"
    String tree1 = ConvertCanonical(3, -1);
 
    // Clearing our current tree
    // before taking input of
    // next tree
    tree.clear();
 
    // Given N-ary Tree 2
    addEdge(1, 3);
    addEdge(3, 4);
    addEdge(3, 5);
    addEdge(1, 8);
    addEdge(8, 9);
    addEdge(1, 2);
    addEdge(2, 6);
    addEdge(2, 7);
 
    // Function Call to convert Tree 2
    // into canonical
    String tree2 = ConvertCanonical(1, -1);
 
    // Check if canonical form of both
    // tree are equal or not
    if (tree1.equals(tree2)) {
      System.out.println("YES");
    }
    else {
      System.out.println("NO");
    }
  }
}
 
// This code is contributed by karthik.


Python3




# Python3 program for the above approach
 
# To create N-ary tree
tree=dict()
 
# Function which will accept the root
# of the tree and its parent (which
# is initially "-1") and will return
# the canonical form of the tree
def ConvertCanonical(vertex, parent):
    # In this string vector we will
    # store canonical form of out
    # current node and its subtree
    child=[]
 
    # Loop to the neighbours of
    # current vertex
    for neighbour in tree[vertex] :
 
        # This is to prevent us from
        # visiting the parent again
        if (neighbour == parent):
            continue
 
        # DFS call neighbour of our
        # current vertex & it will
        # pushback the subtree-structure
        # of this neighbour into our
        # child vector.
        child.append(ConvertCanonical(
            neighbour, vertex))
     
 
    # These opening and closing
    # brackets are for the
    # current node
    s = "("
 
    # Sorting function will re-order
    # the structure of subtree of
    # the current vertex in a
    # shortest-subtree-first manner.
    # Hence we can
    # now compare the two tree
    # structures effectively
    child.sort()
 
    for j in child:
        s += j
 
    # Append the subtree structure
    # and enclose it with "(" <subtree>
    # ")" the opening and closing
    # brackets of current vertex
    s += ")"
 
    # return the subtree-structure
    # of our Current vertex
    return s
 
 
# Function to add edges
def addedge(a, b):
    if a in tree:
        tree[a].append(b)
    else:
        tree[a]=[b,]
    if b in tree:
        tree[b].append(a)
    else:
        tree[b]=[a,]
 
 
# Driver code
if __name__=='__main__':
    # Given N-ary Tree 1
    addedge(1, 3)
    addedge(1, 2)
    addedge(1, 5)
    addedge(3, 4)
    addedge(4, 8)
    addedge(4, 9)
    addedge(3, 6)
    addedge(6, 7)
 
    # Function Call to convert Tree 1
    # into canonical with 3 is the root
    # and the parent of root be "-1"
    tree1 = ConvertCanonical(3, -1)
 
    # Clearing our current tree
    # before taking input of
    # next tree
    tree.clear()
 
    # Given N-ary Tree 2
    addedge(1, 3)
    addedge(3, 4)
    addedge(3, 5)
    addedge(1, 8)
    addedge(8, 9)
    addedge(1, 2)
    addedge(2, 6)
    addedge(2, 7)
 
    # Function Call to convert Tree 2
    # into canonical
    tree2 = ConvertCanonical(1, -1)
 
    # Check if canonical form of both
    # tree are equal or not
    if (tree1 == tree2):
        print("YES")
    else:
        print("NO")


C#




// C# program for the above approach
 
using System;
using System.Collections.Generic;
 
public class GFG {
 
    // To create N-ary tree
    static Dictionary<int, List<int> > tree
        = new Dictionary<int, List<int> >();
 
    // Function which will accept the root
    // of the tree and its parent (which
    // is initially "-1") and will return
    // the canonical form of the tree
    static string ConvertCanonical(int vertex, int parent)
    {
        // In this string vector we will
        // store canonical form of out
        // current node and its subtree
        List<string> child = new List<string>();
 
        // Loop to the neighbours of
        // current vertex
        if (tree.ContainsKey(vertex)) {
            foreach(int neighbour in tree[vertex])
            {
                // This is to prevent us from
                // visiting the parent again
                if (neighbour == parent) {
                    continue;
                }
                // DFS call neighbour of our
                // current vertex & it will
                // pushback the subtree-structure
                // of this neighbour into our
                // child vector.
                child.Add(
                    ConvertCanonical(neighbour, vertex));
            }
        }
 
        // These opening and closing
        // brackets are for the
        // current node
        string str = "(";
 
        // Sorting function will re-order
        // the structure of subtree of
        // the current vertex in a
        // shortest-subtree-first manner.
        // Hence we can
        // now compare the two tree
        // structures effectively
        child.Sort();
        foreach(string j in child) { str += j; }
 
        // Append the subtree structure
        // and enclose it with "(" <subtree>
        // ")" the opening and closing
        // brackets of current vertex
        str += ")";
 
        // return the subtree-structure
        // of our Current vertex
        return str;
    }
 
    // Function to add edges
    static void addEdge(int a, int b)
    {
        if (!tree.ContainsKey(a)) {
            tree.Add(a, new List<int>());
        }
        tree[a].Add(b);
 
        if (!tree.ContainsKey(b)) {
            tree.Add(b, new List<int>());
        }
        tree[b].Add(a);
    }
 
    static public void Main()
    {
 
        // Code
        // Given N-ary Tree 1
        addEdge(1, 3);
        addEdge(1, 2);
        addEdge(1, 5);
        addEdge(3, 4);
        addEdge(4, 8);
        addEdge(4, 9);
        addEdge(3, 6);
        addEdge(6, 7);
 
        // Function Call to convert Tree 1
        // into canonical with 3 is the root
        // and the parent of root be "-1"
        string tree1 = ConvertCanonical(3, -1);
 
        // Clearing our current tree
        // before taking input of
        // next tree
        tree.Clear();
 
        // Given N-ary Tree 2
        addEdge(1, 3);
        addEdge(3, 4);
        addEdge(3, 5);
        addEdge(1, 8);
        addEdge(8, 9);
        addEdge(1, 2);
        addEdge(2, 6);
        addEdge(2, 7);
 
        // Function Call to convert Tree 2
        // into canonical
        String tree2 = ConvertCanonical(1, -1);
 
        // Check if canonical form of both
        // tree are equal or not
        if (tree1.Equals(tree2)) {
            Console.WriteLine("YES");
        }
        else {
            Console.WriteLine("NO");
        }
    }
}
 
// This code is contributed by lokesh.


Javascript




<script>
       // JavaScript code for the above approach
       // Function to add edges
       function addedge(a, b) {
           if (!tree[a])
               tree[a] = []
           if (!tree[b])
               tree[b] = []
           tree[a].push(b);
           tree[b].push(a);
       }
 
       // Function which will accept the root
       // of the tree and its parent (which
       // is initially "-1") and will return
       // the canonical form of the tree
       function ConvertCanonical(vertex, parent) {
           // In this string vector we will
           // store canonical form of out
           // current node and its subtree
           let child = [];
 
           // Loop to the neighbours of
           // current vertex
           for (let neighbour of tree[vertex]) {
               // This is to prevent us from
               // visiting the parent again
               if (neighbour === parent) continue;
 
               // DFS call neighbour of our
               // current vertex & it will
               // pushback the subtree-structure
               // of this neighbour into our
               // child vector.
               child.push(ConvertCanonical(neighbour, vertex));
           }
 
           // These opening and closing
           // brackets are for the
           // current node
           let str = "(";
 
           // Sorting function will re-order
           // the structure of subtree of
           // the current vertex in a
           // shortest-subtree-first manner.
           // Hence we can
           // now compare the two tree
           // structures effectively
           child.sort();
 
           for (let j of child) str += j;
 
           // Append the subtree structure
           // and enclose it with "(" <subtree>
           // ")" the opening and closing
           // brackets of current vertex
           str += ")";
 
           // return the subtree-structure
           // of our Current vertex
           return str;
       }
 
       // To create N-ary tree
       let tree = {};
 
       // Given N-ary Tree 1
       addedge(1, 3);
       addedge(1, 2);
       addedge(1, 5);
       addedge(3, 4);
       addedge(4, 8);
       addedge(4, 9);
       addedge(3, 6);
       addedge(6, 7);
 
       // Function Call to convert Tree 1
       // into canonical with 3 is the root
       // and the parent of root be "-1"
       let tree1 = ConvertCanonical(3, -1);
 
       // Clearing our current tree
       // before taking input of
       // next tree
       tree = {};
 
       // Given N-ary Tree 2
       addedge(1, 3);
       addedge(3, 4);
       addedge(3, 5);
       addedge(1, 8);
       addedge(8, 9);
       addedge(1, 2);
       addedge(2, 6);
       addedge(2, 7);
 
       // Function Call to convert Tree 2
       // into canonical
       let tree2 = ConvertCanonical(1, -1);
 
       // Check if canonical form of both
       // tree are equal or not
       if (tree1 === tree2) {
           console.log("YES");
       } else {
           console.log("NO");
       }
 
// This code is contributed by Potta Lokesh.


Output

YES

Time complexity: O(N log N), where N is the number of vertices in the tree. This is because for each vertex, we perform a sorting operation on its child vector, which takes O(log N) time in the worst case. Since there are N vertices in the tree, the overall time complexity is O(N log N).

Auxiliary space: O(N), where N is the number of vertices in the tree. This is because we use a map to represent the tree, which takes O(N) space to store all the vertices and their corresponding neighbors. Additionally, during the recursive calls of the ConvertCanonical function, we create a vector of strings to store the canonical form of each vertex and its subtree, which can take up to O(N) space in the worst case.



Last Updated : 06 Jun, 2023
Like Article
Save Article
Previous
Next
Share your thoughts in the comments
Similar Reads