Introduction to Link-Cut Tree
Last Updated :
31 Jan, 2024
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;
fa = -1;
}
}
class Program {
static Node[] s;
static void MakeRoot( int u)
{
}
static void Link( int u, int v)
{
MakeRoot(u);
s[u].ch[1] = v;
s[v].fa = u;
}
static void Main()
{
int n = 10;
s = new Node[n];
for ( int i = 0; i < n; i++) {
s[i] = new Node(i);
}
Link(1, 2);
Link(3, 4);
}
}
|
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
{
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];
}
class SplayTree
{
private SplayNode[] s;
private void Access( int u)
{
}
private void Splay( int u)
{
}
private void Pushdown( int u)
{
}
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)
{
SplayTree splayTree = new SplayTree();
int root = splayTree.FindRoot(1);
}
}
|
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);
}
|
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);
}
}
|
Python
from lct import LinkCutTree
lct = LinkCutTree(n)
lct.link(u, v)
lct.cut(u, v)
root = lct.find_root(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);
}
}
|
Javascript
let lct = new LinkCutTree(n);
lct.link(u, v);
lct.cut(u, v);
let root = lct.find_root(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
import java.util.*;
class Node {
int fa, ch[];
int subtree_size;
int path_sum;
Node() {
ch = new int [ 2 ];
ch[ 0 ] = ch[ 1 ] = 0 ;
subtree_size = path_sum = 0 ;
}
}
class LinkCutTree {
private Vector<Node> s;
LinkCutTree( int n) {
s = new Vector<Node>(n + 1 );
for ( int i = 0 ; i <= n; i++) {
s.add( new Node());
}
}
void link( int u, int v) {
make_root(u);
s.get(u).ch[ 1 ] = v;
s.get(v).fa = u;
}
void cut( int u, int v) {
access(u);
make_root(v);
s.get(v).fa = s.get(u).ch[ 1 ] = 0 ;
}
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;
}
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;
}
int subtree_size( int u) { return s.get(u).subtree_size; }
int path_sum( int u) { return s.get(u).path_sum; }
private void make_root( 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 , 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
lct = LinkCutTree(n)
lct.link(u, v)
lct.cut(u, v)
root = lct.find_root(u)
value = lct.access(u)
subtree_size = lct.subtree_size(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)
{
}
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)
{
}
private void Pushdown( int u)
{
}
}
class Program
{
static void Main( string [] args)
{
int n = 10;
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
class Node {
constructor() {
this .fa = 0;
this .ch = [0, 0];
this .subtree_size = 0;
this .path_sum = 0;
}
}
class LinkCutTree {
constructor(n) {
this .s = new Array(n + 1).fill( null ).map(() => new Node());
}
link(u, v) {
this .makeRoot(u);
this .s[u].ch[1] = v;
this .s[v].fa = u;
}
cut(u, v) {
this .access(u);
this .makeRoot(v);
this .s[v].fa = this .s[u].ch[1] = 0;
}
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;
}
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;
}
subtreeSize(u) {
return this .s[u].subtree_size;
}
pathSum(u) {
return this .s[u].path_sum;
}
makeRoot(u) { }
splay(u) { }
pushdown(u) { }
}
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);
}
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.
Share your thoughts in the comments
Please Login to comment...