The Algorithms logo
The Algorithms
AboutDonate

Djikstra

s
"""
    dijkstra(graph::Vector{Vector{Tuple{Int,Int}}}, source::Int)

Given a directed graph with weights on the arcs and a source vertex, the dijkstra algorithm
calculates the distance from the source to all other vertices, and the solution tree associated
with those distances. The solution tree is given by a vector `prev` which stores the source of the
arc that arrives at each vertex. By definition: distance[source] = prev[source] = 0. If a vertex v
is not reachable from the source, then distance[v] = prev[v] = -1.

# Arguments:
- `graph`: a directed graph with weights on the arcs
- `source`: the source vertex from which the distances will be calculated

# Example
```jldoctest
graph = [
    [(2, 8), (3, 6), (4, 4)],
    [(3, 1), (5, 5)],
    [(5, 4)],
    [(2, 3), (5, 9)],
    [(1, 2), (3, 2), (4, 5)],
    [(1, 1), (4, 3)],
]
distances, prev = dijkstra(graph, 1)

println("v | dist | path")
for v in eachindex(graph)
    distance = distances[v] == -1 ? "  NR" : lpad(distances[v], 4) # NR: Non Reachable
    path = join(get_dijkstra_path(prev, v), " -> ")
    println("\$v | \$distance | \$path")
end

# output

v | dist | path
1 |    0 | 1
2 |    7 | 1 -> 4 -> 2
3 |    6 | 1 -> 3
4 |    4 | 1 -> 4
5 |   10 | 1 -> 3 -> 5
6 |   NR | 
```

Contributed By: [Gabriel Soares](https://github.com/gosoares)
"""
function dijkstra(graph::Vector{Vector{Tuple{Int,Int}}}, source::Int)
    # V = {1, 2, ..., length(graph)}: set of vertices
    # A = {(i, j): i and j are adjacents; i,j in V}: set of arcs
    # graph[i] = {(j, distance[i to j]): (i, j) in A}: adjacency list

    # distance[j] min distance from source to j
    distance::Vector{Int} = fill(typemax(Int), length(graph))
    prev::Vector{Int} = fill(-1, length(graph))
    visited::Vector{Bool} = falses(length(graph))
    pq = MinHeap{Tuple{Int,Int}}() # (distance, vertex) | priority queue

    distance[source] = 0
    prev[source] = 0 # to mark the source
    push!(pq, (0, source))

    while !isempty(pq)
        # get the vertex that was not yet visited and the distance from the source is minimal
        dv, v = pop!(pq)
        visited[v] && continue
        visited[v] = true

        for (u, dvu) in graph[v] # dvu: distance from v to u
            # check if u can be reached through v with a smaller distance than the current
            if !visited[u] && dv + dvu < distance[u]
                distance[u] = dv + dvu
                prev[u] = v
                push!(pq, (distance[u], u))
            end
        end
    end

    replace!(distance, typemax(Int) => -1) # distance[v] = -1 means that v is not reachable from source
    return distance, prev
end

"""
    get_dijkstra_path(tree::Vector{Int}, dest::Int)

Given a solution `tree` from the [`dijkstra`](@ref) algorithm, extract the path from the source to
`dest`, including them.

# Arguments:
- `tree`: solution tree from the [`dijkstra`](@ref) algorithm
- `dest`: path's destionation vertex
"""
function get_dijkstra_path(tree::Vector{Int}, dest::Int)
    path = Int[]
    tree[dest] == -1 && return path # not reachable

    while dest != 0
        push!(path, dest)
        dest = tree[dest]
    end
    return reverse!(path)
end