1. Introduction

Many times in writing software, we need to be able to find the best route between two points in a graph. This is very commonly used in computer games, but also mapping software such as Google Maps, and can find uses in many other types of software as well.

Dijkstra’s Algorithm is a very popular pathfinding algorithm used for finding the shortest route between two points in the same graph.

2. What Is Pathfinding?

Pathfinding is an algorithm for graph traversal, where we have a start and end node and need to determine the best route between the two. This involves both the number of steps on the route, as well as the cost of each step.

This cost is dependent on the type of graph that we’re working with – a real-world map might be measured in meters, whereas a computer game like Civilization might base this on the type of unit that is moving and the type of terrain it’s moving over.

There are even applications of pathfinding for traversing state machines, where each node in the graph corresponds to a state, and each edge corresponds to a transition. This can then be used for finding the most efficient set of transitions between two states, like when solving a Rubik’s Cube.

3. Dijkstra’s Algorithm

Dijkstra’s Algorithm is a pathfinding algorithm that generates every single route through the graph, and then selects the route that has the lowest overall cost.

This works by iteratively calculating a distance for each node in the graph, starting from the start node, and continuing until we reach the end node. In every iteration we have a “current node,” and we compute a new best score for every node that can be reached from it.

3.1. Flowchart

A Algorithm Flowchart Example2

3.2. Pseudocode

Now that we know how the algorithm is going to work, what does it look like? Let’s explore some pseudocode describing the algorithm in more detail.

The core of the algorithm is an iterative process, looking at the current best node each time until we reach our target.

algorithm Dijkstra(graph, startNode, targetNode):
    // INPUT
    //     graph, startNode, targetNode
    // OUTPUT
    //     The shortest path from startNode to targetNode in the graph

    for each node in graph:
        node.score <- infinity
        node.visited <- false

    startNode.score <- 0

    while true:
        currentNode <- nodeWithLowestScore(graph)
        currentNode.visited <- true

        for nextNode in currentNode.neighbors:
            if nextNode.visited = false:
                newScore <- calculateScore(currentNode, nextNode)
                if newScore < nextNode.score:
                    nextNode.score <- newScore
                    nextNode.routeToNode <- currentNode

        if currentNode = targetNode:
            return buildPath(targetNode)

        if nodeWithLowestScore(graph).score = infinity:
            throw a NoPathFound exception

As a part of this, we need to be able to find the current best node. This is the node that has the lowest score that we’ve calculated so far, and that we’ve not already visited.

algorithm nodeWithLowestScore(graph):
    // INPUT
    //     graph = the input graph
    // OUTPUT
    //     The node with the lowest score that has not been visited

    result <- null
    for each node in graph:
        if node.visited = false and (result is null or node.score < result.score):
            result <- node

    return result

Next we need to calculate the new score for a node. This is the current score for the node that we’re coming from added to the edge cost of reaching the new node from it.

algorithm calculateScore(currentNode, nextNode):
    // INPUT
    //     currentNode, nextNode
    // OUTPUT
    //     score for moving from currentNode to nextNode

    return currentNode.score + currentNode.edgeCost(nextNode)

Finally, once we reach our target we then need to actually build our route. This is the list of nodes that we have to traverse to get from start to end. By recording on each node the best previous node, we’re effectively building this list as we go so we just need to pull it together.

algorithm buildPath(targetNode):
    // INPUT
    //     targetNode
    // OUTPUT
    //     The route from startNode to targetNode

    route <- create an empty list
    currentNode <- targetNode

    while currentNode != null:
        route.add(currentNode)
        currentNode <- currentNode.routeToNode

    return route

4. Conclusion

Here we’ve seen what pathfinding algorithms are in general, and what Dijkstra’s Algorithm is specifically. We’ve also seen how it works, so we can see how to apply this to our future problems.