Skip to content

Commit ed1e8f8

Browse files
committed
added graph traversals
1 parent c462c2a commit ed1e8f8

File tree

8 files changed

+192
-3
lines changed

8 files changed

+192
-3
lines changed

src/kotlin/sequential/graph/bfs/BUILD

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
load("@io_bazel_rules_kotlin//kotlin:kotlin.bzl", "kt_jvm_library")
2+
3+
kt_jvm_library(
4+
name = "bfs_lib",
5+
srcs = glob([
6+
"*.kt",
7+
]),
8+
)
9+
10+
java_binary(
11+
name = "bfs",
12+
main_class = "sequential.graph.bfs.BreadthFirstTraversalKt",
13+
runtime_deps = [":bfs_lib"],
14+
)

src/kotlin/sequential/graph/bfs/BreadthFirstTraversal.kt

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package sequential.graph.bfs
22

3-
import kotlin.collections.ArrayDeque
3+
import java.util.*
44

55

66
private typealias Graph<T> = Map<T, List<T>>
@@ -12,8 +12,9 @@ fun <T> Graph<T>.bfs(root: T): Collection<T> {
1212
val graph = this
1313

1414
val explored = mutableSetOf<T>()
15-
val searchQueue = Queue<T>(listOf(root))
15+
val searchQueue = Queue<T>()
1616

17+
searchQueue.addLast(root)
1718
while (searchQueue.isNotEmpty()) {
1819
val node = searchQueue.removeFirst()
1920
explored.add(node)
@@ -23,7 +24,7 @@ fun <T> Graph<T>.bfs(root: T): Collection<T> {
2324
if (succ !in explored) searchQueue.addLast(succ)
2425
}
2526
}
26-
27+
2728
return explored
2829
}
2930

src/kotlin/sequential/graph/dfs/BUILD

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
load("@io_bazel_rules_kotlin//kotlin:kotlin.bzl", "kt_jvm_library")
2+
3+
kt_jvm_library(
4+
name = "dfs_lib",
5+
srcs = glob([
6+
"*.kt",
7+
]),
8+
)
9+
10+
java_binary(
11+
name = "dfs_iterative",
12+
main_class = "sequential.graph.dfs.iterative.IterativeDepthFirstTraversalKt",
13+
runtime_deps = [":dfs_lib"],
14+
)
15+
16+
java_binary(
17+
name = "dfs_recursive",
18+
main_class = "sequential.graph.dfs.RecursiveDepthFirstTraversalKt",
19+
runtime_deps = [":dfs_lib"],
20+
)
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package sequential.graph.dfs.iterative
2+
3+
import java.util.*
4+
5+
6+
private typealias Graph<T> = Map<T, List<T>>
7+
8+
private typealias Stack<T> = ArrayDeque<T>
9+
10+
11+
fun <T> Graph<T>.iterativeDfs(root: T): Collection<T> {
12+
val graph = this
13+
14+
val explored = mutableSetOf<T>()
15+
val stack = Stack<T>()
16+
17+
stack.addLast(root)
18+
while (stack.isNotEmpty()) {
19+
val node = stack.removeLast()
20+
explored.add(node)
21+
22+
val successors = graph[node].orEmpty()
23+
for (succ in successors) {
24+
if (succ !in explored) stack.addLast(succ)
25+
}
26+
}
27+
28+
return explored
29+
}
30+
31+
32+
fun main() {
33+
34+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package sequential.graph.dfs
2+
3+
4+
private typealias Graph<T> = Map<T, List<T>>
5+
6+
7+
fun <T> Graph<T>.dfs(root: T): Collection<T> {
8+
val graph = this
9+
10+
val explored = mutableSetOf<T>()
11+
12+
fun explore(node: T) {
13+
explored.add(node)
14+
15+
val successors = graph[node].orEmpty()
16+
for (succ in successors) {
17+
if (succ !in explored) explore(succ)
18+
}
19+
}
20+
explore(root)
21+
22+
return explored
23+
}
24+
25+
26+
fun main() {
27+
28+
}

src/kotlin/sequential/graph/dfs/iterative/DepthFirstTraversal.kt

Whitespace-only changes.
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
load("@io_bazel_rules_kotlin//kotlin:kotlin.bzl", "kt_jvm_library")
2+
3+
kt_jvm_library(
4+
name = "dijkstra_lib",
5+
srcs = glob([
6+
"*.kt",
7+
]),
8+
)
9+
10+
java_binary(
11+
name = "dijkstra",
12+
main_class = "sequential.graph.dijkstra.DijkstraKt",
13+
runtime_deps = [":dijkstra_lib"],
14+
)
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
package sequential.graph.dijkstra
2+
3+
4+
/**
5+
* Weighted graph representation using hash table.
6+
* @param T graph value type.
7+
*/
8+
private typealias Graph<T> = Map<T, Map<T, Double>>
9+
10+
/**
11+
* Infinity value which is used as graph edge weight.
12+
*/
13+
private val INFINITY = Double.POSITIVE_INFINITY
14+
15+
16+
/**
17+
* Finds shortest path to the [target] node using Dijkstra algorithm.
18+
*/
19+
fun <T> Graph<T>.dijkstra(root: T, target: T): Collection<T> {
20+
val graph = this
21+
22+
val costs = graph.getValue(root).toMutableMap()
23+
val explored = mutableSetOf<T>()
24+
val parents = graph.getValue(root).mapValues { root }.toMutableMap()
25+
26+
var node = costs.smallestCostNode(explored)
27+
while (node != null) {
28+
val nodeCost = costs[node]!!
29+
val successors = graph[node].orEmpty()
30+
31+
for ((succ, edgeWeight) in successors) {
32+
if (nodeCost + edgeWeight < costs[succ] ?: INFINITY) {
33+
costs[succ] = nodeCost + edgeWeight
34+
parents[succ] = node
35+
}
36+
}
37+
38+
explored.add(node)
39+
node = costs.smallestCostNode(explored)
40+
}
41+
42+
return pathToNode(target, parents)
43+
}
44+
45+
private fun <T> Map<T, Double>.smallestCostNode(explored: Collection<T>): T? = asSequence()
46+
.filter { (node, _) -> node !in explored }
47+
.minBy { (_, cost) -> cost }
48+
?.key
49+
50+
/**
51+
* Build path from root to target node.
52+
*/
53+
private fun <T> pathToNode(target: T, parents: Map<T, T>): List<T> {
54+
if (target !in parents) return emptyList()
55+
56+
val path = mutableListOf<T>()
57+
var node: T? = target
58+
while (node != null) {
59+
path.add(node)
60+
node = parents[node]
61+
}
62+
return path.reversed()
63+
}
64+
65+
66+
fun main() {
67+
68+
val waypoints: Graph<String> = mapOf(
69+
"Start" to mapOf("A" to 5.0),
70+
"A" to mapOf("B" to 4.0, "C" to 7.0),
71+
"B" to mapOf("Finish" to 4.0),
72+
"C" to mapOf("Finish" to 3.0)
73+
)
74+
75+
println(
76+
waypoints.dijkstra("Start", "Finish")
77+
)
78+
}

0 commit comments

Comments
 (0)