8.9 Euler Tour Technique

Many tree problems become dramatically easier when you stop thinking of the tree as a tree.

8.9 Euler Tour Technique

Many tree problems become dramatically easier when you stop thinking of the tree as a tree.

At first, this sounds strange. Trees are hierarchical structures, so why abandon that structure? The answer is that many efficient algorithms operate on arrays, not trees. Segment Trees, Fenwick Trees, Sparse Tables, Range Minimum Queries, and many caching techniques all expect linear data.

The Euler Tour Technique bridges these two worlds. It transforms a tree into an array while preserving important structural relationships. Once the transformation is complete, subtree queries often become ordinary range queries.

This technique serves as a foundation for subtree aggregation, Lowest Common Ancestor algorithms, Heavy-Light Decomposition, and numerous competitive programming solutions.

Problem

You need to answer subtree queries efficiently.

Examples include:

  • Sum of values in a subtree
  • Maximum value in a subtree
  • Number of descendants
  • Frequency counts
  • Range updates on subtrees

A naive traversal per query is often too expensive.

Solution

Perform a DFS and record the time when each node is entered.

Consider this tree:

          0
        /   \\n       1     2
      / \   / \\n     3   4 5   6

During DFS:

package main

var timer int

func eulerTour(
    tree [][]int,
    node int,
    tin []int,
    tout []int,
) {
    tin[node] = timer
    timer++

    for _, child := range tree[node] {
        eulerTour(
            tree,
            child,
            tin,
            tout,
        )
    }

    tout[node] = timer
}

The resulting entry times may look like:

Node Entry
0 0
1 1
3 2
4 3
2 4
5 5
6 6

An important property immediately appears:

Subtree(1)
=
{1, 3, 4}

These nodes occupy a contiguous range:

[1, 3]

inside the DFS order.

The subtree becomes an interval.

Discussion

The key insight is simple:

When DFS enters a subtree, it completely finishes that subtree before moving elsewhere.

As a result:

All descendants of a node
appear consecutively
in DFS order.

This property is what makes Euler Tour useful.

Instead of:

Tree problem

we obtain:

Array interval problem

which is often much easier.

Building the Euler Array

A common implementation stores nodes in visitation order.

var timer int

func buildEuler(
    tree [][]int,
    node int,
    tin []int,
    size []int,
    order []int,
) {
    tin[node] = timer
    order[timer] = node
    timer++

    size[node] = 1

    for _, child := range tree[node] {
        buildEuler(
            tree,
            child,
            tin,
            size,
            order,
        )

        size[node] += size[child]
    }
}

For the sample tree:

0 1 3 4 2 5 6

becomes the Euler order.

Every node now corresponds to a position in an array.

Subtree Ranges

Suppose:

tin[1] = 1
size[1] = 3

The subtree occupies:

[1, 3]

because:

start = tin[node]
end   = tin[node] + size[node] - 1

For node 2:

tin[2] = 4
size[2] = 3

Range:

[4, 6]

This observation is powerful because subtree queries become ordinary range queries.

Subtree Sum Queries

Suppose each node contains a value.

Node  Value
0     5
1     2
2     7
3     1
4     4
5     3
6     6

Transform the values into Euler order.

Index  Node  Value
0      0     5
1      1     2
2      3     1
3      4     4
4      2     7
5      5     3
6      6     6

Now subtree 1 corresponds to:

[2, 1, 4]

The subtree sum becomes:

2 + 1 + 4 = 7

Instead of traversing the tree, we query a contiguous interval.

A Fenwick Tree or Segment Tree can answer these queries efficiently.

Ancestor Testing

Euler Tour also provides a fast ancestor test.

Node u is an ancestor of node v if:

tin[u] <= tin[v]

and

tout[v] <= tout[u]

Implementation:

func isAncestor(
    u int,
    v int,
    tin []int,
    tout []int,
) bool {
    return tin[u] <= tin[v] &&
        tout[v] <= tout[u]
}

This check runs in constant time.

Without preprocessing, ancestor queries typically require traversal.

Full Euler Tour

The previous approach records only entry times.

Some algorithms require recording both entry and exit events.

For the sample tree:

0
├── 1
│   ├── 3
│   └── 4
└── 2

A full Euler Tour produces:

0
1
3
1
4
1
0
2
0

Nodes appear multiple times.

This representation is useful because path relationships become visible in the sequence.

Many LCA algorithms rely on this form.

Euler Tour for LCA

Suppose we record:

  • Euler sequence
  • Depth sequence

Example:

Node:
0 1 3 1 4 1 0 2 0

Depth:
0 1 2 1 2 1 0 1 0

To find:

LCA(3, 4)

Locate the first occurrences:

3 at position 2
4 at position 4

Examine the interval:

2..4

Depth values:

2 1 2

The minimum depth corresponds to:

1

which is exactly the LCA.

This reduces LCA to a Range Minimum Query problem.

Flattening Trees

Euler Tour is often described as:

Flattening the tree

because it converts hierarchical structure into linear structure.

Before:

Tree

After:

Array

Many sophisticated tree algorithms begin with this transformation.

The flattening process allows direct reuse of data structures designed for arrays.

Combining with Fenwick Trees

Suppose we need:

Add 5 to every node
inside a subtree.

Without Euler Tour:

Visit every descendant.

With Euler Tour:

Update one interval.

The operation becomes:

update(
    tin[node],
    tin[node] + size[node] - 1
)

This reduction is one reason Euler Tour appears so frequently in large-scale systems.

Combining with Segment Trees

Suppose each node stores a score.

Queries:

Maximum score in subtree.

After flattening:

Maximum value in interval.

A Segment Tree handles this efficiently.

The tree algorithm disappears.

Only interval processing remains.

This transformation is often the difference between:

O(n)

per query and:

O(log n)

per query.

Complexity Analysis

Let:

  • n = number of nodes

Euler Tour construction:

Operation Complexity
DFS traversal O(n)
Entry times O(n)
Exit times O(n)
Total O(n)

Memory:

Structure Complexity
tin O(n)
tout O(n)
Euler order O(n)

Queries:

Query Complexity
Ancestor test O(1)
Subtree interval lookup O(1)
Subtree range query Depends on data structure

The preprocessing cost is linear.

Common Mistakes

A common mistake is confusing:

Entry time

with:

Position in full Euler Tour

These are different concepts.

Another mistake is forgetting:

end =
tin[node] + size[node] - 1

The subtraction by one is easy to miss and creates subtle off-by-one errors.

A third mistake is using DFS order without guaranteeing that subtrees remain contiguous. The Euler Tour property depends on fully exploring a subtree before visiting another branch.

Finally, some implementations store entry times but forget to compute subtree sizes. Without subtree sizes, interval boundaries become difficult to recover.

Takeaway

Euler Tour transforms a tree into an array while preserving subtree structure. The key property is that every subtree becomes a contiguous interval in DFS order. Once the tree is flattened, many problems that appear hierarchical become ordinary range-query problems. This technique serves as a bridge between tree algorithms and powerful array-based data structures such as Fenwick Trees, Segment Trees, and Range Minimum Query structures.