首页 > 其他分享 >Go - Finding the Shortest Path on a Graph

Go - Finding the Shortest Path on a Graph

时间:2023-10-09 23:00:00浏览次数:35  
标签:node algorithm graph Finding London Graph Go nodes AddEdge

Problem: You want to find the shortest path between two nodes on a weighted graph.


Solution: Use Dijkstra’s algorithm to find the shortest path between two nodes. Dijkstra’s algorithm also uses a priority queue, which can be implemented using a min heap.

 

Plenty of algorithms rely on graphs. One of the most well - known algorithms on graphs is Dijkstra’s algorithm , sometimes called the shortest path algorithm . As the name suggests, this simple yet effective algorithm finds the shortest path between two nodes in a graph.

Say you want to travel from London to Istanbul and you figure out the following possible routes through the different major cities in Europe. Figure 14 - 10 shows a map of Europe, with the cities as nodes of a weighted directed graph, and flight routes between them as edges. The flight times between two cities are the edge weights.

You have figured out the amount of time to fly from these cities to other cities but now you want to find the shortest amount of time needed to travel from London to Istanbul.

This is where Dijkstra’s algorithm comes in handy, and it’s probably the most popular algorithm to solve the shortest path problem. The algorithm itself is quite simple. Dijkstra famously came up with the algorithm without pen and paper, in about 20 minutes, while accompanying his fiancee shopping in Amsterdam.

You need two data structures for Dijkstra’s algorithm — a weighted graph and a min heap. The only change you need is the Node struct in the weighted graph, to add a through pointer to a Node . This field, when not nil, points to the previous node in the shortest path:

type   Node   struct   { 
      name      string 
      value     int 
      through   * Node 
}

Before you start with the algorithm, you need to populate the graph accordingly:

func   buildGraph ()   * Graph   { 
      graph   :=   NewGraph () 
      nodes   :=   make ( map [ string ] * Node ) 
      names   :=   [] string { "London" ,   "Paris" ,   "Amsterdam" ,   "Luxembourg" ,    "Zurich" ,   "Rome" ,   "Berlin" ,   "Vienna" ,   "Warsaw" ,   "Istanbul" } 
      for   _ ,   name   :=   range   names   { 
          n   :=   & Node { name ,   math . MaxInt ,   nil } 
          graph . AddNode ( n ) 
          nodes [ name ]   =   n 
      } 

      graph . AddEdge ( nodes [ "London" ],   nodes [ "Paris" ],   80 ) 
      graph . AddEdge ( nodes [ "London" ],   nodes [ "Luxembourg" ],   75 ) 
      graph . AddEdge ( nodes [ "London" ],   nodes [ "Amsterdam" ],   75 ) 
      graph . AddEdge ( nodes [ "Paris" ],   nodes [ "Luxembourg" ],   60 ) 
      graph . AddEdge ( nodes [ "Paris" ],   nodes [ "Rome" ],   125 ) 
      graph . AddEdge ( nodes [ "Luxembourg" ],   nodes [ "Berlin" ],   90 ) 
      graph . AddEdge ( nodes [ "Luxembourg" ],   nodes [ "Zurich" ],   60 ) 
      graph . AddEdge ( nodes [ "Luxembourg" ],   nodes [ "Amsterdam" ],   55 ) 
      graph . AddEdge ( nodes [ "Zurich" ],   nodes [ "Vienna" ],   80 ) 
      graph . AddEdge ( nodes [ "Zurich" ],   nodes [ "Rome" ],   90 ) 
      graph . AddEdge ( nodes [ "Zurich" ],   nodes [ "Berlin" ],   85 ) 
      graph . AddEdge ( nodes [ "Berlin" ],   nodes [ "Amsterdam" ],   85 ) 
      graph . AddEdge ( nodes [ "Berlin" ],   nodes [ "Vienna" ],   75 ) 
      graph . AddEdge ( nodes [ "Vienna" ],   nodes [ "Rome" ],   100 ) 
      graph . AddEdge ( nodes [ "Vienna" ],   nodes [ "Istanbul" ],   130 ) 
      graph . AddEdge ( nodes [ "Warsaw" ],   nodes [ "Berlin" ],   80 ) 
      graph . AddEdge ( nodes [ "Warsaw" ],   nodes [ "Istanbul" ],   180 ) 
      graph . AddEdge ( nodes [ "Rome" ],   nodes [ "Istanbul" ],   155 ) 
      return   graph 
}

Now that you have the graph, take a look at Dijkstra’s algorithm:

func   dijkstra ( graph   * Graph ,   city   string )   { 
      visited   :=   make ( map [ string ] bool ) 
      heap   :=   & Heap {} 

      startNode   :=   graph . GetNode ( city ) 
      startNode . value   =   0 
      heap . Push ( startNode ) 

      for   heap . Size ()   >   0   { 
          current   :=   heap . Pop () 
          visited [ current . name ]   =   true 
          edges   :=   graph . Edges [ current . name ] 
          for   _ ,   edge   :=   range   edges   { 
              if   ! visited [ edge . node . name ]   { 
                  heap . Push ( edge . node ) 
                  if   current . value + edge . weight   <   edge . node . value   { 
                      edge . node . value   =   current . value   + 
                      edge . weight 
                      edge . node . through   =   current 
                  } 
              } 
          } 
      } 
}

Here is how it works. Let’s say you want to find the shortest travel time from London to Istanbul:

1 Set up the weighted graph, a min heap, and a map to mark cities that have been visited before.
2 Get the origin node, London, set its node value to 0, and push it into the heap.
3 The next step is a loop. While the heap is not empty, you pop the heap. This will be your current node.
4 Mark the city as visited.
5 For each city that the current node is connected to (Paris, Luxembourg, and Amsterdam for London), push the city into the heap if it’s not been visited before.
6 Check if the current node’s value plus the edge’s weight is less than the connected node’s (Paris, Luxembourg, and Amsterdam) value.
7 If it is, set the connected node’s value to the current node’s value plus the edge’s weight. This is why you set every node (except the originating node, London) to be MaxInt .
8 Set the connected node’s through field to be the current node. The through field tells you which node the shortest path to this node is, so you can trace back later to come up with the path.
9 Once you’re done, loop from step 3 until all the nodes are visited.

That’s it for the algorithm. Here’s how you can use it:

func   main ()   { 
      //  build  and  run  Dijkstra's  algorithm  on  graph 
      graph   :=   buildGraph () 
      city   :=   os . Args [ 1 ] 
      dijkstra ( graph ,   city ) 

      //  display  the  nodes 
      for   _ ,   node   :=   range   graph . Nodes   { 
          fmt . Printf ( "Shortest  time  from  %s  to  %s  is  %d\n" , 
              city ,   node . name ,   node . value ) 
          for   n   :=   node ;   n . through   !=   nil ;   n   =   n . through   { 
              fmt . Print ( n ,   "  <-  " ) 
          } 
          fmt . Println ( city ) 
          fmt . Println () 
      } 
}

Build the graph and pass it to the algorithm. The results are all in the nodes. After calling the dijkstra function, the values of the nodes are now the shortest flight times needed to travel from London while the through fields are the previous nodes that will link back to the shortest path.

If you take the nodes and walk backward to London, you’ll see the shortest path:

$  %  go  run  dijkstra.go  London
Shortest  time  from  London  to  London  is  0
London

Shortest  time  from  London  to  Paris  is  80
Paris  <-  London

Shortest  time  from  London  to  Amsterdam  is  75
Amsterdam  <-  London

Shortest  time  from  London  to  Luxembourg  is  75
Luxembourg  <-  London

Shortest  time  from  London  to  Zurich  is  135
Zurich  <-  Luxembourg  <-  London

Shortest  time  from  London  to  Rome  is  205
Rome  <-  Paris  <-  London

Shortest  time  from  London  to  Berlin  is  160
Berlin  <-  Amsterdam  <-  London

Shortest  time  from  London  to  Vienna  is  215
Vienna  <-  Zurich  <-  Luxembourg  <-  London

Shortest  time  from  London  to  Warsaw  is  240
Warsaw  <-  Berlin  <-  Amsterdam  <-  London

Shortest  time  from  London  to  Istanbul  is  345
Istanbul  <-  Vienna  <-  Zurich  <-  Luxembourg  <-  London

 

标签:node,algorithm,graph,Finding,London,Graph,Go,nodes,AddEdge
From: https://www.cnblogs.com/zhangzhihui/p/17753394.html

相关文章

  • CF986C AND Graph
    出题人纯nt要用bitset存bool数组来卡空间也真是没谁了这题的思路其实有点像高维前缀和,考虑对于某个数\(x\),我们知道\(y=(2^n-1)\oplusx\)与\(x\)的与一定为\(0\),且\(y\)的所有子集也满足与\(x\)后为\(0\)考虑怎么处理这种子集关系,我们借鉴于高维前缀和,每次把某个数\(y\)的某一......
  • MongoDB常用查询
    1.数据库数据说明#集合:school#文档:stu#结合字段:id,学号、姓名、电话、性别、年龄、学历、备注#初始化20条数据useschoolfor(varnum=1;num<=20;num++){db.stu.insert({id:num,no:"SN"+num,name:"name"+num,tel:"111......
  • MongoDB基础知识
    1.简介MongoDB官方文档菜鸟教程1、NoSQL(NotOnlySQL),不仅仅是SQL,主要是指非关系型数据库,是对不同与传统的关系型数据库的数据管理系统的统称2、NoSQL用于超大规模数据的存储,这些类型的数据存储吧需要固定的模式,无需多余的操作就可以横向扩展1.2NoSQL和RDBMS的区分......
  • Rust cargo常用命令
    目录设置国内镜像创建新项目构建项目运行项目检查项目,但不构建可执行文件运行项目的测试发布项目更新依赖查看项目依赖关系树创建新的库项目文档生成设置国内镜像cd~/.cargo#创建config文件vimconfig#添加如下镜像源[source.crates-io]registry="https://github.com/......
  • Django 数据库--values_list 指定字段取值及 distinct 去重处理
    通过QuerySet会返回model的所有字段,通过obj.field_name即可获取对应字段的数据values():获取某一个或者某几个字段的数据指定字段使用values()指定参数可以仅仅返回相应字段的字典列表,如:name_dict_list=Project.objects.values('name')则name_dict_list= <Q......
  • Google Guava 库用法整理
    参考:(2,3,4)http://blog.publicobject.com更多用法参考http://ajoo.iteye.com/category/119082以前这么用:Java代码Map<String,Map<Long,List<String>>>map=newHashMap<String,Map<Long,List<String>>>();现在这么用(JDK7将实现该功能......
  • Go字符串实战操作大全!
    在本篇文章中,我们深入探讨了Go语言中字符串的魅力和深度。从基础定义、操作、字符编码到复杂的类型转换,每个环节都带有实例和代码示例来深化理解。通过这些深入的解析,读者不仅能够掌握字符串在Go中的核心概念,还能洞察Go设计哲学背后的思考。关注公众号【TechLeadCloud】,分享互......
  • Go函数全景:从基础到高阶的深度探索
    在本篇文章中,我们深入探索了Go语言中的函数特性。从基础的函数定义到特殊函数类型,再到高阶函数的使用和函数调用的优化,每一个部分都揭示了Go的设计哲学和其对编程效率的追求。通过详细的代码示例和专业解析,读者不仅可以掌握函数的核心概念,还能了解如何在实践中有效利用这些特性来......
  • client-go实战之六:时隔两年,刷新版本继续实战
    欢迎访问我的GitHub这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos时隔两年,《client-go实战》被激活,更多内容将会继续更新时间过得真快,《client-go实战》系列已是两年前的作品,近期工作中再次用到client-go时,突然发现自己原创的内容远达不......
  • Go - Creating Graphs
    Problem: Youwanttocreateaweightedgraphdatastructure.Solution: CreatestructsfornodesandedgesandplacetheminaGraphstruct.CreateandattachfunctionstoGraphtocreatenodesandedgesforthegraph. Graphsareverycommonnonlineard......