Open In App

What is Link-Cut Tree?

Last Updated : 16 Aug, 2023
Improve
Improve
Like Article
Like
Save
Share
Report

The link-cut tree was invented in 1982 by Daniel Dominic Sleator and Robert Endre Tarjan. The Link-cut trees store a collection of vertex disjoint rooted trees subject to the following operations :

  • makeTree(): It makes a new vertex and puts a singleton tree.
  • getRoot(v): It returns the root of the tree containing v.
  • cut(v): It destroys the edge {v, parent(v)}.
  • link(v, w): Assumes v is the root of its tree and v & w are in different trees, then makes v a child of w.

Terminologies 

1. Preferred child: For some node v, the child containing subtree which contains the last accessed node (say w) in v’s subtree.

Note: A node might not have a preferred child if it was the last accessed node in its subtree.

2. Preferred edge: An edge leading to a preferred child is called a preferred edge. 

Preferred Child and Edge

Preferred Child and Edge

3. Preferred path: It is a maximal chain of preferred edges, which we will represent using splay trees.

Example of Preferred path

Example of Preferred path

Here, let the edges under the ellipse represent a preferred path, here we have A and B which represent two different preferred paths, to represent these, we are gonna use the following structure.

Splay tree representation

These splay trees are keyed by depth (i.e: a higher node in the tree is smaller). The actual tree is called the represented tree, and the root of each auxiliary tree has a path-parent pointer, pointing to the parent of the top node of the path in the represented tree.

Operations

  • access(v): Let’s say we access a node v, then it no longer has any preferred child(read the note),  We splay v, which brings it to the root of the auxiliary tree, we then detach the right subtree of v, so now v has no right subtree and it might have a path-pointer to another vertex w which is in another auxiliary tree. Now we are going to splay w and detach its right subtree, after which we will make v its right subtree, lastly, we will splay v again.
  • getRoot(v): First access(v), and return the lowest depth element in v’s auxiliary tree, by repeatedly going to the left subtree of v.
  • cut(v): access(v) and detach its left subtree(if it exists)
  • link(v, w): Assuming, w is the child and v is the parent, access(w), and then access(v), now we attach v as w’s left child, and make w, v’s parent.
  • The time complexity of all these operations is O(log(n)) amortized.

Example 1:

Java




import java.util.*;
 
public class LinkCutTree {
 
    class Node {
        int value;
        Node left, right, parent, pathParent;
 
        Node(int value)
        {
            this.value = value;
            this.left = null;
            this.right = null;
            this.parent = null;
            this.pathParent = null;
        }
    }
 
    private Node[] tree;
 
    LinkCutTree(int n)
    {
        tree = new Node[n];
    }
    // Splay tree methods
    private Node getNode(int i)
    {
        if (i < 0 || i >= tree.length) {
            return null;
        }
        Node x = tree[i];
        if (x == null) {
            x = new Node(i);
            tree[i] = x;
        }
        return x;
    }
 
    private void rotate(Node x)
    {
        Node y = x.parent;
        if (y != null && y.parent != null) {
            x.parent = y.parent;
            if (y.parent.left == y)
                y.parent.left = x;
            else
                y.parent.right = x;
        }
        else
            x.parent = null;
        if (y.left == x) {
            y.left = x.right;
            if (y.left != null)
                y.left.parent = y;
            x.right = y;
        }
        else {
            y.right = x.left;
            if (y.right != null)
                y.right.parent = y;
            x.left = y;
        }
        if (y.pathParent != null) {
            x.pathParent = y.pathParent;
            y.pathParent = null;
        }
    }
 
    private void splay(Node x)
    {
        while (x.parent != null) {
            Node y = x.parent;
            Node z = y.parent;
            if (z != null) {
                if ((z.left == y) == (y.left == x))
                    rotate(y);
                else
                    rotate(x);
            }
            rotate(x);
        }
    }
    // Link-Cut Tree Methods
    public Node access(int i)
    {
        Node x = getNode(i);
        splay(x);
        if (x.right != null) {
            x.right.pathParent = x;
            x.right.parent = null;
            x.right = null;
        }
        Node y = null;
        while (x.pathParent != null) {
            y = x.parent;
            if (y != null) {
                splay(y);
                if (y.right != null) {
                    y.right.pathParent = y;
                    y.right.parent = null;
                }
                x.parent = y;
                y.right = x;
            }
            x.pathParent = null;
            splay(x);
        }
        return y;
    }
 
    public Node findRoot(int i)
    {
        access(i);
        Node x = getNode(i);
        while (x.left != null)
            x = x.left;
        access(x.value);
        return x;
    }
 
    public void link(int x, int y)
    {
        Node ny = getNode(y);
        Node nx = getNode(x);
        access(y);
        access(x);
        nx.parent = ny;
        ny.left = nx;
    }
 
    public void cut(int x)
    {
        access(x);
        Node nx = getNode(x);
        if (nx.left != null) {
            nx.left.parent = null;
            nx.left = null;
        }
    }
    // Driver Method
    public static void main(String[] args)
    {
        LinkCutTree tree = new LinkCutTree(6);
 
        // Link nodes to form a tree
        tree.link(0, 1);
        tree.link(2, 3);
        tree.link(2, 4);
        tree.link(4, 5);
 
        // making 3 singleon
        tree.cut(3);
 
        LinkCutTree.Node root = tree.findRoot(3);
        System.out.println("Root Value for 3: " + root.value);
 
        root = tree.findRoot(4);
        System.out.println("Root Value for 4: " + root.value);
 
        // Making 4 as root
        tree.access(4);
 
        root = tree.findRoot(4);
        System.out.println("New Root Value for 4: " + root.value);
    }
}


C#




using System;
 
public class LinkCutTree
{
    public class Node // Make the Node class public
    {
        public int value;
        public Node left, right, parent, pathParent;
 
        public Node(int value)
        {
            this.value = value;
            this.left = null;
            this.right = null;
            this.parent = null;
            this.pathParent = null;
        }
    }
 
    private Node[] tree;
 
    public LinkCutTree(int n)
    {
        tree = new Node[n];
    }
 
    // Splay tree methods
 
    private Node GetNode(int i)
    {
        if (i < 0 || i >= tree.Length)
        {
            return null;
        }
 
        Node x = tree[i];
        if (x == null)
        {
            x = new Node(i);
            tree[i] = x;
        }
        return x;
    }
 
    private void Rotate(Node x)
    {
        Node y = x.parent;
        if (y != null && y.parent != null)
        {
            x.parent = y.parent;
            if (y.parent.left == y)
                y.parent.left = x;
            else
                y.parent.right = x;
        }
        else
            x.parent = null;
        if (y.left == x)
        {
            y.left = x.right;
            if (y.left != null)
                y.left.parent = y;
            x.right = y;
        }
        else
        {
            y.right = x.left;
            if (y.right != null)
                y.right.parent = y;
            x.left = y;
        }
        if (y.pathParent != null)
        {
            x.pathParent = y.pathParent;
            y.pathParent = null;
        }
    }
 
    private void Splay(Node x)
    {
        while (x.parent != null)
        {
            Node y = x.parent;
            Node z = y.parent;
            if (z != null)
            {
                if ((z.left == y) == (y.left == x))
                    Rotate(y);
                else
                    Rotate(x);
            }
            Rotate(x);
        }
    }
 
    // Link-Cut Tree Methods
 
    public Node Access(int i)
    {
        Node x = GetNode(i);
        Splay(x);
        if (x.right != null)
        {
            x.right.pathParent = x;
            x.right.parent = null;
            x.right = null;
        }
        Node y = null;
        while (x.pathParent != null)
        {
            y = x.parent;
            if (y != null)
            {
                Splay(y);
                if (y.right != null)
                {
                    y.right.pathParent = y;
                    y.right.parent = null;
                }
                x.parent = y;
                y.right = x;
            }
            x.pathParent = null;
            Splay(x);
        }
        return y;
    }
 
    public Node FindRoot(int i)
    {
        Access(i);
        Node x = GetNode(i);
        while (x.left != null)
            x = x.left;
        Access(x.value);
        return x;
    }
 
    public void Link(int x, int y)
    {
        Node ny = GetNode(y);
        Node nx = GetNode(x);
        Access(y);
        Access(x);
        nx.parent = ny;
        ny.left = nx;
    }
 
    public void Cut(int x)
    {
        Access(x);
        Node nx = GetNode(x);
        if (nx.left != null)
        {
            nx.left.parent = null;
            nx.left = null;
        }
    }
 
    // Driver Method
 
    public static void Main(string[] args)
    {
        LinkCutTree tree = new LinkCutTree(6);
 
        // Link nodes to form a tree
        tree.Link(0, 1);
        tree.Link(2, 3);
        tree.Link(2, 4);
        tree.Link(4, 5);
 
        // making 3 singleton
        tree.Cut(3);
 
        LinkCutTree.Node root = tree.FindRoot(3);
        Console.WriteLine("Root Value for 3: " + root.value);
 
        root = tree.FindRoot(4);
        Console.WriteLine("Root Value for 4: " + root.value);
 
        // Making 4 as root
        tree.Access(4);
 
        root = tree.FindRoot(4);
        Console.WriteLine("New Root Value for 4: " + root.value);
    }
}


Output:

Root Value for 3: 3
Root Value for 4: 2
New Root Value for 4: 4

For more details, you can refer to the relation to the Max-Flow problem and Dinic’s algorithm articles.



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

Similar Reads