在前面两篇中我们通过深度优先搜索可以从图中找出一条通过顶点v到顶点w的路径,但是深度优先搜索与顶点的输入有很大的关系,找出来的路径也不一定是最短的,通常情况下我们很多时候需要找出图中的最短路径,比如:地图功能。

 图算法系列之计算图中最短路径(有关图的最短路径算法有) 图算法 路径 顶点 第1张

本文转载自微信公众号「贝塔学JAVA」,作者Silently9527。转载本文请联系贝塔学JAVA公众号。

前言

在前面两篇中我们通过深度优先搜索可以从图中找出一条通过顶点v到顶点w的路径,但是深度优先搜索与顶点的输入有很大的关系,找出来的路径也不一定是最短的,通常情况下我们很多时候需要找出图中的最短路径,比如:地图功能。这里我们就需要使用到广度优先搜索算法

广度优先搜索

依然使用之前定义的寻找路径的API

  1. publicclassPaths{
  2. Paths(Graphgraph,ints);
  3. booleanhasPathTo(intv);//判断出从s->v是否存在路径
  4. Iterable<Integer>pathTo(intv);//如果存在路径,返回路径
  5. }

在广度优先搜索中,为了找出最短路径,我们需要按照起点的顺序来遍历所有的顶点,而不在是使用递归来实现;算法的思路:

  • 使用队列来保存已经被标记过但是邻接表还未被遍历过的顶点
  • 取出队列中的下一个顶点v并标记它
  • 将v相邻的所有未被标记的顶点加入到队列

在该算法中,为了保存路径,我们依然需要使用一个边的数组edgeTo[],用一颗父链树来表示根节点到所有连通顶点的最短路径。

  1. publicclassBreadthFirstPaths{
  2. privatebooleanmarked[];
  3. privateint[]edgeTo;
  4. privateints;
  5. privateQueue<Integer>queue=newLinkedListQueue<>();
  6. publicBreadthFirstPaths(Graphgraph,ints){
  7. this.s=s;
  8. this.marked=newboolean[graph.V()];
  9. this.edgeTo=newint[graph.V()];
  10. bfs(graph,s);
  11. }
  12. privatevoidbfs(Graphgraph,ints){
  13. this.marked[s]=true;
  14. this.queue.enqueue(s);
  15. while(!this.queue.isEmpty()){
  16. Integerv=this.queue.dequeue();
  17. for(intw:graph.adj(v)){
  18. if(!this.marked[w]){
  19. this.marked[w]=true;
  20. this.edgeTo[w]=v;
  21. this.queue.enqueue(w);
  22. }
  23. }
  24. }
  25. }
  26. publicbooleanhasPathTo(intv){
  27. returnthis.marked[v];
  28. }
  29. publicIterable<Integer>pathTo(intv){
  30. if(!hasPathTo(v)){
  31. thrownewIllegalStateException("s不能到达v");
  32. }
  33. Stack<Integer>stack=newLinkedListStack<>();
  34. stack.push(v);
  35. while(edgeTo[v]!=s){
  36. stack.push(edgeTo[v]);
  37. v=edgeTo[v];
  38. }
  39. stack.push(s);
  40. returnstack;
  41. }
  42. }

以下图为列,来看看广度优先搜索的运行轨迹

 图算法系列之计算图中最短路径(有关图的最短路径算法有) 图算法 路径 顶点 第2张

 图算法系列之计算图中最短路径(有关图的最短路径算法有) 图算法 路径 顶点 第3张

单元测试的代码:

  1. @Test
  2. publicvoidtest(){
  3. Graphgraph=newGraph(8);
  4. graph.addEdge(0,1);
  5. graph.addEdge(0,2);
  6. graph.addEdge(0,5);
  7. graph.addEdge(1,3);
  8. graph.addEdge(2,4);
  9. graph.addEdge(4,3);
  10. graph.addEdge(5,3);
  11. graph.addEdge(6,7);
  12. BreadthFirstPathspaths=newBreadthFirstPaths(graph,0);
  13. System.out.println(paths.hasPathTo(5));
  14. System.out.println(paths.hasPathTo(2));
  15. System.out.println(paths.hasPathTo(6));
  16. paths.pathTo(5).forEach(System.out::print);
  17. System.out.println();
  18. paths.pathTo(4).forEach(System.out::print);
  19. System.out.println();
  20. paths.pathTo(2).forEach(System.out::print);
  21. System.out.println();
  22. paths.pathTo(3).forEach(System.out::print);
  23. }

执行的结果与我们分析的运行轨迹一致

 图算法系列之计算图中最短路径(有关图的最短路径算法有) 图算法 路径 顶点 第4张

符号图

最近几篇文章一起学习到的图算法都是以数字作为了顶点,是因为数字来实现这些算法会非常的简洁方便,但是在实际的场景中,通常都是使用的字符作为顶点而非数字,比如:地图上的位置、电影与演员的关系。

为了满足实际的场景,我们只需要在数字与字符串的关系做一个映射,此时我们会想到之前文章实现的map(通过二叉树实现map、红黑树实现map、哈希表实现map等等,有兴趣的同学可以去翻翻),使用Map来维护字符串和数字的映射关系。

  1. publicinterfaceSymbolGraph{
  2. booleancontains(Stringkey);//判断是否存在顶点
  3. intindex(Stringkey);//通过名称返回对应的数字顶点
  4. Stringname(intv);//通过数字顶点返回对应的字符名称
  5. Graphgraph();
  6. }

实现的思路:

  • 使用Map来保存字符串-数字的映射,key为字符串,value为数字
  • 使用一个数组来反向映射数字-字符串,数组的下标对应数字顶点,值对应字符串名称
  • 假设构造图的顶点与边是通过字符串来表示的,如:a,b,c,d\nb,a,h,l,p\ng,s,z 使用\n分隔的每段第一个字符串表示顶点v,后面的表示与顶点v相连的相邻顶点;

实际的过程可以根据具体情况来确定,不一定非得这种字符串,可以来源于数据库,也可以来源于网路的请求。

代码实现如下:

  1. publicclassSymbolGraph{
  2. privateMap<String,Integer>map=newRedBlack23RedBlackTreeMap<>();
  3. privateString[]keys;
  4. privateGraphgraph;
  5. publicSymbolGraph(Stringtext){
  6. Arrays.stream(text.split("\n")).forEach(line->{
  7. String[]split=line.split(",");
  8. for(inti=0;i<split.length;i++){
  9. map.put(split[i],i);
  10. }
  11. });
  12. this.keys=newString[map.size()];
  13. map.keys().forEach(key->{
  14. this.keys[this.map.get(key)]=key;
  15. });
  16. this.graph=newGraph(this.keys.length);
  17. Arrays.stream(text.split("\n")).forEach(line->{
  18. String[]split=line.split(",");
  19. intv=this.map.get(split[0]);
  20. for(inti=1;i<split.length;i++){
  21. this.graph.addEdge(v,this.map.get(split[i]));
  22. }
  23. });
  24. }
  25. publicbooleancontains(Stringkey){
  26. returnmap.contains(key);
  27. }
  28. publicintindex(Stringkey){
  29. returnmap.get(key);
  30. }
  31. publicStringname(intindex){
  32. returnthis.keys[index];
  33. }
  34. publicGraphgraph(){
  35. returnthis.graph;
  36. }
  37. publicstaticvoidmain(String[]args){
  38. System.out.println(Arrays.toString("323\n2323".split("\n")));
  39. }
  40. }

文中所有源码已放入到了github仓库:https://github.com/silently9527/JavaCore

转载请说明出处
知优网 » 图算法系列之计算图中最短路径(有关图的最短路径算法有)

发表评论

您需要后才能发表评论