8.19 Link-Cut Trees
All previous tree algorithms in this chapter assume that the tree structure is fixed.
8.19 Link-Cut Trees
All previous tree algorithms in this chapter assume that the tree structure is fixed.
Nodes remain connected in the same way throughout execution.
Examples include:
- Euler Tour
- Heavy-Light Decomposition
- Centroid Decomposition
- Lowest Common Ancestor
- Binary Lifting
These techniques are extremely powerful, but they struggle when the tree itself changes.
Suppose we need operations such as:
Cut edge (u,v)
Link node u
to node w
Query path(u,v)
The topology changes continuously.
Traditional preprocessing quickly becomes invalid.
Link-Cut Trees were designed specifically for this environment. They support dynamic forests, allowing edges to be inserted and removed while maintaining efficient path queries.
Introduced by entity["people","Daniel Sleator","Link-Cut Tree co-inventor"] and entity["people","Robert Tarjan","Link-Cut Tree co-inventor"], Link-Cut Trees remain one of the most sophisticated and influential dynamic tree data structures.
Problem
You need efficient path queries on trees whose structure changes over time.
Operations include:
link(u,v)
cut(u,v)
connected(u,v)
path query(u,v)
Rebuilding tree structures after every modification is too expensive.
Solution
Represent tree paths using self-adjusting binary trees.
The key idea is surprising:
Represent a tree as a collection of preferred paths.
Each preferred path is stored inside a Splay Tree.
When access patterns change, preferred paths change as well.
The structure reorganizes itself dynamically.
This allows updates and queries to remain logarithmic.
Discussion
At first glance, Link-Cut Trees appear unrelated to ordinary trees.
Internally, they operate on:
- Preferred paths
- Auxiliary trees
- Splay Trees
- Path rotations
The original tree is still present conceptually, but most operations occur inside these auxiliary structures.
The complexity of Link-Cut Trees comes from maintaining consistency between the two representations.
Preferred Paths
Consider:
0
/ \\n 1 2
/
3
/
4
Suppose recent accesses repeatedly traverse:
0 → 1 → 3 → 4
This path becomes preferred.
0
|
1
|
3
|
4
The preferred path is stored as a Splay Tree.
Node:
2
belongs to a different auxiliary structure.
The decomposition changes automatically as operations occur.
Why Preferred Paths Matter
Suppose we want:
Path Sum(4,0)
The entire path already exists inside one auxiliary structure.
Instead of traversing many tree edges:
4
→ 3
→ 1
→ 0
the query becomes a binary-tree operation.
This is where the efficiency originates.
Core Node Structure
A simplified node representation:
type Node struct {
Left *Node
Right *Node
Parent *Node
Value int
Sum int
Reverse bool
}
Additional fields are typically included depending on the problem.
Examples:
Maximum
Minimum
Count
Custom aggregates
The exact state depends on the desired path queries.
Maintaining Aggregates
Like Segment Trees, Link-Cut Trees maintain summaries.
func update(x *Node) {
x.Sum = x.Value
if x.Left != nil {
x.Sum += x.Left.Sum
}
if x.Right != nil {
x.Sum += x.Right.Sum
}
}
Whenever rotations occur, aggregates are recomputed.
This allows path queries to execute efficiently.
Splay Trees
Link-Cut Trees rely heavily on Splay Trees.
A Splay Tree is a self-adjusting binary search tree.
Frequently accessed nodes move closer to the root.
Operations include:
Zig
Zig-Zig
Zig-Zag
rotations.
These rotations maintain logarithmic amortized complexity.
Without Splay Trees, Link-Cut Trees would not work.
Access Operation
The most important primitive is:
access(x)
Conceptually:
Expose path
from root
to x
After access:
root → x
becomes the preferred path.
Future operations can work directly on this exposed path.
Most Link-Cut Tree operations begin with access.
Making a Node the Root
Traditional rooted trees have a fixed root.
Link-Cut Trees allow rerooting.
func makeRoot(x *Node)
Conceptually:
Old Root
↓
x
becomes
x
↓
Old Root
The represented tree changes orientation.
Internally, this is implemented using lazy path reversals.
Link Operation
To connect two trees:
link(u,v)
First:
makeRoot(u)
Then:
u.Parent = v
The two forests become one.
Complexity:
O(log n)
amortized.
Cut Operation
Removing an edge is equally important.
Suppose:
u --- v
exists.
After:
cut(u,v)
the tree splits into two components.
The operation:
- Exposes the path.
- Identifies the edge.
- Removes the connection.
Again:
O(log n)
amortized.
Connectivity Queries
Determining whether two nodes belong to the same tree is straightforward.
connected(u,v)
Compare roots.
findRoot(u)
findRoot(v)
If the roots match:
true
otherwise:
false
This is useful in dynamic graph algorithms.
Path Sum Queries
Suppose every node stores:
weight
Query:
Sum(u,v)
Procedure:
makeRoot(u)
access(v)
The path:
u → v
becomes exposed.
The aggregate stored at the root of the auxiliary tree immediately contains the answer.
No explicit path traversal is required.
Path Maximum Queries
The same framework supports:
Maximum(u,v)
Store:
Max int
instead of:
Sum int
Update aggregates during rotations.
The query logic remains unchanged.
This flexibility resembles Segment Trees.
Lazy Propagation
Many implementations support path updates.
Examples:
Add value to path
Assign value to path
Reverse path
Lazy propagation is used internally.
The idea is identical to the lazy Segment Trees discussed earlier.
Pending updates move downward only when necessary.
Dynamic Forests
Link-Cut Trees maintain:
Forest
rather than:
Single Tree
At any moment:
Tree A
Tree B
Tree C
may coexist.
Operations can connect or separate them dynamically.
This capability is one reason Link-Cut Trees appear in advanced graph algorithms.
Minimum Spanning Trees
One famous application involves dynamic minimum spanning trees.
Suppose:
Add edge
Remove edge
Query MST weight
continuously.
Link-Cut Trees help maintain path information inside the spanning forest.
Many dynamic graph algorithms rely on this capability.
Network Management
Consider a communication network.
Operations:
Connect routers
Disconnect routers
Query latency
Query bottlenecks
The topology changes frequently.
Recomputing tree structures from scratch would be expensive.
Link-Cut Trees support these operations efficiently.
Comparing HLD and Link-Cut Trees
Heavy-Light Decomposition assumes a static tree.
Link-Cut Trees support dynamic trees.
| Feature | HLD | Link-Cut |
|---|---|---|
| Static paths | Excellent | Excellent |
| Dynamic links | No | Yes |
| Dynamic cuts | No | Yes |
| Complexity | Simpler | Much harder |
| Implementation | Moderate | Advanced |
If the tree never changes, HLD is usually preferable.
If the topology changes continuously, Link-Cut Trees become attractive.
Amortized Complexity
Link-Cut Trees rely on amortized analysis.
Individual operations may occasionally cost more.
Across a long sequence of operations:
| Operation | Complexity |
|---|---|
| Access | O(log n) amortized |
| Link | O(log n) amortized |
| Cut | O(log n) amortized |
| Query | O(log n) amortized |
The amortized guarantee comes from Splay Tree theory.
Why Link-Cut Trees Are Difficult
Many data structures have a simple mental model.
Examples:
Heap
→ priority queue
Trie
→ prefixes
Segment Tree
→ intervals
Link-Cut Trees combine multiple ideas:
- Trees
- Splay Trees
- Lazy propagation
- Path decomposition
- Amortized analysis
The interaction among these concepts creates significant implementation complexity.
Even experienced programmers often require extensive testing to produce correct implementations.
Common Mistakes
A common mistake is forgetting lazy propagation during rotations.
Pending reversals must be pushed before structural changes occur.
Another mistake is confusing:
Represented tree
with:
Auxiliary tree
They are different structures.
Operations on one affect the other indirectly.
A third mistake is assuming worst-case logarithmic complexity. The guarantees are amortized, not per-operation worst-case bounds.
Finally, many implementations update local pointers correctly but forget to recompute aggregate values afterward.
Every rotation must maintain auxiliary information consistently.
Takeaway
Link-Cut Trees support efficient path queries and structural modifications in dynamic forests. By representing preferred paths with Splay Trees and reorganizing those paths as access patterns change, they provide logarithmic amortized complexity for linking, cutting, rerooting, and querying. Although significantly more complex than static-tree techniques such as Heavy-Light Decomposition, they remain one of the most powerful tools for dynamic tree and graph algorithms.