Java实现利用广度优先遍历(BFS)计算最短路径的方法

 更新时间:2015年04月20日 15:24:06   作者:司青  
这篇文章主要介绍了Java实现利用广度优先遍历(BFS)计算最短路径的方法,实例分析了广度优先遍历算法的原理与使用技巧,具有一定参考借鉴价值,需要的朋友可以参考下

本文实例讲述了Java实现利用广度优先遍历(BFS)计算最短路径的方法。分享给大家供大家参考。具体分析如下:

我们用字符串代表图的顶点(vertax),来模拟学校中Classroom, Square, Toilet, Canteen, South Gate, North Gate几个地点,然后计算任意两点之间的最短路径。

如下图所示:

如,我想从North Gate去Canteen, 程序的输出结果应为:

BFS: From [North Gate] to [Canteen]:
North Gate
Square
Canteen

首先定义一个算法接口Algorithm:

public interface Algorithm {
  /**
   * 执行算法
   */
  void perform(Graph g, String sourceVertex);
  /**
   * 得到路径
   */
  Map<String, String> getPath();
}

然后,定义图:

/**
 * (无向)图
 */
public class Graph {
  // 图的起点
  private String firstVertax;
  // 邻接表
  private Map<String, List<String>> adj = new HashMap<>();
  // 遍历算法
  private Algorithm algorithm;
  public Graph(Algorithm algorithm) {
    this.algorithm = algorithm;
  }
  /**
   * 执行算法
   */
  public void done() {
    algorithm.perform(this, firstVertax);
  }
  /**
   * 得到从起点到{@code vertex}点的最短路径
   * @param vertex
   * @return
   */
  public Stack<String> findPathTo(String vertex) {
    Stack<String> stack = new Stack<>();
    stack.add(vertex);
    Map<String, String> path = algorithm.getPath();
    for (String location = path.get(vertex) ; false == location.equals(firstVertax) ; location = path.get(location)) {
      stack.push(location);
    }
    stack.push(firstVertax);
    return stack;
  }
  /**
   * 添加一条边
   */
  public void addEdge(String fromVertex, String toVertex) {
    if (firstVertax == null) {
      firstVertax = fromVertex;
    }
    adj.get(fromVertex).add(toVertex);
    adj.get(toVertex).add(fromVertex);
  }
  /**
   * 添加一个顶点
   */
  public void addVertex(String vertex) {
    adj.put(vertex, new ArrayList<>());
  }
  public Map<String, List<String>> getAdj() {
    return adj;
  }
}

这里我们使用策略设计模式,将算法与Graph类分离,通过在构造Graph对象时传入一个Algorithm接口的实现来为Graph选择遍历算法。

public Graph(Algorithm algorithm) {
    this.algorithm = algorithm;
  }

无向图的存储结构为邻接表,这里用一个Map表示邻接表,map的key是学校地点(String),value是一个与该地点相连通的地点表(List<String>)。

// 邻接表
  private Map<String, List<String>> adj = new HashMap<>();

然后,编写Algorithm接口的BFS实现:

/**
 * 封装BFS算法
 */
public class BroadFristSearchAlgorithm implements Algorithm {
  // 保存已经访问过的地点
  private List<String> visitedVertex;
  // 保存最短路径
  private Map<String, String> path;
  @Override
  public void perform(Graph g, String sourceVertex) {
    if (null == visitedVertex) {
      visitedVertex = new ArrayList<>();
    }
    if (null == path) {
      path = new HashMap<>();
    }
    BFS(g, sourceVertex);
  }
  @Override
  public Map<String, String> getPath() {
    return path;
  }
  private void BFS(Graph g, String sourceVertex) {
    Queue<String> queue = new LinkedList<>();
    // 标记起点
    visitedVertex.add(sourceVertex);
    // 起点入列
    queue.add(sourceVertex);
    while (false == queue.isEmpty()) {
      String ver = queue.poll();
      List<String> toBeVisitedVertex = g.getAdj().get(ver);
      for (String v : toBeVisitedVertex) {
        if (false == visitedVertex.contains(v)) {
          visitedVertex.add(v);
          path.put(v, ver);
          queue.add(v);
        }
      }
    }
  }
}

其中,path是Map类型,意为从 value 到 key 的一条路径。

BFS算法描述:

1. 将起点标记为已访问并放入队列。
2. 从队列中取出一个顶点,得到与该顶点相通的所有顶点。
3. 遍历这些顶点,先判断顶点是否已被访问过,如果否,标记该点为已访问,记录当前路径,并将当前顶点入列。
4. 重复2、3,直到队列为空。

测试用例:

String[] vertex = {"North Gate", "South Gate", "Classroom", "Square", "Toilet", "Canteen"};
  Edge[] edges = {
      new Edge("North Gate", "Classroom"),
      new Edge("North Gate", "Square"),
      new Edge("Classroom", "Toilet"),
      new Edge("Square", "Toilet"),
      new Edge("Square", "Canteen"),
      new Edge("Toilet", "South Gate"),
      new Edge("Toilet", "South Gate"),
  };
@Test
  public void testBFS() {
    Graph g = new Graph(new BroadFristSearchAlgorithm());
    addVertex(g);
    addEdge(g);
    g.done();
    Stack<String> result = g.findPathTo("Canteen");
    System.out.println("BFS: From [North Gate] to [Canteen]:");
    while (!result.isEmpty()) {
      System.out.println(result.pop());
    }
  }

希望本文所述对大家的java程序设计有所帮助。

相关文章

  • 用GUI实现java版贪吃蛇小游戏

    用GUI实现java版贪吃蛇小游戏

    这篇文章主要为大家详细介绍了用GUI实现java版贪吃蛇小游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-04-04
  • Spring MVC文件上传大小和类型限制以及超大文件上传bug问题

    Spring MVC文件上传大小和类型限制以及超大文件上传bug问题

    这篇文章主要介绍了Spring MVC文件上传大小和类型限制以及超大文件上传bug问题,非常具有实用价值,需要的朋友可以参考下
    2017-10-10
  • Java创建和启动线程的两种方式实例分析

    Java创建和启动线程的两种方式实例分析

    这篇文章主要介绍了Java创建和启动线程的两种方式,结合实例形式分析了java多线程创建、使用相关操作技巧与注意事项,需要的朋友可以参考下
    2019-09-09
  • 一文带你弄懂Java中线程池的原理

    一文带你弄懂Java中线程池的原理

    工作中,我们经常使用线程池,但是你真的了解线程池的原理吗?同时,线程池工作原理和底层实现原理也是面试经常问的考题,所以,今天我们一起聊聊线程池的原理吧
    2022-12-12
  • 详细分析java 动态代理

    详细分析java 动态代理

    这篇文章主要介绍了java 动态代理的的相关资料,文中讲解非常细致,代码帮助大家更好的理解和学习,感兴趣的朋友可以了解下
    2020-06-06
  • Java并发编程中的ReentrantLock类详解

    Java并发编程中的ReentrantLock类详解

    这篇文章主要介绍了Java并发编程中的ReentrantLock类详解,ReentrantLock是juc.locks包中的一个独占式可重入锁,相比synchronized,它可以创建多个条件等待队列,还支持公平/非公平锁、可中断、超时、轮询等特性,需要的朋友可以参考下
    2023-12-12
  • JAVA 注解详解及简单实例

    JAVA 注解详解及简单实例

    这篇文章主要介绍了JAVA 注解详解及简单实例的相关资料,需要的朋友可以参考下
    2017-05-05
  • 通过Session案例分析一次性验证码登录

    通过Session案例分析一次性验证码登录

    这篇文章主要介绍了通过Session案例分析一次性验证码登录,非常不错,具有参考借鉴价值,需要的朋友可以参考下
    2017-03-03
  • Spring AI + 混元带你实现企业级稳定可部署的AI业务智能体

    Spring AI + 混元带你实现企业级稳定可部署的AI业务智能体

    我们深入探讨了Spring AI在智能体构建中的实际应用,特别是在企业环境中的价值与效能,通过逐步实现一个本地部署的智能体解决方案,我们不仅展示了Spring AI的灵活性与易用性,还强调了它在推动AI技术与业务深度融合方面的潜力,感兴趣的朋友一起看看吧
    2024-11-11
  • java实现分布式锁的常用三种方式

    java实现分布式锁的常用三种方式

    本文主要介绍了java实现分布式锁,一般有这3种方式,基于数据库实现的分布式锁、基于Redis实现的分布式锁和基于Zookeeper实现的分布式锁,具有一定的参考价值,感兴趣的可以了解一下
    2024-08-08

最新评论