- 
          
 - 
                Notifications
    
You must be signed in to change notification settings  - Fork 18
 
Graphs
        Nazmul Idris edited this page Jul 24, 2018 
        ·
        34 revisions
      
    Here's code in Kotlin that describes undirected graphs with and adjacency list to represent the
edges. For more info, checkout this
website. The adjacency list is
stored in a MutableMap, which holds a LinkedList of nodes. A node / vertex in this graph can
be of any class (T).
Here's an image of an undirected graph.
/**
 * [More info](https://www.geeksforgeeks.org/graph-and-its-representations/).
 */
class Graph<T> {
    val adjacencyMap: MutableMap<T, MutableSet<T>> = mutableMapOf()
    fun addEdge(src: T, dest: T) {
        adjacencyMap[src] = adjacencyMap[src] ?: mutableSetOf()
        adjacencyMap[src]?.add(dest)
        adjacencyMap[dest] = adjacencyMap[dest] ?: mutableSetOf()
        adjacencyMap[dest]?.add(src)
    }
    override fun toString(): String = StringBuffer().apply {
        for (key in adjacencyMap.keys) {
            append("$key -> ")
            append(adjacencyMap[key]?.joinToString(", ", "[", "]\n"))
        }
    }.toString()
}To do a breadth first traversal of the graph, here's some code that uses a Queue (FIFO). The following implementation doesn't use recursion, and also keeps track of the depth as it's traversing the graph.
/**
 * Breadth first traversal leverages a [Queue] (FIFO).
 */
fun <T> breadthFirstTraversal(graph: Graph<T>, 
                              startNode: T, 
                              maxDepth: Int): String {
    // Mark all the vertices / nodes as not visited
    val visitedMap = mutableMapOf<T, Boolean>().apply {
        graph.adjacencyMap.keys.forEach { node -> put(node, false) }
    }
    // Keep track of the depth of each node, so that more than maxDepth
    // nodes aren't visited
    val depthMap = mutableMapOf<T, Int>().apply {
        graph.adjacencyMap.keys.forEach { node -> put(node, Int.MAX_VALUE) }
    }
    // Create a queue for BFS
    val queue: Deque<T> = LinkedList()
    // Initial step -> add the startNode to the queue
    startNode.also { node ->
        // Add to the tail of the queue
        queue.add(node)
        // Record the depth of this node
        depthMap[node] = 0
    }
    // Store the sequence in which nodes are visited, for return value
    val traversalList = mutableListOf<T>()
    // Traverse the graph
    while (queue.isNotEmpty()) {
        // Peek and remove the item at the head of the queue
        val currentNode = queue.remove()
        val depth = depthMap[currentNode]!!
        if (depth <= maxDepth) {
            if (!visitedMap[currentNode]!!) {
                // Mark the current node visited and add to traversal list
                visitedMap[currentNode] = true
                traversalList.add(currentNode)
                // Add nodes in the adjacency map
                graph.adjacencyMap[currentNode]?.forEach { node ->
                    // Add to the tail of the queue
                    queue.add(node)
                    // Record the depth of this node
                    depthMap[node] = depth + 1
                }
            }
        }
    }
    return traversalList.joinToString()
}To do a depth first traversal of the graph, here's some code that uses a Stack (LIFO).
/**
 * Depth first traversal leverages a [Stack] (LIFO).
 *
 * It's possible to use recursion instead of using this iterative 
 * implementation using a [Stack]. Also, this algorithm is almost
 * the same above, except for [Stack] is LIFO and [Queue] is FIFO.
 *
 * [More info](https://stackoverflow.com/a/35031174/2085356).
 */
fun <T> depthFirstTraversal(graph: Graph<T>, startNode: T): String {
    // Mark all the vertices / nodes as not visited
    val visitedMap = mutableMapOf<T, Boolean>().apply {
        graph.adjacencyMap.keys.forEach { node -> put(node, false) }
    }
    // Create a queue for DFS
    val stack: Deque<T> = LinkedList()
    // Initial step -> add the startNode to the stack
    startNode.also { node ->
        stack.push(node)
    }
    // Store the sequence in which nodes are visited, for return value
    val traversalList = mutableListOf<T>()
    // Traverse the graph
    while (stack.isNotEmpty()) {
        // Pop the node off the top of the stack
        val currentNode = stack.pop()
        if (!visitedMap[currentNode]!!) {
            // Store this for the result
            traversalList.add(currentNode)
            // Mark the current node visited and add to the traversal list
            visitedMap[currentNode] = true
            // Add nodes in the adjacency map
            graph.adjacencyMap[currentNode]?.forEach { node ->
                stack.push(node)
            }
        }
    }
    return traversalList.joinToString()
}To see a similar implementation of BFS and DFS traversal for binary trees, please refer to the Binary-Trees page.