For business execs
who love technology
Home » , , , , , , , , » How to implement Dijkstra algorithm with Python (solved with all explanations) ?

How to implement Dijkstra algorithm with Python (solved with all explanations) ?

In this post, I will show you how to implement Dijkstra's algorithm for shortest path calculations in a graph with Python.

NB: If you need to revise how Dijstra's work, have a look to the post where I detail Dijkstra's algorithm operations step by step on the whiteboard, for the example below.

First, let's choose the right data structures. The implementation can be a nightmare if you pick a bad data structure for describing the graphs. In python, the dict structure is particularly handy, so let's use it.

On the above example, s has two neighbors a and b, linked with cost 2 and 1 respectively. So, our graph dictionary will look like this:

graph = {'s': {'a': 2, 'b': 1},
            'a': {'s': 3, 'b': 4, 'c':8},
            'b': {'s': 4, 'a': 2, 'd': 2},
            'c': {'a': 2, 'd': 7, 't': 4},
            'd': {'b': 1, 'c': 11, 't': 5},
            't': {'c': 3, 'd': 5}}

Now, if you have followed my previous post detailing the pseudo code and operations of Dijsktra's shortest path tree algorithm, you know that we need to keep track of  the following information:
- which nodes are not visited yet: we can use a list for that.
- a predecessor and a cost for every node. Let's use dict type for these elements, it will ease our life when we look up their value for a given node.

So the signature of our function is the following:

def shortestpath(graph,src,dest,visited=[],distances={},predecessors={}):

It will return the shortest path from src to dest, but could as well return a shortest path tree.

You will need to know the two following python functions to implement Dijkstra smartly.

mydict.get(mykeyvalue,17)
this function of a dict element (here 'mydict') searches for the value of the dict for the keyvalue 'mykeyvalue'. If this key does not exist in the dict, the function does not raise an error. Instead, it returns the value provided as the second argument, here 17.
min(mydict, key=mydict.get)
The min function can be applied on dictionaries! You just have to specify the key function as in the above example.

Now, we have everything we need to implement the function. I propose that you try it on your side and then look at the solution. Otherwise, you'll not memorize the algorithm. That being said, it's your choice...



def dijkstra(graph,src,dest,visited=[],distances={},predecessors={}):
    """ calculates a shortest path tree routed in src
    """    
    # a few sanity checks
    if src not in graph:
        raise TypeError('the root of the shortest path tree cannot be found in the graph')
    if dest not in graph:
        raise TypeError('the target of the shortest path cannot be found in the graph')    
    # ending condition
    if src == dest:
        # We build the shortest path and display it
        path=[]
        pred=dest
        while pred != None:
            path.append(pred)
            pred=predecessors.get(pred,None)
        print('shortest path: '+str(path)+" cost="+str(distances[dest])) 
    else :     
        # if it is the initial  run, initializes the cost
        if not visited: 
            distances[src]=0
        # visit the neighbors
        for neighbor in graph[src] :
            if neighbor not in visited:
                new_distance = distances[src] + graph[src][neighbor]
                if new_distance < distances.get(neighbor,float('inf')):
                    distances[neighbor] = new_distance
                    predecessors[neighbor] = src
        # mark as visited
        visited.append(src)
        # now that all neighbors have been visited: recurse                         
        # select the non visited node with lowest distance 'x'
        # run Dijskstra with src='x'
        unvisited={}
        for k in graph:
            if k not in visited:
                unvisited[k] = distances.get(k,float('inf'))        
        x=min(unvisited, key=unvisited.get)
        dijkstra(graph,x,dest,visited,distances,predecessors)
        


if __name__ == "__main__":
    #import sys;sys.argv = ['', 'Test.testName']
    #unittest.main()
    graph = {'s': {'a': 2, 'b': 1},
            'a': {'s': 3, 'b': 4, 'c':8},
            'b': {'s': 4, 'a': 2, 'd': 2},
            'c': {'a': 2, 'd': 7, 't': 4},
            'd': {'b': 1, 'c': 11, 't': 5},
            't': {'c': 3, 'd': 5}}
    dijkstra(graph,'s','t')

And magic... It returns:

shortest path: ['t', 'd', 'b', 's'] cost=8

Well done and congratulations for reading this post to the end! Now, if you are working on Dijsktra's algorithm, I think you are preparing for an interview at Google, Microsoft, or Amazon. Am I right?

Then, the best tip I can give you is to look at two books "Cracking the PM Interview: How to Land a Product Manager Job in Technology" and "Cracking the Coding Interview: 150 Programming Questions and Solutions". They were written by Gayle Laakmann McDowell, a former recruiter from Google who also worked at Apple and I find them really great!

  

I hope this post has helped you. 

PS: I love your comments and I must apologize for not being able to answer them all in time. I have a demanding job and family. Thank you for your understanding!

SHARE

About Gilles

12 commentaires :

  1. Hi,
    If I am given an input as the following:
    node1, node2, length1
    node2, node3, length2
    node1, node3, length3

    How do I create a nested dictionary like your graph?

    Thank you very much for your help.

    ReplyDelete
  2. Hello,

    your question is incomplete, sorry! What's your data structure in input? A string, an array, ... This point conditions the conversion method to the nested dictionary format I propose. Anyway it should be pretty easy: a loop to go through your input and fill in the nested dictionary appropriately.

    ReplyDelete
  3. dic={}

    def add_dic(node1,node2,length):
    if node1 not in dic:
    dic[node1]={}
    dic[node1][node2]=length
    else:
    dic[node1].update({node2:length})

    ReplyDelete
  4. what if there won't be any path between s and t ?

    ReplyDelete
    Replies
    1. Good question :) In such case, the path has an infinite cost.

      Delete
  5. thanks alot and i really love the outlook of your blog. please can you teach me how to design my this way

    ReplyDelete
  6. Thank you for this article.
    I used it in my code and saved a lot of time :)

    I have to make a comment though: the end condition is not ok. Now it ends when it reaches the destination and if there is a shorter path it won't find it. I came around such a case. The proper end is when there are no more unvisited nodes. Then all the posibile distances are computed.
    There is a catch: you can't return the path in this case. Not without some othere modifications. (I didn't need the path so I skipped it)

    I just wanted to tell you this. Maybe it will help you like it helped me :)

    ReplyDelete
    Replies
    1. Actually, your wrong. Dijkstras algorithm builds upon the paths it already has and in such a way that it extends the shortest path it has. Hence, upon reaching your destination you have found the shortest path possible. This is the strength of Dijkstra's algorithm, it does not need to evaluate all nodes to find the shortest path from a to b. Of course, this only works if the distances between nodes are only positive.

      Delete
  7. Can you suggest why this error occurs? It does not occur in the previous use of ('inf').
    unvisited[k] = distances.get(k,float('inf'))

    TypeError: list indices must be integers, not str

    ReplyDelete
  8. Hi,

    I tried changing print to return, but it gave me none, is there way to return the values instead?

    ReplyDelete
  9. this is my input 1->7 7->2 2->5 5->6
    i need output 7->1->2->5->6
    how do i change this algorithm to obtain this output

    ReplyDelete