<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://wiki.sarg.dev/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=185.230.112.145</id>
	<title>Vero - Wikipedia - User contributions [en]</title>
	<link rel="self" type="application/atom+xml" href="https://wiki.sarg.dev/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=185.230.112.145"/>
	<link rel="alternate" type="text/html" href="https://wiki.sarg.dev/index.php/Special:Contributions/185.230.112.145"/>
	<updated>2026-06-25T19:29:27Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.44.2</generator>
	<entry>
		<id>https://wiki.sarg.dev/index.php?title=Tarjan%27s_off-line_lowest_common_ancestors_algorithm&amp;diff=164983</id>
		<title>Tarjan&#039;s off-line lowest common ancestors algorithm</title>
		<link rel="alternate" type="text/html" href="https://wiki.sarg.dev/index.php?title=Tarjan%27s_off-line_lowest_common_ancestors_algorithm&amp;diff=164983"/>
		<updated>2025-07-24T17:13:36Z</updated>

		<summary type="html">&lt;p&gt;185.230.112.145: Provide complexity estimate, and stress importance of using a correct data-structure for the query. Please improve if possible.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{distinguish|Tarjan&#039;s strongly connected components algorithm}}&lt;br /&gt;
In [[computer science]], &#039;&#039;&#039;Tarjan&#039;s off-line lowest common ancestors algorithm&#039;&#039;&#039; is an [[algorithm]] for computing [[lowest common ancestor]]s for pairs of nodes in a tree, based on the [[union-find]] data structure. The lowest common ancestor of two nodes &#039;&#039;d&#039;&#039; and &#039;&#039;e&#039;&#039; in a [[rooted tree]] &#039;&#039;T&#039;&#039; is the node &#039;&#039;g&#039;&#039; that is an ancestor of both &#039;&#039;d&#039;&#039; and &#039;&#039;e&#039;&#039; and that has the greatest depth in &#039;&#039;T&#039;&#039;. It is named after [[Robert Tarjan]], who discovered the technique in 1979. Tarjan&#039;s algorithm is an offline algorithm; that is, unlike other lowest common ancestor algorithms, it requires that all pairs of nodes for which the lowest common ancestor is desired must be specified in advance. The simplest version of the algorithm uses the union-find data structure, which unlike other lowest common ancestor data structures can take more than constant time per operation when the number of pairs of nodes is similar in magnitude to the number of nodes. A later refinement by {{harvtxt|Gabow|Tarjan|1983}} speeds the algorithm up to [[linear time]].&lt;br /&gt;
&lt;br /&gt;
== Pseudocode ==&lt;br /&gt;
&lt;br /&gt;
The pseudocode below determines the lowest common ancestor of each pair in &#039;&#039;P&#039;&#039;, given the root &#039;&#039;r&#039;&#039; of a tree in which the children of node &#039;&#039;n&#039;&#039; are in the set &#039;&#039;n.children&#039;&#039;.  For this offline algorithm, the set &#039;&#039;P&#039;&#039; must be specified in advance.  It uses the &#039;&#039;MakeSet&#039;&#039;, &#039;&#039;Find&#039;&#039;, and &#039;&#039;Union&#039;&#039; functions of a [[disjoint-set data structure]].  &#039;&#039;MakeSet(u)&#039;&#039; removes &#039;&#039;u&#039;&#039; to a singleton set, &#039;&#039;Find(u)&#039;&#039; returns the standard representative of the set containing &#039;&#039;u&#039;&#039;, and &#039;&#039;Union(u,v)&#039;&#039; merges the set containing &#039;&#039;u&#039;&#039; with the set containing &#039;&#039;v&#039;&#039;.&lt;br /&gt;
TarjanOLCA(&#039;&#039;r&#039;&#039;) is first called on the root &#039;&#039;r&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
 &#039;&#039;&#039;function&#039;&#039;&#039; TarjanOLCA(u) &#039;&#039;&#039;is&#039;&#039;&#039;&lt;br /&gt;
     MakeSet(u)&lt;br /&gt;
     u.ancestor := u&lt;br /&gt;
     &#039;&#039;&#039;for each&#039;&#039;&#039; v &#039;&#039;&#039;in&#039;&#039;&#039; u.children &#039;&#039;&#039;do&#039;&#039;&#039;&lt;br /&gt;
         TarjanOLCA(v)&lt;br /&gt;
         Union(u, v)&lt;br /&gt;
         Find(u).ancestor := u&lt;br /&gt;
     u.color := black&lt;br /&gt;
     &#039;&#039;&#039;for each&#039;&#039;&#039; v &#039;&#039;&#039;such that&#039;&#039;&#039; {u, v} &#039;&#039;&#039;in&#039;&#039;&#039; P &#039;&#039;&#039;do&#039;&#039;&#039;&lt;br /&gt;
         &#039;&#039;&#039;if&#039;&#039;&#039; v.color == black &#039;&#039;&#039;then&#039;&#039;&#039;&lt;br /&gt;
             print &amp;quot;Tarjan&#039;s Lowest Common Ancestor of &amp;quot; + u +&lt;br /&gt;
                   &amp;quot; and &amp;quot; + v + &amp;quot; is &amp;quot; + Find(v).ancestor + &amp;quot;.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Each node is initially white, and is colored black after it and all its children have been visited.&lt;br /&gt;
&lt;br /&gt;
For each node pair &#039;&#039;{u,v}&#039;&#039; to be investigated:&lt;br /&gt;
* &#039;&#039;&#039;When &#039;&#039;v&#039;&#039; is already black&#039;&#039;&#039; (viz. when &#039;&#039;v&#039;&#039; comes before &#039;&#039;u&#039;&#039; in a post-order traversal of the tree): After &#039;&#039;u&#039;&#039; is colored black, the lowest common ancestor of this pair is available as &#039;&#039;Find(v).ancestor&#039;&#039;, but only while the LCA of &#039;&#039;u&#039;&#039; and &#039;&#039;v&#039;&#039; is not colored black.&lt;br /&gt;
* &#039;&#039;&#039;Otherwise:&#039;&#039;&#039; Once &#039;&#039;v&#039;&#039; is colored black, the LCA will be available as &#039;&#039;Find(u).ancestor&#039;&#039;, while the LCA is not colored black.&lt;br /&gt;
&lt;br /&gt;
According to {{harvtxt|Tarjan|1979}} the time complexity is O((m + n)a(m + n, n)), where m is the number of edges and n the number of vertices and a is the inverse Ackerman function, &#039;&#039;&#039;provided&#039;&#039;&#039; that the time to find the vertex pairs corresponding to u takes constant time per vertex. The paper recommends using an [[adjacency list]] (called adjacency structure in the paper).&lt;br /&gt;
&lt;br /&gt;
For reference, here are optimized versions of &#039;&#039;MakeSet&#039;&#039;, &#039;&#039;Find&#039;&#039;, and &#039;&#039;Union&#039;&#039; for a [[Disjoint-set data structure#Disjoint-set forests|disjoint-set forest]]:&lt;br /&gt;
&lt;br /&gt;
 &#039;&#039;&#039;function&#039;&#039;&#039; MakeSet(x) &#039;&#039;&#039;is&#039;&#039;&#039;&lt;br /&gt;
     x.parent := x&lt;br /&gt;
     x.rank   := 1&lt;br /&gt;
  &lt;br /&gt;
 &#039;&#039;&#039;function&#039;&#039;&#039; Union(x, y) &#039;&#039;&#039;is&#039;&#039;&#039;&lt;br /&gt;
     xRoot := Find(x)&lt;br /&gt;
     yRoot := Find(y)&lt;br /&gt;
     &#039;&#039;&#039;if&#039;&#039;&#039; xRoot.rank &amp;gt; yRoot.rank &#039;&#039;&#039;then&#039;&#039;&#039;&lt;br /&gt;
         yRoot.parent := xRoot&lt;br /&gt;
     &#039;&#039;&#039;else if&#039;&#039;&#039; xRoot.rank &amp;lt; yRoot.rank &#039;&#039;&#039;then&#039;&#039;&#039;&lt;br /&gt;
         xRoot.parent := yRoot&lt;br /&gt;
     &#039;&#039;&#039;else if&#039;&#039;&#039; xRoot.rank == yRoot.rank &#039;&#039;&#039;then&#039;&#039;&#039;&lt;br /&gt;
         yRoot.parent := xRoot&lt;br /&gt;
         xRoot.rank := xRoot.rank + 1&lt;br /&gt;
   &lt;br /&gt;
 &#039;&#039;&#039;function&#039;&#039;&#039; Find(x) &#039;&#039;&#039;is&#039;&#039;&#039;&lt;br /&gt;
     &#039;&#039;&#039;if&#039;&#039;&#039; x.parent != x &#039;&#039;&#039;then&#039;&#039;&#039;&lt;br /&gt;
        x.parent := Find(x.parent)&lt;br /&gt;
     &#039;&#039;&#039;return&#039;&#039;&#039; x.parent&lt;br /&gt;
&lt;br /&gt;
==Speeding up the algorithm==&lt;br /&gt;
It is possible to preprocess the input LCA queries in such a manner, that the algorithm works faster by an order of magnitude. &lt;br /&gt;
&lt;br /&gt;
 &#039;&#039;&#039;function&#039;&#039;&#039; Preprocess(P) &#039;&#039;&#039;is&#039;&#039;&#039;&lt;br /&gt;
    m := empty map&lt;br /&gt;
    &#039;&#039;&#039;for each&#039;&#039;&#039; {u, v} &#039;&#039;&#039;in&#039;&#039;&#039; P &#039;&#039;&#039;do&#039;&#039;&#039;&lt;br /&gt;
        &#039;&#039;&#039;if&#039;&#039;&#039; u &#039;&#039;&#039;is not mapped in&#039;&#039;&#039; m&lt;br /&gt;
            m[u] := ∅&lt;br /&gt;
        m[u] := m[u] &amp;amp;cup; {(u, v)}&lt;br /&gt;
        &#039;&#039;&#039;if&#039;&#039;&#039; v &#039;&#039;&#039;is not mapped in&#039;&#039;&#039; m&lt;br /&gt;
            m[v] := ∅&lt;br /&gt;
        m[v] := m[v] &amp;amp;cup; {(u, v)}&lt;br /&gt;
    &#039;&#039;&#039;return&#039;&#039;&#039; m&lt;br /&gt;
&lt;br /&gt;
 &#039;&#039;&#039;function&#039;&#039;&#039; GetOpposite(q, u) &#039;&#039;&#039;is&#039;&#039;&#039;&lt;br /&gt;
    &#039;&#039;&#039;if&#039;&#039;&#039; q[0] == u &#039;&#039;&#039;then&#039;&#039;&#039;&lt;br /&gt;
        &#039;&#039;&#039;return&#039;&#039;&#039; q[1]&lt;br /&gt;
    &#039;&#039;&#039;return&#039;&#039;&#039; q[0]    &lt;br /&gt;
&lt;br /&gt;
 &#039;&#039;&#039;function&#039;&#039;&#039; FasterTarjanOLCA(u) &#039;&#039;&#039;is&#039;&#039;&#039;&lt;br /&gt;
    MakeSet(u)&lt;br /&gt;
    u.ancestor := u&lt;br /&gt;
    &#039;&#039;&#039;for each&#039;&#039;&#039; v &#039;&#039;&#039;in&#039;&#039;&#039; u.children &#039;&#039;&#039;do&#039;&#039;&#039;&lt;br /&gt;
        FasterTarjanOLCA(v)&lt;br /&gt;
        Union(u, v)&lt;br /&gt;
        Find(u).ancestor := u&lt;br /&gt;
    u.color := black&lt;br /&gt;
    &#039;&#039;&#039;if&#039;&#039;&#039; m[u] == &#039;&#039;&#039;nil then&#039;&#039;&#039;&lt;br /&gt;
        &#039;&#039;&#039;return&#039;&#039;&#039;&lt;br /&gt;
    &#039;&#039;&#039;for each&#039;&#039;&#039; q &#039;&#039;&#039;in&#039;&#039;&#039; m[u] &#039;&#039;&#039;do&#039;&#039;&#039;&lt;br /&gt;
        v := GetOpposite(q, u)&lt;br /&gt;
        &#039;&#039;&#039;if&#039;&#039;&#039; v != &#039;&#039;&#039;nil and &#039;&#039;&#039;v.color == black &#039;&#039;&#039;then&#039;&#039;&#039;&lt;br /&gt;
            &#039;&#039;&#039;print&#039;&#039;&#039; &amp;quot;LCA of &amp;quot; + u + &amp;quot; and &amp;quot; + v + &amp;quot; is &amp;quot; + Find(v).ancestor + &amp;quot;.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
The idea in the optimization is associating nodes with their counterparts in the list of input queries.&lt;br /&gt;
&lt;br /&gt;
==References==&lt;br /&gt;
*{{citation&lt;br /&gt;
 | last1 = Gabow | first1 = H. N. | author1-link = Harold N. Gabow&lt;br /&gt;
 | last2 = Tarjan | first2 = R. E.&lt;br /&gt;
 | authorlink2 = Robert Tarjan&lt;br /&gt;
 | contribution = A linear-time algorithm for a special case of disjoint set union&lt;br /&gt;
 | title = Proceedings of the 15th ACM Symposium on Theory of Computing (STOC)&lt;br /&gt;
 | year = 1983 | pages = 246–251 | doi = 10.1145/800061.808753| isbn = 0-89791-099-0 }}.&lt;br /&gt;
&lt;br /&gt;
*{{citation&lt;br /&gt;
 | last = Tarjan&lt;br /&gt;
 | authorlink = Robert Tarjan&lt;br /&gt;
 | title = Applications of path compression on balanced trees&lt;br /&gt;
 | journal = [[Journal of the ACM]]&lt;br /&gt;
 | volume = 26&lt;br /&gt;
 | issue = 4&lt;br /&gt;
 | year = 1979&lt;br /&gt;
 | pages = 690–715&lt;br /&gt;
 | doi = 10.1145/322154.322161|first =R. E.| doi-access = free&lt;br /&gt;
 }}.&lt;br /&gt;
&lt;br /&gt;
== External links ==&lt;br /&gt;
* [https://github.com/coderodde/CR.OfflineLCA/blob/main/src/main/java/com/github/coderodde/algo/lca/impl/FasterTarjansOfflineLCAAlgorithm.java Optimized Java implementation]&lt;br /&gt;
&lt;br /&gt;
[[Category:Graph algorithms]]&lt;br /&gt;
[[Category:Articles with example pseudocode]]&lt;/div&gt;</summary>
		<author><name>185.230.112.145</name></author>
	</entry>
</feed>