Open In App

Maintain subtree information using link/cut trees

Last Updated : 06 Mar, 2024
Improve
Improve
Like Article
Like
Save
Share
Report

Link/Cut Trees is a data structure used in computer science that efficiently maintains a forest of trees under the operations of link (joining two trees into one) and cut (removing an edge to split a tree into two). The Link/Cut Trees data structure is useful in a variety of applications such as in-network flow algorithms, dynamic connectivity, and tree path queries. It has a worst-case time complexity of O(log n) for both link and cut operations, where n is the number of nodes in the tree.

The Link/Cut Tree is a data structure that supports several operations which are discussed below:

  • Access(x): This operation makes x the tree’s root and changes the subtree information for all nodes on the path from the current root to x.
  • Cut(x): Removes the edge between x and its parent and changes the subtree information for all nodes on the route from the current root to x.
  • Link(x, y): This operation creates an edge between x and y, making x the parent of y, and updates the subtree information for all nodes on the route from the current root to x.

To maintain subtree information using Link/Cut trees, we need to perform the following steps:

Step 1: Define a node structure that contains the subtree information and pointers to its parent and children.

C++




struct Node {
 
    // parent, left and right children,
    // size of subtree (including node),
    // size of virtual subtree
    int parent, child[2], size, virtual_size;
} nodes[N];


Java




class Node {
    int parent;
    int[] child = new int[2];
    int size;
    int virtual_size;
}


Python




class Node:
    def __init__(self):
        self.parent = 0
        self.child = [0, 0]
        self.size = 0
        self.virtual_size = 0


C#




class Node {
    public int parent;
    public int[] child = new int[2];
    public int size;
    public int virtual_size;
}


Javascript




class Node {
    constructor() {
        this.parent = 0;
        this.child = [0, 0];
        this.size = 0;
        this.virtual_size = 0;
    }
}


Step 2: Implement the Access(x) operation. To do this, we first call the Splay(x) operation, which makes x the root of the tree. Then, we update the subtree information for all the nodes on the path from the previous root to x.

C++




void access(int x)
{
    for (int y = 0; x; x = nodes[y = x].parent) {
        splay(x);
 
        // Update virtual subtree size
        nodes[x].virtual_size -= nodes[y].size;
        nodes[x].virtual_size
            += nodes[nodes[x].child[1]].size;
        nodes[x].child[1] = y;
 
        // Update node information
        pushup(x);
    }
}


Java




void access(int x) {
    for (int y = 0; x != 0; x = nodes[y = x].parent) {
        splay(x);
 
        // Update virtual subtree size
        nodes[x].virtual_size -= nodes[y].size;
        nodes[x].virtual_size += nodes[nodes[x].child[1]].size;
        nodes[x].child[1] = y;
 
        // Update node information
        pushup(x);
    }
}


C#




void Access(int x)
{
    for (int y = 0; x != 0; x = nodes[y = x].parent)
    {
        Splay(x);
 
        // Update virtual subtree size
        nodes[x].virtual_size -= nodes[y].size;
        nodes[x].virtual_size += nodes[nodes[x].child[1]].size;
        nodes[x].child[1] = y;
 
        // Update node information
        PushUp(x);
    }
}


Javascript




function access(x) {
    for (let y = 0; x !== 0; x = nodes[y = x].parent) {
        splay(x);
 
        // Update virtual subtree size
        nodes[x].virtual_size -= nodes[y].size;
        nodes[x].virtual_size += nodes[nodes[x].child[1]].size;
        nodes[x].child[1] = y;
 
        // Update node information
        pushUp(x);
    }
}


Python3




def access(x):
    y = 0
    while x:
        y = x
        x = nodes[x].parent
        splay(y)
 
        # Update virtual subtree size
        nodes[y].virtual_size -= nodes[x].size if x else 0
        nodes[y].virtual_size += nodes[nodes[y].child[1]].size if nodes[y].child[1] else 0
        nodes[y].child[1] = x if x else 0
 
        # Update node information
        pushup(y)


Step 3: Implement the Cut(x) operation. To do this, we first call the Access(x) operation, which makes x the root of the tree. Then, we set the parent of x to null, and update the subtree information for all the nodes on the path from the previous root to x.

C++




void cut(int x)
{
    access(x);
    splay(x);
 
    // x is already a root
    if (!nodes[x].child[0]) {
        return;
    }
 
    // Detach left child
    nodes[nodes[x].child[0]].parent = 0;
    nodes[x].child[0] = 0;
 
    // Update node information
    pushup(x);
}


Java




class Node {
    int parent;
    int[] child = new int[2];
 
    // Constructor to initialize the node
    public Node(int parent, int child0, int child1) {
        this.parent = parent;
        this.child[0] = child0;
        this.child[1] = child1;
    }
}
 
class SplayTree {
    Node[] nodes;
 
    // Constructor to initialize the SplayTree with nodes
    public SplayTree(Node[] nodes) {
        this.nodes = nodes;
    }
 
    // Function to access the node x and perform splay operation
    private void access(int x) {
        // Implement the access operation here
        // ...
    }
 
    // Function to perform the splay operation on the node x
    private void splay(int x) {
        // Implement the splay operation here
        // ...
    }
 
    // Function to push up the updated information after cutting
    private void pushUp(int x) {
        // Update node information after cutting
        // ...
    }
 
    // Function to cut the node x
    public void cut(int x) {
        // Access the node x and perform splay operation
        access(x);
        splay(x);
 
        // Check if x is already the root
        if (nodes[x].child[0] == 0) {
            // x is already a root, so no further action is needed
            return;
        }
 
        // Detach the left child
        nodes[nodes[x].child[0]].parent = 0;
        nodes[x].child[0] = 0;
 
        // Update node information after cutting
        pushUp(x);
    }
}
 
public class Main {
    public static void main(String[] args) {
        // Create an array of nodes
        Node[] nodes = new Node[/* Size of your tree */];
 
        // Initialize the SplayTree with nodes
        SplayTree splayTree = new SplayTree(nodes);
 
        // Example: Cut the node with index x
        int x = /* Index of the node to cut */;
        splayTree.cut(x);
    }
}


Python




def cut(x):
    # Access the node x and perform splay operation
    access(x)
    splay(x)
 
    # Check if x is already the root
    if nodes[x].child[0] == 0:
        # x is already a root, so no further action is needed
        return
 
    # Detach the left child
    nodes[nodes[x].child[0]].parent = 0
    nodes[x].child[0] = 0
 
    # Update node information after cutting
    push_up(x)


C#




void Cut(int x)
{
    // Access the node x and perform splay operation
    Access(x);
    Splay(x);
 
    // Check if x is already the root
    if (nodes[x].Child[0] == 0)
    {
        // x is already a root, so no further action is needed
        return;
    }
 
    // Detach the left child
    nodes[nodes[x].Child[0]].Parent = 0;
    nodes[x].Child[0] = 0;
 
    // Update node information after cutting
    PushUp(x);
}


Javascript




class Node {
    constructor(parent, child0, child1) {
        this.parent = parent;
        this.child = [child0, child1];
    }
}
 
class SplayTree {
    constructor(nodes) {
        this.nodes = nodes;
    }
 
    access(x) {
        // Implement the access operation here
        // ...
    }
 
    splay(x) {
        // Implement the splay operation here
        // ...
    }
 
    pushUp(x) {
        // Update node information after cutting
        // ...
    }
 
    cut(x) {
        this.access(x);
        this.splay(x);
 
        if (this.nodes[x].child[0] === 0) {
            // x is already a root, so no further action is needed
            return;
        }
 
        // Detach the left child
        this.nodes[this.nodes[x].child[0]].parent = 0;
        this.nodes[x].child[0] = 0;
 
        // Update node information after cutting
        this.pushUp(x);
    }
}
 
// Example usage
const nodes = []; // Array of nodes
const splayTree = new SplayTree(nodes);
 
// Example: Cut the node with index x
const x = 0; // Index of the node to cut
splayTree.cut(x);


Step 4: Implement the Link(x, y) operation. To do this, we first call the Access(y) operation, which makes y the root of its tree. Then, we call the Access(x) operation, which makes x the root of its tree. Finally, we set the parent of y to x, and update the subtree information for all the nodes on the path from the previous roots of x and y to x.

C++




void link(int x, int y)
{
    makeroot(x);
    access(y);
    splay(y);
    nodes[x].parent = y;
    nodes[y].virtual_size += nodes[x].size;
}


Java




void link(int x, int y) {
    makeroot(x);
    access(y);
    splay(y);
    nodes[x].parent = y;
    nodes[y].virtual_size += nodes[x].size;
}


Python




def link(x, y):
    makeroot(x)
    access(y)
    splay(y)
    nodes[x].parent = y
    nodes[y].virtual_size += nodes[x].size


C#




void link(int x, int y) {
    makeroot(x);
    access(y);
    splay(y);
    nodes[x].parent = y;
    nodes[y].virtual_size += nodes[x].size;
}


Javascript




function link(x, y) {
    makeroot(x);
    access(y);
    splay(y);
    nodes[x].parent = y;
    nodes[y].virtual_size += nodes[x].size;
}


Here are some codes:

C++




struct Node {
 
    // parent, left and right children,
    // size of subtree (including node),
    // size of virtual subtree
 
    int parent, child[2], size, virtual_size;
} nodes[N];
 
void access(int x)
{
    for (int y = 0; x; x = nodes[y = x].parent) {
        splay(x);
 
        // Update virtual subtree size
        nodes[x].virtual_size -= nodes[y].size;
        nodes[x].virtual_size
            += nodes[nodes[x].child[1]].size;
        nodes[x].child[1] = y;
 
        // Update node information
        pushup(x);
    }
}
 
void link(int x, int y)
{
    makeroot(x);
    access(y);
    splay(y);
    nodes[x].parent = y;
    nodes[y].virtual_size += nodes[x].size;
}
 
void cut(int x)
{
    access(x);
    splay(x);
 
    // x is already a root
    if (!nodes[x].child[0]) {
        return;
    }
 
    // Detach left child
    nodes[nodes[x].child[0]].parent = 0;
    nodes[x].child[0] = 0;
 
    // Update node information
    pushup(x);
}
 
void pushup(int x)
{
    nodes[x].size = nodes[nodes[x].child[0]].size
                    + nodes[nodes[x].child[1]].size
                    + nodes[x].virtual_size + 1;
}
 
// The value associated with node x is not
// stored in the LCT data structure,
// so you need to implement a separate
// mapping from nodes to values.


Java




class Node {
    // parent, left and right children,
    // size of subtree (including node),
    // size of virtual subtree
 
    int parent, child[] = new int[2], size, virtual_size;
}
 
class GFG {
    Node[] nodes;
 
    public LinkCutTree(int N) {
        nodes = new Node[N];
        for (int i = 0; i < N; i++) {
            nodes[i] = new Node();
            nodes[i].parent = nodes[i].child[0] = nodes[i].child[1] = 0;
            nodes[i].size = nodes[i].virtual_size = 1;
        }
    }
 
    void access(int x) {
        for (int y = 0; x != 0; x = nodes[y = x].parent) {
            splay(x);
 
            // Update virtual subtree size
            nodes[x].virtual_size -= nodes[y].size;
            nodes[x].virtual_size += nodes[nodes[x].child[1]].size;
            nodes[x].child[1] = y;
 
            // Update node information
            pushup(x);
        }
    }
 
    void link(int x, int y) {
        makeroot(x);
        access(y);
        splay(y);
        nodes[x].parent = y;
        nodes[y].virtual_size += nodes[x].size;
    }
 
    void cut(int x) {
        access(x);
        splay(x);
 
        // x is already a root
        if (nodes[x].child[0] == 0) {
            return;
        }
 
        // Detach left child
        nodes[nodes[x].child[0]].parent = 0;
        nodes[x].child[0] = 0;
 
        // Update node information
        pushup(x);
    }
 
    void pushup(int x) {
        nodes[x].size = nodes[nodes[x].child[0]].size
                + nodes[nodes[x].child[1]].size
                + nodes[x].virtual_size + 1;
    }
}
 
// The value associated with node x is not
// stored in the LCT data structure,
// so you need to implement a separate
// mapping from nodes to values.


C#




class Node
{
    // parent, left, and right children,
    // size of subtree (including node),
    // size of virtual subtree
    public int parent;
    public int[] child = new int[2];
    public int size;
    public int virtual_size;
}
 
const int N = 10000; // Adjust the size as needed
 
Node[] nodes = new Node[N];
 
void Access(int x)
{
    for (int y = 0; x != 0; x = nodes[y = x].parent)
    {
        Splay(x);
 
        // Update virtual subtree size
        nodes[x].virtual_size -= nodes[y].size;
        nodes[x].virtual_size += nodes[nodes[x].child[1]].size;
        nodes[x].child[1] = y;
 
        // Update node information
        PushUp(x);
    }
}
 
void Link(int x, int y)
{
    MakeRoot(x);
    Access(y);
    Splay(y);
    nodes[x].parent = y;
    nodes[y].virtual_size += nodes[x].size;
}
 
void Cut(int x)
{
    Access(x);
    Splay(x);
 
    // x is already a root
    if (nodes[x].child[0] == 0)
    {
        return;
    }
 
    // Detach left child
    nodes[nodes[x].child[0]].parent = 0;
    nodes[x].child[0] = 0;
 
    // Update node information
    PushUp(x);
}
 
void PushUp(int x)
{
    nodes[x].size = nodes[nodes[x].child[0]].size +
                   nodes[nodes[x].child[1]].size +
                   nodes[x].virtual_size + 1;
}
 
// The value associated with node x is not
// stored in the LCT data structure,
// so you need to implement a separate
// mapping from nodes to values.


Javascript




class Node {
  constructor() {
    // parent, left and right children,
    // size of subtree (including node),
    // size of virtual subtree
    this.parent = 0;
    this.child = [0, 0];
    this.size = 0;
    this.virtual_size = 0;
  }
}
 
const N = 1000; // Assuming N is some predefined value
const nodes = Array.from({ length: N }, () => new Node());
 
function access(x) {
  for (let y = 0; y <= x; y++) {
    x = nodes[y].parent;
    splay(x);
 
    // Update virtual subtree size
    nodes[x].virtual_size -= nodes[y].size;
    nodes[x].virtual_size += nodes[nodes[x].child[1]].size;
    nodes[x].child[1] = y;
 
    // Update node information
    pushup(x);
  }
}
 
function link(x, y) {
  makeroot(x);
  access(y);
  splay(y);
  nodes[x].parent = y;
  nodes[y].virtual_size += nodes[x].size;
}
 
function cut(x) {
  access(x);
  splay(x);
 
  // x is already a root
  if (!nodes[x].child[0]) {
    return;
  }
 
  // Detach left child
  nodes[nodes[x].child[0]].parent = 0;
  nodes[x].child[0] = 0;
 
  // Update node information
  pushup(x);
}
 
function pushup(x) {
  nodes[x].size =
    nodes[nodes[x].child[0]].size +
    nodes[nodes[x].child[1]].size +
    nodes[x].virtual_size +
    1;
}
 
// The value associated with node x is not
// stored in the LCT data structure,
// so you need to implement a separate
// mapping from nodes to values.


Python3




class Node:
    def __init__(self):
        # parent, left and right children,
        # size of subtree (including node),
        # size of virtual subtree
        self.parent = 0
        self.child = [0, 0]
        self.size = 0
        self.virtual_size = 0
 
 
N = 1000  # Assuming N is some predefined value
nodes = [Node() for _ in range(N)]
 
 
def access(x):
    for y in range(0, x + 1):
        x = nodes[y].parent
        splay(x)
 
        # Update virtual subtree size
        nodes[x].virtual_size -= nodes[y].size
        nodes[x].virtual_size += nodes[nodes[x].child[1]].size
        nodes[x].child[1] = y
 
        # Update node information
        pushup(x)
 
 
def link(x, y):
    makeroot(x)
    access(y)
    splay(y)
    nodes[x].parent = y
    nodes[y].virtual_size += nodes[x].size
 
 
def cut(x):
    access(x)
    splay(x)
 
    # x is already a root
    if not nodes[x].child[0]:
        return
 
    # Detach left child
    nodes[nodes[x].child[0]].parent = 0
    nodes[x].child[0] = 0
 
    # Update node information
    pushup(x)
 
 
def pushup(x):
    nodes[x].size = nodes[nodes[x].child[0]].size + nodes[nodes[x].child[1]].size + nodes[x].virtual_size + 1
 
 
# The value associated with node x is not
# stored in the LCT data structure,
# so you need to implement a separate
# mapping from nodes to values.




Like Article
Suggest improvement
Share your thoughts in the comments

Similar Reads