GA: 7. Dijkstra's algorithm
source link: https://mingzhi198.github.io/p/ga-7.-dijkstras-algorithm/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
Statement
- This article is my study notes from the book: grokking algorithm: an illustrated guide for programmers and other curious people.
- Please refer to the original work for more details and indicate the source for reprinting.
- Weighted graphs: assigning weight to some edges.
- Dijkstra’s algorithm: What’s the shortest path to X?
- Cycles in graphs will make Dijkstra’s algorithm not work.
Dijkstra’s algorithm
Sometimes the shortest path is not the fastest path, since there are different weights with various edges.
In the figures above, you are going to walk from home to shool.
By using BFS, we will find the shortest path A, while Dijkstra’s algorithm is better for us to find the fastest path.
Therefore, you will choose path B instead of path A, since it will take you less time from home to school.
Four steps of Dijkstra’s algorithm
- A. Find the node that will take you the least time to get to.
- B. Update the costs of this node’s neighbors.
- C. Repeat until you’ve done this for all nodes in the graph.
- D. Get the final path.
In the figure above, we want to walk from Begin to the End.
Step A: It will take you 6 minutes from Begin to A and 2 minutes from Begin to B.
Step B: Find how long it will take you to get to B’s neighbors by following edges from B.
- You find that it will take less time to get to A and 7 minutes to get to End. Then, we update the costs.
Step C: Repeat steps above.
Step A: Now, we find that Node A will take you less time to get to.
Step B: Update the costs of neighbors of Node A.
Finally, we find that it only take us 6 minutes to get to the End.
- Remember that Dijkstra’s algorithm can only be used in directed acyclic graphs, which is called DAGs.
Trading for a piano
Here, the author provide an example to further illustrate Dijkstra’s algorithm.
You have a book, and other people have other items, the relationship of which you can see from the figure above.
In the figure, you want trade your book for the piano.
To solve this problem, we should follow the steps of Dijkstra’s algorithm.
- Step 1: Find the node with the least cost.
We can see from the figure that it costs you 0 to get to poster, which is our target. - Step 2: Find the costs of it’s neighbors
We compare the costs to it’s neighbors, and update the costs with smaller values and their parents(from which node we get to these neighbors).
- Repeat:
- Step 1: The node LP is the next one with the least cost.
- Step 2: Update the costs and parent of LP’s neighbors.
- Repeat:
- Step 1: The node Guitar is the next one with the least cost.
- Step 2: Update the costs and parent of Guitar’s neighbors.
- Repeat:
- Step 1: the node Drum is the last one to be processed.
- Step 2: Update the costs and parent of Drum’s neighbors.
Finally, we get the cheapest path: Book->LP->Drum->Piano
Implementation
- Golang
package main
import (
"fmt"
"math"
"util"
)
type Node struct {
Name string
Cost int
}
type Table struct {
Name string
Parent string
Cost int
}
var (
graph map[string][]*Node
table map[string]*Table
)
func init() {
graph = make(map[string][]*Node, 6)
graph["book"] = []*Node{
&Node{Name: "lp", Cost: 5},
&Node{Name: "poster", Cost: 0},
}
graph["poster"] = []*Node{
&Node{Name: "guitar", Cost: 30},
&Node{Name: "drum", Cost: 35},
}
graph["lp"] = []*Node{
&Node{Name: "guitar", Cost: 15},
&Node{Name: "drum", Cost: 20},
}
graph["guitar"] = []*Node{
&Node{Name: "piano", Cost: 20},
}
graph["drum"] = []*Node{
&Node{Name: "piano", Cost: 10},
}
graph["piano"] = []*Node{}
table = make(map[string]*Table, 5)
table["lp"] = &Table{Name: "lp", Cost: 5, Parent: "book"}
table["poster"] = &Table{Name: "poster", Cost: 0, Parent: "book"}
table["guitar"] = &Table{Name: "guitar", Cost: math.MaxUint32, Parent: ""}
table["drum"] = &Table{Name: "drum", Cost: math.MaxUint32, Parent: ""}
table["piano"] = &Table{Name: "piano", Cost: math.MaxUint32, Parent: ""}
return
}
func GetDijkstraPath(table map[string]*Table, name string) (ret []string) {
ret = append(ret, name)
for {
nodeT, ok := table[name]
if !ok {
break
}
ret = append(ret, nodeT.Parent)
name = nodeT.Parent
}
n := len(ret)
for i := 0; i < n/2; i++ {
ret[i], ret[n-i-1] = ret[n-i-1], ret[i]
}
return
}
func FindCheapestNode(list map[string]*Table, lastMinCost int) (min *Table) {
if len(list) <= 0 {
return
}
for _, tab := range list {
// Be careful that the minimal value we want must be bigger than the last minimal value.
// And there will be no cheaper node than the last minimal cost
if tab.Cost <= lastMinCost {
continue
}
if nil == min {
min = tab
continue
}
if tab.Cost < min.Cost {
min = tab
}
}
return
}
func Dijkstra(graph map[string][]*Node, name string) (finalPath []string, totalCost int) {
lastMinCost := -1
for i := 0; i < len(table); i++ {
// 1. find the cheapst node
node := FindCheapestNode(table, lastMinCost)
fmt.Println("cheapest node: ", util.ToString(node))
lastMinCost = node.Cost
// 2. update it's neighbors' cost and parent
for _, neighbor := range graph[node.Name] {
neighborT, ok := table[neighbor.Name]
if !ok {
table[neighbor.Name] = &Table{Name: neighbor.Name}
}
neighborT.Cost = node.Cost + neighbor.Cost // update cost
neighborT.Parent = node.Name // update parent
}
}
fmt.Println(util.ToStringIndent(table))
finalPath = GetDijkstraPath(table, name)
totalCost = table[name].Cost
return
}
func main() {
path, cost := Dijkstra(graph, "piano")
fmt.Printf("Dijkstra path: %s, cost: %d\n", path, cost)
}
- Output
cheapest node: {"Name":"poster","Parent":"book","Cost":0}
cheapest node: {"Name":"lp","Parent":"book","Cost":5}
cheapest node: {"Name":"guitar","Parent":"lp","Cost":20}
cheapest node: {"Name":"drum","Parent":"lp","Cost":25}
cheapest node: {"Name":"piano","Parent":"drum","Cost":35}
{
"drum": {
"Name": "drum",
"Parent": "lp",
"Cost": 25
},
"guitar": {
"Name": "guitar",
"Parent": "lp",
"Cost": 20
},
"lp": {
"Name": "lp",
"Parent": "book",
"Cost": 5
},
"piano": {
"Name": "piano",
"Parent": "drum",
"Cost": 35
},
"poster": {
"Name": "poster",
"Parent": "book",
"Cost": 0
}
}
Dijkstra path: [book lp drum piano], cost: 35
Negative-weight edges
Implementation
Summary
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK