All A Level 10 55 min read 10 question quiz 19 exam marks

Dijkstra's Algorithm and A* Search

The two shortest path algorithms in the specification: Dijkstra's, built on a priority queue and traced in a table, and A*, which adds a heuristic and visibly explores a fraction of the graph, both running on real data.

Dijkstra's shortest path algorithm

In a weighted graph every edge has a cost, such as a distance, a time or a price. The shortest path between two vertices is the one with the lowest total weight, which is not necessarily the one with the fewest edges. Breadth first search counts edges. Dijkstra's algorithm counts weights.

It finds the shortest distance from one start vertex to every other vertex, as long as no weight is negative. It works by always fixing the nearest vertex that is not yet fixed, and then seeing whether that vertex gives a shorter way to any of its neighbours.

Dijkstra's algorithmDijkstra's algorithm on a weighted graph of six vertices, A to F, with the edges A to B 7, A to C 9, A to F 14, B to C 10, B to D 15, C to D 11, C to F 2, D to E 6 and E to F 9. Starting at A, the vertices are fixed in the order A, B, C, F, D, E. The working table gives the best known distance to each vertex after each step, and the final distances from A are A 0, B 7, C 9, D 20, E 20, F 11. The shortest path from A to E goes A, C, F, E with a total of 20.Weighted graphWorking table7914101511269ABCDEFstepfixedABCDEF1A079∞∞142B07922∞143C07920∞114F0792020115D0792020116E079202011orange: a shorter route was just foundA0A0B7A0B7C9A0B7C9F11A0B7C9F11D20A0B7C9F11D20E20929ACFEshortest path to E: A → C → F → E (total 20)
Always fix the nearest vertex that is not yet fixed. Its distance can never be improved after that.

The method, which the exam expects you to be able to carry out by hand:

  1. Give the start vertex a distance of 0 and every other vertex a distance of infinity.
  2. Choose the unfixed vertex with the smallest distance and fix it. That distance is now final.
  3. For each neighbour of that vertex, work out distance of the vertex plus the edge weight. If that is smaller than the neighbour's recorded distance, replace the distance, and record that the neighbour was reached from this vertex.
  4. Repeat from step 2 until every vertex is fixed, or until the goal is fixed.

Step 2 is "find the smallest of the waiting vertices", and a priority queue does exactly that. Python's heapq is one.

Python
import heapq

graph = {
    "A": {"B": 7, "C": 9, "F": 14},
    "B": {"A": 7, "C": 10, "D": 15},
    "C": {"A": 9, "B": 10, "D": 11, "F": 2},
    "D": {"B": 15, "C": 11, "E": 6},
    "E": {"D": 6, "F": 9},
    "F": {"A": 14, "C": 2, "E": 9},
}

def dijkstra(graph, start):
    distance = {v: float("inf") for v in graph}
    previous = {v: None for v in graph}
    distance[start] = 0
    queue = [(0, start)]
    fixed = []
    while queue:
        dist, vertex = heapq.heappop(queue)
        if vertex in fixed:
            continue                                  # an out of date entry
        fixed.append(vertex)
        for neighbour, weight in graph[vertex].items():
            new = dist + weight
            if new < distance[neighbour]:
                distance[neighbour] = new
                previous[neighbour] = vertex
                heapq.heappush(queue, (new, neighbour))
        row = [distance[v] for v in graph]
        print("fixed", vertex, row)
    return distance, previous

def path_to(previous, goal):
    path = []
    while goal is not None:
        path.append(goal)
        goal = previous[goal]
    return path[::-1]

distance, previous = dijkstra(graph, "A")
print(path_to(previous, "E"), distance["E"])

The lines printed are the rows of the working table the exam asks for, with the columns in the order A, B, C, D, E, F:

StepFixedABCDEF
1A079∞∞14
2B07922∞14
3C07920∞11
4F079202011
5D079202011
6E079202011

Step 3 is the one that shows the method working. When C is fixed at 9, the route to D through C costs 9 + 11 = 20, which beats the 22 found through B. The route to F through C costs 9 + 2 = 11, which beats the direct edge of 14. The bold values are distances that were improved.

Read the shortest path backwards

Each vertex remembers the vertex it was reached from. To find the route to E, start at E and follow previous back to A, then reverse it. E was reached from F, F from C, and C from A, so the route is A, C, F, E, with a total of 20.

Why a negative weight breaks it

Dijkstra's algorithm is greedy. When it fixes a vertex, it assumes no later route can be shorter, because every later route has to go through vertices that are further away and can only add. A negative edge could subtract, making an apparently longer route into a shorter one after the vertex was already declared final.

How much work

With a heap, each edge causes at most one push, and each push or pop costs the logarithm of the heap's size. The total is about (V + E) log V steps, where V is the number of vertices and E the number of edges. That is fast enough for a road network of millions of junctions.

Dijkstra's algorithm finding the shortest path from A to FA weighted graph of six nodes, A to F, with the weight of each edge written on it, and the shortest path from A to F picked out in coral. Dijkstra's algorithm starts at A with a distance of 0 and every other node at infinity. It repeatedly visits the unvisited node that is nearest to the start and updates its neighbours if the route through it is shorter. It visits A, then C at distance 2, then B at 3, then D at 8, then E at 10 and finally F at 13. The working table records each node's distance and the node it was reached via, and reading the via column back from F gives the path: F from E, E from D, D from B, B from C and C from A. So the shortest path is A, C, B, D, E, F with a total weight of 2 plus 1 plus 5 plus 2 plus 3, which is 13. The direct looking route through the heavy edge from C to E would cost 15.Shortest path from A to F4215810263ABCDEFstartfinishshortest: A, C, B, D, E, F = 2 + 1 + 5 + 2 + 3 = 1312The workingnodedistanceviaA0-C2AB3CD8BE10DF13Evisited in this order:A, C, B, D, E, Falways visit the unvisitednode nearest to the start34
  1. A weighted graph: each edge has a cost, such as a distance or a time.
  2. The shortest route is not always the one with the fewest edges: here it takes five edges to save two units.
  3. The working table holds each node's best distance so far and the node it came via.
  4. Always visit the unvisited node nearest the start, then update its neighbours. Read the route back through the via column.
Dijkstra gives the cheapest route to every node. Show the table, not just the answer.

Dijkstra's algorithm spreads out from the start in every direction, like a ripple, and fixes every vertex nearer than the goal. If you are navigating from Manchester to Leeds, that includes vertices in Wales. A* (say "A star") avoids this by being guided: it prefers vertices that look as if they are heading towards the goal.

For each vertex n it uses two numbers:

  • g(n): the real cost of the best route found so far from the start to n
  • h(n): a heuristic, an estimate of the cost still to go from n to the goal

and it always expands the vertex with the smallest f(n) = g(n) + h(n), the estimated cost of the whole journey through n. Dijkstra's algorithm is exactly A* with h = 0 for every vertex.

The heuristic

A heuristic is a rule of thumb: a quick estimate that need not be exact. For finding a route across a map, the straight line distance to the goal is a natural one. On a grid where you can move up, down, left and right, it is the Manhattan distance, the number of rows plus the number of columns still to travel.

For A* to be certain to find the shortest path, the heuristic must be admissible: it must never overestimate the true remaining cost. The Manhattan distance is admissible on this grid, because you can never get there in fewer moves than the rows plus columns, and walls can only make it longer. A heuristic that overestimated could make the best route look too expensive, and A* would pass it over.

Dijkstra and A* on the same grid

Both programs below are the same code. The only difference is whether h is 0 or the Manhattan distance. o marks every cell that was expanded, and * is the path found. Ties in f are broken in favour of the smaller h, which keeps A* heading for the goal.

Python
import heapq

GRID = [
    "S.........",
    "..........",
    "..........",
    "......#...",
    "......#...",
    "......#...",
    "......#...",
    "..........",
    "..........",
    ".........G",
]

def search(grid, start, goal, use_heuristic):
    rows, cols = len(grid), len(grid[0])

    def h(cell):
        if not use_heuristic:
            return 0
        return abs(cell[0] - goal[0]) + abs(cell[1] - goal[1])

    best = {start: 0}
    previous = {start: None}
    queue = [(h(start), h(start), 0, start)]
    expanded = []
    while queue:
        f, estimate, g, cell = heapq.heappop(queue)
        if cell in expanded:
            continue
        expanded.append(cell)
        if cell == goal:
            break
        row, col = cell
        for step_row, step_col in ((0, 1), (1, 0), (0, -1), (-1, 0)):
            r, c = row + step_row, col + step_col
            if 0 <= r < rows and 0 <= c < cols and grid[r][c] != "#":
                new_g = g + 1
                if new_g < best.get((r, c), 10 ** 9):
                    best[(r, c)] = new_g
                    previous[(r, c)] = cell
                    heapq.heappush(queue, (new_g + h((r, c)), h((r, c)), new_g, (r, c)))
    path = []
    cell = goal
    while cell is not None:
        path.append(cell)
        cell = previous[cell]
    return expanded, path[::-1]

for name, use_h in (("Dijkstra", False), ("A*", True)):
    expanded, path = search(GRID, (0, 0), (9, 9), use_h)
    canvas = [list(line) for line in GRID]
    for row, col in expanded:
        if canvas[row][col] == ".":
            canvas[row][col] = "o"
    for row, col in path[1:-1]:
        canvas[row][col] = "*"
    print(name, "expanded", len(expanded), "cells; path of", len(path) - 1, "steps")
    for line in canvas:
        print("".join(line))

Both find a path of 18 steps, which is the shortest possible. Dijkstra's algorithm had to look at 96 cells to prove it, and A* looked at 19.

Choosing between the three

Breadth firstDijkstraA*
Edge weightsAll equalAny, not negativeAny, not negative
Finds the shortest pathYes, in edgesYesYes, if the heuristic is admissible
ExploresEvenly in all directionsEvenly in all directionsMostly towards the goal
NeedsA queueA priority queueA priority queue and a heuristic
Used forFewest hopsNetwork routing protocols, shortest path to many destinationsSatnav, game characters finding their way

Dijkstra's algorithm answers "how far is it to everywhere?" A* answers "what is the best route to this place?", and gets there faster by using what it knows about where the goal is.

Always write f = g + h, and say which is which

A frequent question gives a table of g and h values for the vertices waiting in the open list, and asks which A* expands next. Work out f for each one, write it down, and choose the smallest. Say that h must never overestimate, or the shortest path is not guaranteed.

Dijkstra and A* finding a path round a wall, and how many cells each one looks atTwo searches find a path from S on the left to G on the right round a wall. Dijkstra's algorithm picks the cell with the smallest distance from the start, so it spreads out in every direction and looks at a large area of the grid. A star picks the cell with the smallest distance travelled plus a heuristic guess of the distance left, so it favours cells that look closer to the goal and looks at far fewer cells. Both find a shortest path of the same length, drawn in red. The explored cells are shaded blue for Dijkstra and green for A star.Dijkstra: looks everywhere1SGlooked at 98 cells, path of 15 stepsA*: looks towards the goal2SGlooked at 38 cells, path of 15 stepsThe only difference is a guess3Dijkstra picks the cell with the smallest distance from the start: g.A* picks the smallest g + h, where h is a guess of the distance left, so it favours cells that look closer to the goal.
  1. Dijkstra explores in every direction, cell by cell in order of distance from the start.
  2. A* adds a heuristic, a guess of the distance still to go, so it heads for the goal.
  3. The difference is one guess: g for Dijkstra, g plus h for A*. The heuristic must never overestimate.
A* finds the same shortest path as Dijkstra, but looks at far fewer cells, as long as the heuristic never overestimates.

Key terms you must be able to define

Shortest path
The route between two vertices of a weighted graph with the lowest total weight.
Dijkstra's algorithm
An algorithm that finds the shortest distance from a start vertex to every other vertex in a graph with non-negative weights.
Priority queue
A queue from which the item with the highest priority, here the smallest distance, is always removed first.
Greedy algorithm
An algorithm that makes the best looking choice at each step, without reconsidering it.
A* search
A shortest path algorithm that uses a heuristic to prefer vertices that appear to lead towards the goal.
Heuristic
An estimate or rule of thumb that is quick to calculate and need not be exact.
Admissible heuristic
A heuristic that never overestimates the true cost of reaching the goal.
g(n)
In A*, the cost of the best known route from the start to vertex n.
h(n)
In A*, the heuristic estimate of the cost from vertex n to the goal.
Manhattan distance
The distance between two points on a grid measured as rows plus columns, as when moving only up, down, left and right.

Flashcards

Say the definition out loud before you flip the card. Retrieving it from memory is what builds recall, reading it again is not.

DefineShortest path
AnswerThe route between two vertices of a weighted graph with the lowest total weight.
DefineDijkstra's algorithm
AnswerAn algorithm that finds the shortest distance from a start vertex to every other vertex in a graph with non-negative weights.
DefinePriority queue
AnswerA queue from which the item with the highest priority, here the smallest distance, is always removed first.
DefineGreedy algorithm
AnswerAn algorithm that makes the best looking choice at each step, without reconsidering it.
DefineA* search
AnswerA shortest path algorithm that uses a heuristic to prefer vertices that appear to lead towards the goal.
DefineHeuristic
AnswerAn estimate or rule of thumb that is quick to calculate and need not be exact.
DefineAdmissible heuristic
AnswerA heuristic that never overestimates the true cost of reaching the goal.
Defineg(n)
AnswerIn A*, the cost of the best known route from the start to vertex n.

Reach the top grade on this topic

Shortest path questions are marked on method shown in a table, and on being exact about the difference between the two algorithms.

  • Set up the working table with infinity for every vertex but the start, and update a row per fixed vertex
  • Only replace a distance when the new total is strictly smaller, and record where it came from
  • Write the path backwards from the goal using the recorded previous vertices, then reverse it
  • State A* as f = g + h and name g as cost so far and h as the estimate still to go
  • Say that an admissible heuristic never overestimates, and that this is what guarantees the shortest path

Mistakes that cost marks here

Avoid these
  • Fixing a vertex that does not have the smallest distance among those not yet fixed.
  • Adding the edge weight to the start distance instead of to the distance of the vertex just fixed.
  • Describing A* as always faster, without saying that it depends on a good heuristic.
  • Using a heuristic that overestimates the cost and so losing the guarantee of the shortest path.
  • Applying Dijkstra's algorithm to a graph with negative weights.
Fun fact

Edsger Dijkstra designed his shortest path algorithm in about twenty minutes in 1956, without pencil and paper, while shopping with his fiancée in Amsterdam. He later said that the lack of pencil and paper had forced him to avoid unnecessary complication.

Knowledge check

0 of 10 answered
1What does Dijkstra's algorithm find?
2Which data structure is used to choose the next vertex in Dijkstra's algorithm?
3What distance does Dijkstra's algorithm give every vertex except the start at the beginning?
4Why can Dijkstra's algorithm not be used on a graph with negative weights?
5In A* search, what does f(n) = g(n) + h(n) represent?
6What does g(n) represent in A* search?
7What is an admissible heuristic?
8Which heuristic suits a grid where movement is up, down, left or right only?
9What is A* search when the heuristic is zero for every vertex?
10Why is A* often faster than Dijkstra's algorithm for finding a route to one particular place?
Score 0 / 10

Exam-style questions

Five written questions in the style of the real paper. Write a full answer, then let the marker check it against the mark scheme.

19 marks
1State the purpose of Dijkstra's shortest path algorithm, and state one restriction on the graph it is used on.2 marks
State

Mark scheme0 / 2
  • LIt finds the shortest path, or the shortest distance, from a start vertex to the other vertices of a weighted graph
  • LThe weights must not be negative
Model answer

Dijkstra's algorithm finds the shortest path, meaning the one with the lowest total weight, from a start vertex to the other vertices of a weighted graph. It can only be used if none of the edge weights is negative.

The marker looks for key phrases. If you said it another way and it is still right, give yourself the mark.
2Use Dijkstra's algorithm to find the shortest path from A to E in the graph shown. Show your working, and state the path and its total weight.6 marks
Show
EdgeWeight
A to B4
A to C2
B to C1
B to D5
C to D8
C to E10
D to E2

Mark scheme0 / 6
  • LA is given 0, and B and C are updated to 4 and 2 after A is fixed
  • LC is fixed next, being the smallest, and B is improved to 3
  • LB is fixed and D is updated to 8
  • LD is fixed and E is updated to 10
  • LThe shortest path is A, C, B, D, E
  • LThe total weight is 10
Model answer

A is fixed at 0, which sets B to 4 and C to 2. C has the smallest distance so it is fixed next. The route to B through C is 2 + 1 = 3, which is less than 4, so B becomes 3. C also sets D to 10 and E to 12. B is fixed next at 3, and D becomes 3 + 5 = 8, which is less than 10. D is fixed at 8, and E becomes 8 + 2 = 10, which is less than 12. E is fixed at 10. Reading back from E, it was reached from D, D from B, B from C and C from A. The shortest path is A, C, B, D, E and its total weight is 10.

The marker looks for key phrases. If you said it another way and it is still right, give yourself the mark.
3In an A* search, four vertices are waiting to be expanded. Their values of g and h are shown. Calculate f for each vertex and state which vertex is expanded next.3 marks
Calculate
Vertexgh
P46
Q72
R39
S55

Mark scheme0 / 3
  • Lf is calculated as g + h
  • LThe values are P = 10, Q = 9, R = 12 and S = 10
  • LQ is expanded next, as it has the lowest f
Model answer

f is g plus h, so P is 4 + 6 = 10, Q is 7 + 2 = 9, R is 3 + 9 = 12 and S is 5 + 5 = 10. The vertex with the lowest value of f is expanded next, which is Q.

The marker looks for key phrases. If you said it another way and it is still right, give yourself the mark.
4Explain the role of the heuristic in the A* algorithm, and why it must not overestimate the cost.4 marks
Explain

Mark scheme0 / 4
  • LThe heuristic is an estimate of the cost from a vertex to the goal
  • LIt is used to choose the vertex to expand next, so the search is guided towards the goal
  • LThis means fewer vertices are explored than in Dijkstra's algorithm
  • LIf it overestimated, a vertex on the shortest path could be made to look too costly and be ignored, so the path found might not be the shortest
Model answer

The heuristic is an estimate of the cost of getting from a vertex to the goal. A* adds it to the cost so far and expands the vertex with the lowest total, which guides the search towards the goal, so it explores fewer vertices than Dijkstra's algorithm, which spreads out in every direction. It must not overestimate because a vertex on the shortest path could then look more expensive than it really is, and be passed over, so the path found might not be the shortest.

The marker looks for key phrases. If you said it another way and it is still right, give yourself the mark.
5A satellite navigation system must find a route between two towns on a road network. Compare using Dijkstra's algorithm with using A* for this task.4 marks
Compare

Mark scheme0 / 4
  • LBoth find the shortest route if the weights are not negative and the heuristic is admissible
  • LDijkstra's explores in every direction from the start, including roads that lead away from the destination
  • LA* uses an estimate, such as the straight line distance to the destination, to explore mainly towards it
  • LSo A* usually finds the route more quickly, and is better for this task, though it needs a suitable heuristic
Model answer

Both algorithms find the shortest route, provided the road lengths are not negative and the heuristic used by A* never overestimates. Dijkstra's algorithm explores outwards in every direction from the start, including roads leading away from the destination. A* uses an estimate, such as the straight line distance to the destination, to explore mainly towards it. A* is therefore usually quicker for finding a route between two particular towns, and the better choice here, although it needs a suitable heuristic to be available.

The marker looks for key phrases. If you said it another way and it is still right, give yourself the mark.
Machine mark 0 / 19