Java版高级数据结构算法 - 有向图的深度遍历、广度遍历、不带权值最短路径求解

知识的学习在于点滴记录,坚持不懈;知识的学习要有深度和广度,不能只流于表面,坐井观天;知识要善于总结,不仅能够理解,更知道如何表达!

这篇文章主要输出完整的有向图的相关代码,关于无向图和有向图的理论,很多数据结构的书籍都有涉及,网上也有很多的资料,这里就不再赘述了,关于图的主要基本数据结构主要有三种:邻接矩阵、邻接表、十字链表

这篇文章的代码使用Java实现的,一个基于邻接表结构的不带权值有向图的深度优先遍历、广度优先遍历和最短路径求解

代码实现思想是从文件中读取图的顶点和路径信息,文件名称是citys.txt,文件的内容如下:

(1)西安
3, 5
(2)洛阳
4, 6
(3)安徽
1 ,4
(4)北京
2, 7, 8
(5)福建
3, 6, 9
(6)杭州
1, 4, 8
(7)深圳
2, 3, 6
(8)苏州
3, 6, 1
(9)中南海
2, 5

上面每两行分别显示了图的顶点信息(城市名称)和路径信息。

图的顶点类型定义

/**
 * 定义图的顶点类型
 */
static class Vertex{

    public Vertex(String data, LinkedList<Integer> adjList) {
        this.data = data;
        this.adjList = adjList;
    }

    String data; // 邻接表数组的数据
    LinkedList<Integer> adjList; // 邻接表中的链表
}

图的类型定义

public class Digraph {

    // 定义邻接表
    private ArrayList<Vertex> adj;

    /**
     * 初始化邻接表集合
     */
    public Digraph(){
        adj = new ArrayList<>();
    }
}

从文件中读取图的顶点和路径信息创建邻接表

/**
 * 从指定的流里面读取邻接表的数据
 * @param reader
 */
public void read(BufferedReader reader) throws IOException {
    // 给邻接表的第0项添加一个vertex,因为代码上顶点是从1开始编号的
    adj.add(new Vertex("", null));

    String city = null;
    String[] vertex = null;
    for(;;){
        city = reader.readLine();
        if(city == null){
            break;
        }

        LinkedList<Integer> list = new LinkedList<>();
        vertex = reader.readLine().split(",");
        for (int i = 0; i < vertex.length; i++) {
            list.add(Integer.parseInt(vertex[i].trim()));
        }

        adj.add(new Vertex(city, list));
    }
}

图的深度优先遍历DFS

/**
 * 从指定的start顶点开始,实现深度优先搜索
 */
public void dfs(int start){
    // 因为图的每一个顶点都有可能有多条路径到达,因此需要定义一个数组,记录遍历的情况
    boolean[] visited = new boolean[adj.size()];
    dfs(start, visited);
}

/**
 * 递归实现图的深度遍历
 * @param start
 * @param visited
 */
private void dfs(int start, boolean[] visited) {
    System.out.print(adj.get(start).data + " ");
    visited[start] = true;

    LinkedList<Integer> list = adj.get(start).adjList;
    for (int i = 0; i < list.size(); i++) {
        if(!visited[list.get(i)]){
            dfs(list.get(i), visited);
        }
    }
}

图的广度优先遍历BFS

/**
 * 从指定的start顶点开始,实现广度优先搜索
 * @param start
 */
public void bfs(int start){
    boolean[] visited = new boolean[adj.size()];
    // 广度优先搜搜需要借助一个队列完成层层向外扩张的方式进行遍历,类似于树的层序遍历
    LinkedList<Vertex> queue = new LinkedList<>();

    // 把起始的start节点入队列
    queue.offer(adj.get(start));
    visited[start] = true;

    // 出队遍历顶点,并把顶点的邻接节点入队列
    while(!queue.isEmpty()){
        Vertex vt = queue.poll();
        System.out.print(vt.data + " ");

        LinkedList<Integer> list = vt.adjList;
        for (int i = 0; i < list.size(); i++) {
            if(!visited[list.get(i)]){
                queue.offer(adj.get(list.get(i)));
                visited[list.get(i)] = true;
            }
        }
    }
}

最短路径求解

/**
 * 在不带权图中,找一条从start到end的最短路径信息并且打印,借助广度优先搜索,层层向外扩张
 * 需要记录节点的邻接路径
 * @param start
 * @param end
 */
public void findShortestPath(int start, int end){
    int[] path = new int[adj.size()];
    boolean[] visited = new boolean[adj.size()];
    LinkedList<Integer> queue = new LinkedList<>();
    boolean flag = false;

    queue.offer(start);
    visited[start] = true;

    while(!queue.isEmpty()){
        int v = queue.poll();
        if(v == end){
            flag = true;
            break;
        }

        LinkedList<Integer> list = adj.get(v).adjList;
        for (int i = 0; i < list.size(); i++) {
            if(!visited[list.get(i)]){
                queue.offer(list.get(i));
                path[list.get(i)] = v; // 在这里记录顶点的前驱顶点信息
                visited[list.get(i)] = true;
            }
        }
    }

    if(!flag){
        System.out.println(start + "->" + end + " 不存在路径!");
        return;
    }

    // 从path数组中读取正确的顶点最短路径信息
    LinkedList<Integer> stack = new LinkedList<>();
    stack.push(end);
    for (;;){
        int v = path[stack.peek()];
        stack.push(v);
        if(v == start){
            break;
        }
    }

    // 打印最短路径信息
    while(!stack.isEmpty()){
        System.out.print(adj.get(stack.pop()).data);
        if(stack.isEmpty()){
            break;
        }
        System.out.print(" => ");
    }
}

深度优先遍历、广度优先遍历,最短路径代码测试

@Test
public void testdfs() throws IOException {
    Digraph d = new Digraph();
    d.read(new BufferedReader(new FileReader("citys.txt")));
    d.dfs(1);
}

@Test
public void testbfs() throws IOException {
    Digraph d = new Digraph();
    d.read(new BufferedReader(new FileReader("citys.txt")));
    d.bfs(1);
}

@Test
public void testshortestpath() throws IOException {
    Digraph d = new Digraph();
    d.read(new BufferedReader(new FileReader("citys.txt")));
    d.findShortestPath(1, 8);
}

上面三个测试用例的打印如下:

(1)西安 (3)安徽 (4)北京 (2)洛阳 (6)杭州 (8)苏州 (7)深圳 (5)福建 (9)中南海
(1)西安 (3)安徽 (5)福建 (4)北京 (6)杭州 (9)中南海 (2)洛阳 (7)深圳 (8)苏州 
(1)西安 => (3)安徽 => (4)北京 => (8)苏州
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值