PhysicalConnectivity/src/main/kotlin/net/shadowfacts/cacao/util/LowestCommonAncestor.kt

56 lines
1.3 KiB
Kotlin

package net.shadowfacts.cacao.util
import java.util.*
import kotlin.NoSuchElementException
/**
* A linear time algorithm for finding the lowest common ancestor of two nodes in a graph.
* Based on https://stackoverflow.com/a/6342546/4731558
*
* Works be finding the path from each node back to the root node.
* The LCA will then be the node after which the paths diverge.
*
* @author shadowfacts
*/
object LowestCommonAncestor {
fun <Node> find(node1: Node, node2: Node, parent: Node.() -> Node?): Node? {
@Suppress("NAME_SHADOWING") var node1: Node? = node1
@Suppress("NAME_SHADOWING") var node2: Node? = node2
val parent1 = LinkedList<Node>()
while (node1 != null) {
parent1.push(node1)
node1 = node1.parent()
}
val parent2 = LinkedList<Node>()
while (node2 != null) {
parent2.push(node2)
node2 = node2.parent()
}
// paths don't converge on the same root element
if (parent1.first != parent2.first) {
return null
}
var oldNode: Node? = null
while (node1 == node2 && parent1.isNotEmpty() && parent2.isNotEmpty()) {
oldNode = node1
node1 = parent1.popOrNull()
node2 = parent2.popOrNull()
}
return if (node1 == node2) node1!!
else oldNode!!
}
}
private fun <T> LinkedList<T>.popOrNull(): T? {
return try {
pop()
} catch (e: NoSuchElementException) {
null
}
}