Now we'll look for, at Dijkstra's algorithm for finding shortest paths in graphs with non-negative edge weights. Dijsktra is a famous figure in computer science. And you can amuse yourself by reading some of the things that he wrote. He was a very prolific writer, and, kept notebooks. And then here really some of his, famous quotes. Now, you need to may know something about, old languages to appreciate some of these. For example, COBOL was one of the first business oriented languages, programming languages and it tried to, make it so that programs kind of read like English language sentences. But purists in computer science, such as Dijkstra really didn't like that language as you can tell, The use of COBOL cripples the mind. It's teaching should therefore be regarded as a criminal offense. Actually, when I first came to Princeton and was setting up the department here, One of, there was a big donor, who actually, wanted to have me fired and the department closed down because we wouldn't teach COBOL. I didn't know about Dijkstra's quote at that time, I wish I had. And there's some other opinions that Dijkstra had. You can amuse yourself on the web, reading some of his writing, But he was, here's another one that's actually pretty relevant today. Object-oriented programming is an exceptionally bad idea that could only have originated in California. Dijkstra worked in Texas, the University of Texas for a while, and of course, came from the Netherlands. Okay. But, Dijkstra, did, invent, a very, very important algorithm, that's very widely used for solving the shortest paths problem. It's the one that's in your maps app and in your, car, and many, many other applications. So let's take a look at how that algorithm works. It's a very simple algorithm. We, consider, what we're going to do is consider the vertices In increasing order of their distance from the source. So and a way we're going to do that is take the next vertex, add it to the tree and relax all the edges that point from that vertex. So let's talk about it in terms of a demo and then we'll look at the code. So our mandate is to consider vertices in increasing order of distance from the source. So, our source is zero in this case so what vertices are closest to the source? Well, we can look at the edges that point from that vertex. Well, start out with vertex is zero, is the distance zero from the source, so we pick it. Then we add that vertex to the shortest paths tree, and relax all its edges. And relaxing all the edges pointing from zero in this case is just going to uptake the, this two and edgeTo entries for five, eight, nine. So in this case, this says that the shortest path from zero to one is to go along zero, one and that's distance five and from zero to four, it's to go along zero, four and that's distance nine, And zero to seven, let's go take it zero, seven that's distance eight. And the edge weights are positive. We're not going to find a shorter path to anyone, of what, Sorry. The edge weights are positive and those are the shortest paths that we know so far to the vertices. Now, the first key point of the algorithm is take the closest one. If you take the closest one, in this case, it's one, Then we know we're not going to find a shorter, shorter path to one. The only other way to go out of zero is to take one of these other edges, and they're longer. So, and all the edge weights are positive. So we're not going to find a shorter way from zero to one, than to take the edge, 0-1. So that means that edge 0-1 is on the shortest path tree, That's what the algorithm says. Take the next closest vertex to the source. in this case, it's one. And then, We're going to add that edge, that vertex to the tree. And relax all the edges that point from that. So now both zero and one are on the tree, so now let's look at the edges pointing to that and we have to relax each one of those. So in this case if you go from one to four, Then we look at four, we know a path of length nine. And one to four doesn't give us a shorter path, so we leave that edge alone. One to three gives us a shorter path to three which is going to be twenty, cuz you went from zero to one, and then one to three is of length twenty. And one to two, also, we didn't know a way to two, But now we know now we know one, or better one is seventeen. So that completes the relaxation of all the edges pointing from one. So now we have zero and one on the tree, And. We would, consider next, The next closest vertex to the source. So what we have in this two is the shortest paths to all the vertex, vertices that we've seen so far and this one says that the we've been to the zero and one so the next closest one is seven, which is distance eight. So we are going to choose vertex seven. And again, that's the shortest path we've seen so far, we are not going to find a shorter one cuz to get to every other vertex is longer. and so, we know it's on the shortest paths tree, and now we're going to relax all the edges pointing from seven. In this case, both of them, the one from seven to two, gives us a shorter way to two than what we knew before, and the one from seven to five gives us a shorter way to five than we knew before. Well, we hadn't been to five before. So that relaxes seven, so now seven is on the shortest paths tree. So now 4's the next closest path vertex to the source. From the edge zero, four which is of length nine. So that's the one that we're going to pick next for the tree. We're not going to find a shorter path there, And we relax, relax along all its edges. In this case, we find a shorter path to five than we knew before and a shorter path to six, well, we visit six for the first time. Okay, so that's four, so now we just have to worry about two, three, five, and six, and five is the one over there, and so we select vertex five, Relax along its edges. In this case those edges both give us better paths than we knew before. So now we're left with three vertices, and two is the winner. This is the two from the sources fourteen, To three, it's twenty and to six, it's 26. Relax it's edges, and it gives us again, new shorter paths to both three and six. And then the last, this next to last step is to pick three and that does not give us a new way to six, and then finally we pick six. And then we now know, The shortest path to all the vertices from vertex zero. If we just take the edgeTo edges, That's from one, to one you take zero, to five you take two, And so forth you get the shortest paths tree and that gives the shortest way to get from the source to every vertex. That's a simulation of Dijkstra's algorithm. Here's a demo of Dijsktra's algorithm running in a large digraph. So the source is the protects the center with the open circle and you can see it considers the vertices in the graph in order of their distance from the source. Now you can see this is maybe like a lake in the middle of the graph, And so it's not going to move on from those vertices. It's going to take a little while to get around the lake. And again, if, this visualization is produced by our code, Then it, and it gives a very, natural feeling for the way that the algorithm works, This, in principle. And I think, in fact, in many cases, is what your, car does when you turn the map system on, It computes the shortest path to everywhere, And then it's all ready when you ask for a certain place, how to get there. So here's just, starting from another point in the graph. You have, if you happen to live at a corner of the Earth, then it's going to be slightly longer to get to the other corner. And, a nice feeling for how the algorithm gets its job done. Again, when it gets into those blank areas, it takes a little while to get over to the other side. And of course if we had islands, if we had little roads in the middle there that were not connected, Then we know where to get to them from the source and we wouldn't see them. And that's fine for the way our algorithm works. We just leave that out of the demo and the proof to avoid adding an extra comment about that for every algorithm. That's a visualization of Dijkstra's algorithm on a large graph. So, so how do we prove it's correct? Well, essentially we've prove that it's an instance of the generic algorithm. So, first thing is that every edge is relaxed exactly once. Every time we, everytime we put a vertex onto the tree, we relax all the edges that come from that vertex and we never reconsider the vertex. And what does relaxation do? Relaxation ensures that after the relaxation, either way there was before, afterwards, you have the distance to w is less than or equal the distance to v plus e to the, e plus the way to the edge. Either it's equal cuz we just made it that way, or it's less than it was before and the edge was not relevant. And this inequality is going to hold for every an, entry for every edge corresponding to every edge, for just two entries corresponding to every edge because number one, the, these two values are always increasing. We never I'm sorry, we're always decreasing. The only thing we ever change, only reason we ever change distTo w is to make it smaller. If we, if we found an edge that would make it bigger we ignor that edge, So it's always decreasing. So when we change distTo w, we're not going to make that inequality holds. We're just going to make it better. And this distTo v is not going to change at all. Once we relax an edge from a vertex, we're done with that vertex. We're not going to process that at all. So, then when we're done we have the optimality conditions hold that exactly is the optimality condition. And, not only that, we have a path from the source to every vertex. So that's the correctness proof for Dijkstra's algorithm based on the optimality conditions. Here's the code. It's similar to code that we've seen before. We're going to use the indexed priority queue that allows us to implement the decreased key operation. And we have our edgeTo and distTo arrays that are part of the shortest paths computation and the goal of the shortest paths computation. So we initialized the constructor, initializes those rays and including the Index Minimum PQ. and we start out with all the distances infinity except for the distance of the source is zero. We put the source on the priority Q and then what we're going to do is take the, Edge that closest to the source off the priority. That's on the priority queue off and then relax all the edges that are adjacent to that, so I'm using our standard iterator to get all the edges that emanate from that vertex and relax them. And then the relax code is just like the code that was showed when describing relaxation, except that it also updates the priority queue. If the, vertex, that, that edge goes to is on the priority queue, it gives a new shorter way to get to that, So we have to, decrease the key on the priority queue, Cuz if it's not on the priority queue, we insert it, And that's it. That's a complete implementation, of Dijsktra's algorithm using modern data structures. Now, this algorithm might seem very familiar than, if you've been paying attention. It's essentially the same algorithm as Prim's algorithm. The difference is that, in both cases, we're building what's called a spanning tree of the graph. But in Prim's algorithm, we take a vertex that, that's not on the tree using the rule of let's take the vertex that's closest to the tree, anywhere on the tree, Closest to some vertex on the tree. For Dijsktra's algorithm, we take next, the vertex that's closest to the source through a path, they go through a tree and then into a non-tree vertex. That's the difference and the differences in the code have to do with the fact that Prim's algorithm is for an undirected graph, Dijsktra's algorithm is for a directed graph, But essentially, they're the same algorithm. And actually, several of the algorithms that we've talked about are in this same family. They compute a spanning tree. I have, you, you have a tree that takes care of where that records where you've been in the graph, From every vertex, back to where you started. Then you used different rules for choosing which vertex to add next. For breadth-first search, you use a Q, for depth-first search, you use something like a stack and then you have to decide what to do, if you encounter a vertex that you've been to before. But many graph algorithms use this same basic idea. So in particular, when we are talking about what the running time of Dijsktra's algorithm, it depends on what priority Q the implementation we use, And it's the same considerations as for Prim's algorithm. We have V insert operations, every vertex goes on to the priority queue. V delete-min, every vertex comes off the priority queue. and for every edge, in the worst case, we could compute decrease-key operation. So, the original implementations of Dijsktra's algorithm used an unordered array, Which would mean that it would take time proportional to V to find the minimum, To find the vertex closest to the source. So the total running time would be proportional to V squared. That's not adequate for the huge sparse graph that we see in practice today like the map in your car. So, the binary heap data structure, makes the,, makes it feasible to run this algorithm. And that's where all the operations take time proportional to log V. We have to use the indexing trick that we talked about last time to support decrease-key. But still, we get a total running time of E log V, Which makes it feasible. And just as with Prim's algorithm, by using a implementation of the priority queue that can do a faster decrease-key, you can get a faster algorithm. And in, in practice something like a 4-way hit is going to give quite a fast algorithm. And so, more expensive to insert and t-, to insert but much faster to delete min in decrease key. And again, there's a theoretical data structure that's not useful in practice. It gets the running time down to E + V log V. Of course, if your graph, graph is dense, And again, the examples I used, they're not. The array implementation is optimal, we can't do any better. We have to go through all the edges and might as well find the minimum, at the same time. But in, in, in practice, people use binary heaps, for sparse graphs. I maybe going to four way, If, the performance is really critical. The bottom line is, we have, extremely efficient implementations for the huge graphs that arise in practice nowadays.