{x}
blog image

Move Sub-Tree of N-Ary Tree

Solution Explanation for Moving Subtree of N-ary Tree

This problem requires moving a subtree rooted at node p to become a child of node q in an N-ary tree. The key challenges are handling the three possible relationships between p and q (p is ancestor of q, q is ancestor of p, or neither) and efficiently updating parent-child relationships.

Algorithm:

  1. Find Nodes: Perform a Depth-First Search (DFS) to locate nodes p and q in the tree. Store the parent of p (p_parent) during the search.

  2. Handle Relationships:

    • Case 1: q is in the subtree of p: This is invalid; moving p under q would disconnect the tree. Therefore, we need to find the path from p to q and then reconnect.
    • Case 2: p is already a child of q: Do nothing – the condition is already satisfied.
    • Case 3: Neither is in the other's subtree: Remove p from its current parent and append it as the last child of q.
  3. Reconnect (Case 1): If q is a descendant of p, we need to re-establish the connection by finding the Lowest Common Ancestor (LCA) and adjusting the parent of the subtree rooted at q.

  4. Return Root: The root of the tree remains the same unless p was originally the root.

Time Complexity: O(N), where N is the number of nodes in the tree. The DFS traversal visits each node at most once. Finding the LCA (in case 1) also takes at most O(N) time in the worst case.

Space Complexity: O(H), where H is the height of the tree. This is due to the recursive call stack during DFS. In the worst-case scenario (a skewed tree), H can be equal to N.

Code (Python):

class Node:
    def __init__(self, val=None, children=None):
        self.val = val
        self.children = children if children is not None else []
 
def moveSubTree(root, p, q):
    def find_node_and_parent(root, target):
        if root is None:
            return None, None
        if root.val == target:
            return root, None
        for child in root.children:
            node, parent = find_node_and_parent(child, target)
            if node:
                return node, root
        return None, None
    
    p_node, p_parent = find_node_and_parent(root, p)
    q_node, _ = find_node_and_parent(root, q)
 
    if p_parent is None: #p is root
        return root
 
    if p_node in q_node.children:  # Case 2: p is already a child of q
        return root
 
    # Remove p from its parent's children
    if p_parent:
        p_parent.children.remove(p_node)
    
    #Append p as last child of q
    q_node.children.append(p_node)
 
    return root
 

Code (Java):

class Node {
    public int val;
    public List<Node> children;
 
    public Node() {}
    public Node(int _val) { val = _val; }
    public Node(int _val, List<Node> _children) { val = _val; children = _children; }
}
 
class Solution {
    public Node moveSubTree(Node root, int p, int q) {
        // ... (Similar logic as Python code, using Java's List and recursion) ...
        return root;
    }
}

Note: The Java and C++ code would follow a very similar structure to the Python example. The core logic of DFS, node removal, and appending remains consistent. The only differences would be in syntax and data structure handling (e.g., using List in Java or vector in C++ instead of Python's lists). The handling of Case 1 (reconnecting when q is a descendant of p) would require slightly more complex path tracking within the DFS.