Open In App

Introduction to Link-Cut Tree

Last Updated : 31 Jan, 2024
Improve
Improve
Like Article
Like
Save
Share
Report

Link Cut Trees (LCT) is a data structure that allows for efficient dynamic maintenance of trees. It is a type of self-adjusting data structure that allows for efficient manipulation of trees, such as link and cut operations, find-root, and access.

  • The implementation of an LCT typically consists of a set of nodes, each representing a tree or a subtree, and a set of pointers linking the nodes together. 
  • Each node contains two pointers, one pointing to its parent and one pointing to its child, and a value associated with the node.

The basic operations that can be performed on an LCT include:

1. Link(u, v): This operation creates a new edge between two nodes u and v, making u the parent of v.

C++




void link(int u, int v)
{
    make_root(u);
    s[u].ch[1] = v;
    s[v].fa = u;
}


Java




public void link(int u, int v) {
    makeRoot(u);
    s[u].ch[1] = v;
    s[v].fa = u;
}


Python3




def link(u, v):
        make_root(u)
        s[u].ch[1] = v
        s[v].fa = u


C#




class Node {
    public int Key;
    public int[] ch = new int[2];
    public int fa;
 
    public Node(int key)
    {
        Key = key;
        ch[0] = ch[1] = -1; // Assuming -1 represents a
                            // null/empty child
        fa = -1;
    }
}
 
class Program {
    static Node[] s; // Assuming s is an array of nodes
 
    static void MakeRoot(int u)
    {
        // Implementation of the make_root function
        // (You may need to implement it based on your
        // requirements) This function usually involves tree
        // rotations.
    }
 
    static void Link(int u, int v)
    {
        MakeRoot(u);
        s[u].ch[1] = v;
        s[v].fa = u;
    }
 
    static void Main()
    {
        // Example usage
        int n = 10; // Assuming the number of nodes is 10
        s = new Node[n];
 
        for (int i = 0; i < n; i++) {
            s[i] = new Node(i);
        }
 
        // Link nodes 1 and 2
        Link(1, 2);
 
        // Link nodes 3 and 4
        Link(3, 4);
 
        // ... Continue linking nodes as needed
    }
}


Javascript




function link(u, v) {
    makeRoot(u);
    s[u].ch[1] = v;
    s[v].fa = u;
}


2. Cut(u, v): This operation removes the edge between two nodes u and v, disconnecting v from its parent u.

C++




void cut(int u, int v) {
    access(u);
    make_root(v);
    s[v].fa = s[u].ch[1] = 0;
}


Java




public void cut(int u, int v) {
    access(u);
    makeRoot(v);
    s[v].fa = s[u].ch[1] = 0;
}


Python3




def cut(u, v):
        access(u)
        make_root(v)
        s[v].fa = s[u].ch[1] = 0


C#




class Node
{
    public int Value;
    public Node[] Children = new Node[2];
    public Node Parent;
 
    public Node(int value)
    {
        Value = value;
    }
}
 
class LinkCutTree
{
    // Access, MakeRoot, Splay, Reverse, Push, and Rotate methods are assumed to be defined as needed
 
    // Cut operation
    static void Cut(Node u, Node v)
    {
        Access(u);
        MakeRoot(v);
        v.Parent = u.Children[1] = null;
    }
}


Javascript




function cut(u, v) {
    access(u);
    makeRoot(v);
    s[v].fa = s[u].ch[1] = 0;
}


3. Find-root(u): This operation finds the root of the tree that contains the node u.

C++




int find_root(int u) {
    access(u);
    splay(u);
    while (s[u].ch[0]) {
        pushdown(u);
        u = s[u].ch[0];
    }
    splay(u);
    return u;
}


Java




public int findRoot(int u) {
    access(u);
    splay(u);
    while (s[u].ch[0] != 0) {
        pushdown(u);
        u = s[u].ch[0];
    }
    splay(u);
    return u;
}


Python3




def find_root(u):
        access(u)
        splay(u)
        while (s[u].ch[0]):
            pushdown(u)
            u = s[u].ch[0]
        splay(u)
        return u


C#




class SplayNode
{
    public int[] ch = new int[2];
    // Other members of the SplayNode struct/class, if any
}
 
class SplayTree
{
    private SplayNode[] s;  // Assuming you have a SplayNode array
 
    // Access function
    private void Access(int u)
    {
        // Implementation of the Access function
    }
 
    // Splay function
    private void Splay(int u)
    {
        // Implementation of the Splay function
    }
 
    // Pushdown function
    private void Pushdown(int u)
    {
        // Implementation of the Pushdown function
    }
 
    // Find root function
    public int FindRoot(int u)
    {
        Access(u);
        Splay(u);
        while (s[u].ch[0] != 0)
        {
            Pushdown(u);
            u = s[u].ch[0];
        }
        Splay(u);
        return u;
    }
}
 
class MainClass
{
    public static void Main(string[] args)
    {
        // Assuming you have an instance of SplayTree
        SplayTree splayTree = new SplayTree();
 
        // Example usage
        int root = splayTree.FindRoot(1);
 
        // Perform other operations as needed
    }
}


Javascript




function find_root(u) {
    access(u);
    splay(u);
    while (s[u].ch[0]) {
        pushdown(u);
        u = s[u].ch[0];
    }
    splay(u);
    return u;
}


3. Access(u): This operation returns the value associated with the node u, and also updates all the necessary tree information.

C++




void access(int u) {
    int v = 0;
    while (u) {
        splay(u);
        s[u].ch[1] = v;
        v = u;
        u = s[u].fa;
    }
}


Java




public void access(int u) {
    int v = 0;
    while (u != 0) {
        splay(u);
        s[u].ch[1] = v;
        v = u;
        u = s[u].fa;
    }
}


Python3




def access(u):
        v = 0
        while (u):
            splay(u)
            s[u].ch[1] = v
            v = u
            u = s[u].fa


C#




void Access(int u)
{
    int v = 0;
    while (u != 0)
    {
        Splay(u);
        s[u].ch[1] = v;
        v = u;
        u = s[u].fa;
    }
}


Javascript




function access(u) {
  let v = 0;
  while (u !== null) {
    splay(u);
    s[u].ch[1] = v;
    v = u;
    u = s[u].fa;
  }
}


Characteristics of Link-Cut Trees:

  • LCTs are useful in many algorithms such as dynamic connectivity, lowest common ancestor, and dynamic trees.
  • The above is just a brief implementation of LCT with examples, But LCT is a bit more complex than that, it’s recommended to use a library or a pre-built class for LCT.
  • In Python, there are several libraries available for implementing Link Cut Trees, such as the “lct” library and the “linkcuttree” library.
  • The “lct” library is a small and simple library that provides basic LCT functionality, such as link, cut, find-root, and access operations.

Here’s an example of how to use the “lct” library to implement a LCT:

C++




#include <vector>
 
struct Node {
  int fa, ch[2];
};
 
class LinkCutTree {
 public:
  LinkCutTree(int n) : s(n + 1) {}
 
  void link(int u, int v) {
    make_root(u);
    s[u].ch[1] = v;
    s[v].fa = u;
  }
 
  void cut(int u, int v) {
    access(u);
    make_root(v);
    s[v].fa = s[u].ch[1] = 0;
  }
 
  int find_root(int u) {
    access(u);
    splay(u);
    while (s[u].ch[0]) {
      pushdown(u);
      u = s[u].ch[0];
    }
    splay(u);
    return u;
  }
 
  void access(int u) {
    int v = 0;
    while (u) {
      splay(u);
      s[u].ch[1] = v;
      v = u;
      u = s[u].fa;
    }
  }
 
 private:
  void make_root(int u) { /* ... */ }
  void splay(int u) { /* ... */ }
  void pushdown(int u) { /* ... */ }
 
  std::vector<Node> s;
};
 
int main() {
  int n;
  LinkCutTree lct(n);
 
  int u, v;
  lct.link(u, v);
  lct.cut(u, v);
 
  int root = lct.find_root(u);
 
  // The value associated with node u is not stored in the LCT data structure,
  // so you need to implement a separate mapping from nodes to values.
}


Java




import java.util.ArrayList;
import java.util.List;
 
class Node {
  int fa;
  int[] ch = new int[2];
}
 
class LinkCutTree {
  private List<Node> s;
 
  public LinkCutTree(int n) {
    s = new ArrayList<>(n + 1);
    for (int i = 0; i <= n; i++) {
      s.add(new Node());
    }
  }
 
  public void link(int u, int v) {
    makeRoot(u);
    s.get(u).ch[1] = v;
    s.get(v).fa = u;
  }
 
  public void cut(int u, int v) {
    access(u);
    makeRoot(v);
    s.get(v).fa = s.get(u).ch[1] = 0;
  }
 
  public int findRoot(int u) {
    access(u);
    splay(u);
    while (s.get(u).ch[0] != 0) {
      pushdown(u);
      u = s.get(u).ch[0];
    }
    splay(u);
    return u;
  }
 
  public void access(int u) {
    int v = 0;
    while (u != 0) {
      splay(u);
      s.get(u).ch[1] = v;
      v = u;
      u = s.get(u).fa;
    }
  }
 
  private void makeRoot(int u) { /* ... */ }
 
  private void splay(int u) { /* ... */ }
 
  private void pushdown(int u) { /* ... */ }
}
 
public class Main {
  public static void main(String[] args) {
    int n = 10;
    LinkCutTree lct = new LinkCutTree(n);
 
    int u = 1;
    int v = 2;
    lct.link(u, v);
    lct.cut(u, v);
 
    int root = lct.findRoot(u);
 
    // The value associated with node u is not stored in the LCT data structure,
    // so you need to implement a separate mapping from nodes to values.
  }
}
 
// This code is contributed by Tapesh(tapeshdua420)


Python




from lct import LinkCutTree
 
# Initialize a new LCT with n nodes
lct = LinkCutTree(n)
 
# Link node u and node v
lct.link(u, v)
 
# Cut the edge between node u and node v
lct.cut(u, v)
 
# Find the root of the tree that contains node u
root = lct.find_root(u)
 
# Access the value associated with node u
value = lct.access(u)


C#




using System;
using System.Collections.Generic;
 
class Node {
    public int fa;
    public int[] ch = new int[2];
}
 
class LinkCutTree {
    private List<Node> s;
 
    public LinkCutTree(int n) {
        s = new List<Node>(n + 1);
        for (int i = 0; i <= n; i++) {
            s.Add(new Node());
        }
    }
 
    public void link(int u, int v) {
        makeRoot(u);
        s[u].ch[1] = v;
        s[v].fa = u;
    }
 
    public void cut(int u, int v) {
        access(u);
        makeRoot(v);
        s[v].fa = s[u].ch[1] = 0;
    }
 
    public int findRoot(int u) {
        access(u);
        splay(u);
        while (s[u].ch[0] != 0) {
            pushdown(u);
            u = s[u].ch[0];
        }
        splay(u);
        return u;
    }
 
    public void access(int u) {
        int v = 0;
        while (u != 0) {
            splay(u);
            s[u].ch[1] = v;
            v = u;
            u = s[u].fa;
        }
    }
 
    private void makeRoot(int u) { /* ... */ }
 
    private void splay(int u) { /* ... */ }
 
    private void pushdown(int u) { /* ... */ }
}
 
public class GFG {
    public static void Main(string[] args) {
        int n = 10;
        LinkCutTree lct = new LinkCutTree(n);
 
        int u = 1;
        int v = 2;
        lct.link(u, v);
        lct.cut(u, v);
 
        int root = lct.findRoot(u);
 
        // The value associated with node u is not stored in the LCT data structure,
        // so you need to implement a separate mapping from nodes to values.
    }
}
 
// This code is contributed by Tapesh(tapeshdua420)


Javascript




// Initialize a new LCT with n nodes
let lct = new LinkCutTree(n);
 
// Link node u and node v
lct.link(u, v);
 
// Cut the edge between node u and node v
lct.cut(u, v);
 
// Find the root of the tree that contains node u
let root = lct.find_root(u);
 
// Access the value associated with node u
let value = lct.access(u);


Another library “linkcut tree” is a more advanced library that provides additional functionality such as subtree size and path sum.

C++




#include <vector>
 
struct Node {
  int fa, ch[2];
  int subtree_size;
  int path_sum;
};
 
class LinkCutTree {
 public:
  LinkCutTree(int n) : s(n + 1) {}
 
  void link(int u, int v) {
    make_root(u);
    s[u].ch[1] = v;
    s[v].fa = u;
  }
 
  void cut(int u, int v) {
    access(u);
    make_root(v);
    s[v].fa = s[u].ch[1] = 0;
  }
 
  int find_root(int u) {
    access(u);
    splay(u);
    while (s[u].ch[0]) {
      pushdown(u);
      u = s[u].ch[0];
    }
    splay(u);
    return u;
  }
 
  void access(int u) {
    int v = 0;
    while (u) {
      splay(u);
      s[u].ch[1] = v;
      v = u;
      u = s[u].fa;
    }
  }
 
  int subtree_size(int u) { return s[u].subtree_size; }
 
  int path_sum(int u) { return s[u].path_sum; }
 
 private:
  void make_root(int u) { /* ... */ }
  void splay(int u) { /* ... */ }
  void pushdown(int u) { /* ... */ }
 
  std::vector<Node> s;
};
 
int main() {
  int n;
  LinkCutTree lct(n);
 
  int u, v;
  lct.link(u, v);
  lct.cut(u, v);
 
  int root = lct.find_root(u);
  int value = lct.access(u);
  int subtree_size = lct.subtree_size(u);
  int path_sum = lct.path_sum(u);
}


Java




// Importing the vector library for the implementation of the Link Cut Tree
import java.util.*;
 
// Definition of the Node structure
class Node {
    int fa, ch[];
    int subtree_size;
    int path_sum;
 
    // Constructor for the Node class
    Node() {
        ch = new int[2];
        ch[0] = ch[1] = 0;
        subtree_size = path_sum = 0;
    }
}
 
// Definition of the Link Cut Tree class
class LinkCutTree {
    private Vector<Node> s; // Vector to store the tree nodes
 
    // Constructor for the Link Cut Tree class
    LinkCutTree(int n) {
        s = new Vector<Node>(n + 1);
        for (int i = 0; i <= n; i++) {
            s.add(new Node());
        }
    }
 
    // Method to link two nodes in the tree
    void link(int u, int v) {
        make_root(u);
        s.get(u).ch[1] = v;
        s.get(v).fa = u;
    }
 
    // Method to cut a link between two nodes in the tree
    void cut(int u, int v) {
        access(u);
        make_root(v);
        s.get(v).fa = s.get(u).ch[1] = 0;
    }
 
    // Method to find the root of a node
    int find_root(int u) {
        access(u);
        splay(u);
        while (s.get(u).ch[0] != 0) {
            pushdown(u);
            u = s.get(u).ch[0];
        }
        splay(u);
        return u;
    }
 
    // Method to access a node in the tree
    int access(int u) {
        int v = 0;
        while (u != 0) {
            splay(u);
            s.get(u).ch[1] = v;
            v = u;
            u = s.get(u).fa;
        }
        return v;
    }
 
    // Method to get the size of the subtree rooted at a node
    int subtree_size(int u) { return s.get(u).subtree_size; }
 
    // Method to get the sum of the path from the root to a node
    int path_sum(int u) { return s.get(u).path_sum; }
 
    // Private methods for the implementation of the Link Cut Tree
    private void make_root(int u) { /* ... */ }
 
    private void splay(int u) { /* ... */ }
 
    private void pushdown(int u) { /* ... */ }
}
 
// Main method to test the implementation of the Link Cut Tree
public class Main {
    public static void main(String[] args) {
        int n = 10;
        LinkCutTree lct = new LinkCutTree(n);
 
        int u = 1, v = 2;
        lct.link(u, v);
        lct.cut(u, v);
 
        int root = lct.find_root(u);
        int value = lct.access(u);
        int subtree_size = lct.subtree_size(u);
        int path_sum = lct.path_sum(u);
    }
}


Python




from linkcuttree import LinkCutTree
 
# Initialize a new LCT with n nodes
lct = LinkCutTree(n)
 
# Link node u and node v
lct.link(u, v)
 
# Cut the edge between node u and node v
lct.cut(u, v)
 
# Find the root of the tree that contains node u
root = lct.find_root(u)
 
# Access the value associated with node u
value = lct.access(u)
 
# Get the subtree size of node u
subtree_size = lct.subtree_size(u)
 
# Get the path sum from root to node u
path_sum = lct.path_sum(u)


C#




using System;
using System.Collections.Generic;
 
public struct Node
{
    public int fa;
    public int[] ch;
    public int subtree_size;
    public int path_sum;
}
 
public class LinkCutTree
{
    private Node[] s;
 
    public LinkCutTree(int n)
    {
        s = new Node[n + 1];
        for (int i = 1; i <= n; i++)
        {
            s[i] = new Node { fa = 0, ch = new int[2], subtree_size = 0, path_sum = 0 };
        }
    }
 
    public void Link(int u, int v)
    {
        MakeRoot(u);
        s[u].ch[1] = v;
        s[v].fa = u;
    }
 
    public void Cut(int u, int v)
    {
        Access(u);
        MakeRoot(v);
        s[v].fa = s[u].ch[1] = 0;
    }
 
    public int FindRoot(int u)
    {
        Access(u);
        Splay(u);
        while (s[u].ch[0] != 0)
        {
            Pushdown(u);
            u = s[u].ch[0];
        }
        Splay(u);
        return u;
    }
 
    public int SubtreeSize(int u)
    {
        return s[u].subtree_size;
    }
 
    public int PathSum(int u)
    {
        return s[u].path_sum;
    }
 
    private void MakeRoot(int u)
    {
        /* Implement this function as needed */
    }
 
    private void Access(int u)
    {
        int v = 0;
        while (u != 0)
        {
            Splay(u);
            s[u].ch[1] = v;
            v = u;
            u = s[u].fa;
        }
    }
 
    private void Splay(int u)
    {
        /* Implement this function as needed */
    }
 
    private void Pushdown(int u)
    {
        /* Implement this function as needed */
    }
}
 
class Program
{
    static void Main(string[] args)
    {
        int n = 10; // Replace with the desired value of 'n'
        LinkCutTree lct = new LinkCutTree(n);
 
        int u, v;
        lct.Link(u, v);
        lct.Cut(u, v);
 
        int root = lct.FindRoot(u);
        int subtreeSize = lct.SubtreeSize(u);
        int pathSum = lct.PathSum(u);
    }
}


Javascript




// Definition of the Node structure
class Node {
    constructor() {
        this.fa = 0;
        this.ch = [0, 0];
        this.subtree_size = 0;
        this.path_sum = 0;
    }
}
 
// Definition of the Link Cut Tree class
class LinkCutTree {
    constructor(n) {
        this.s = new Array(n + 1).fill(null).map(() => new Node());
    }
 
    // Method to link two nodes in the tree
    link(u, v) {
        this.makeRoot(u);
        this.s[u].ch[1] = v;
        this.s[v].fa = u;
    }
 
    // Method to cut a link between two nodes in the tree
    cut(u, v) {
        this.access(u);
        this.makeRoot(v);
        this.s[v].fa = this.s[u].ch[1] = 0;
    }
 
    // Method to find the root of a node
    findRoot(u) {
        this.access(u);
        this.splay(u);
        while (this.s[u].ch[0] !== 0) {
            this.pushdown(u);
            u = this.s[u].ch[0];
        }
        this.splay(u);
        return u;
    }
 
    // Method to access a node in the tree
    access(u) {
        let v = 0;
        while (u !== 0) {
            this.splay(u);
            this.s[u].ch[1] = v;
            v = u;
            u = this.s[u].fa;
        }
        return v;
    }
 
    // Method to get the size of the subtree rooted at a node
    subtreeSize(u) {
        return this.s[u].subtree_size;
    }
 
    // Method to get the sum of the path from the root to a node
    pathSum(u) {
        return this.s[u].path_sum;
    }
 
    // Private methods for the implementation of the Link Cut Tree
    makeRoot(u) { /* ... */ }
 
    splay(u) { /* ... */ }
 
    pushdown(u) { /* ... */ }
}
 
// Main method to test the implementation of the Link Cut Tree
function main() {
    const n = 10;
    const lct = new LinkCutTree(n);
 
    const u = 1, v = 2;
    lct.link(u, v);
    lct.cut(u, v);
 
    const root = lct.findRoot(u);
    const value = lct.access(u);
    const subtreeSize = lct.subtreeSize(u);
    const pathSum = lct.pathSum(u);
}
 
// Run the main function
main();


It’s worth noting that LCT is a complex data structure and it’s recommended to use a library or pre-built class to avoid errors and bugs, these libraries are available with clear documentation and examples.

Conclusion:

Link Cut Trees is a powerful data structure that allows for the efficient manipulation of trees. It is based on a set of nodes and pointers linking them together. It supports basic operations like link, cut, find-root, and access, but it’s a bit more complex than that, and a library or a pre-built class is recommended to use.



Like Article
Suggest improvement
Previous
Next
Share your thoughts in the comments

Similar Reads