数据结构学习笔记(七):图的深度优先和广度优先算法

数据结构学习笔记(七):图的深度优先和广度优先算法

一、概述

​ 图是一种数据结构,其中结点可以具有0个或者多个相邻的元素,两个结点之间的连接称为边。结点也可以称为顶点。

数据结构学习笔记(七):图的深度优先和广度优先算法_第1张图片
数据结构学习笔记(七):图的深度优先和广度优先算法_第2张图片
二、图的表示方法

  1. 邻接矩阵

数据结构学习笔记(七):图的深度优先和广度优先算法_第3张图片

邻接矩阵中,1表示两个结点相连通,0表示两个结点不连通。如图中0和1,2,3连通,故矩阵中(0,1)(0,2)(0,3)为1,其余不相通的位置为0。
  1. 邻接表

数据结构学习笔记(七):图的深度优先和广度优先算法_第4张图片

三、创建图的邻接矩阵

​ 实现代码:

public class Graph {
    private int numsOfEdges;
    private ArrayList<String> vertexList;
    private int[][] edges;

    public Graph(int n) {
        vertexList = new ArrayList<String>(n);
        edges = new int[n][n];
    }

    //加入结点
    public void insert(String str){
        vertexList.add(str);
    }

    //此方法用于将两个结点连通
    //a,b表示相通的两个结点,weight为权重
    public void setEdges(int a,int b,int weight){
        edges[a][b] = weight;
        edges[b][a] = weight;
        numsOfEdges++;
    }

    //获取结点的个数
    public int getNumsOfVertex(){
        return vertexList.size();
    }

    //得到边的数目
    public int getNumsOfEdges(){
        return numsOfEdges;
    }

    //返回结点i对应的数据
    public String getValueByIndex(int index){
        return vertexList.get(index);
    }

    //返回对应边的权值
    public int getWeight(int a,int b){
        return edges[a][b];
    }

    //返回图对应的矩阵
    public void showGraph(){
        for (int[] arr:edges){
            System.err.println(Arrays.toString(arr));
        }
    }
}

四、图的遍历

​ 图的遍历,即是对结点的访问。一个图有那么多结点,如何遍历这些结点需要特定策略,一般有两种访问策略:深度优先遍历和广度优先遍历。

深度优先

​ 深度优先遍历是从初始访问结点出发,初始访问结点可能有多个邻接结点,深度优先遍历的策略即使首先访问第一个邻接结点,然后再以这个被访问的邻接结点作为初始结点,访问它的第一个邻接结点。可以理解成:每次都在访问当前结点后首先访问当前结点的第一个邻接结点

​ 这样的访问策略是优先往纵向挖掘深入,而不是对一个结点的所有邻接结点进行横向访问,适合使用栈的数据结构实现。

​ 算法步骤:

  1. 访问初始结点A,并将A压入栈中,标记结点A为已访问;

数据结构学习笔记(七):图的深度优先和广度优先算法_第5张图片

  1. 查找结点A的邻接结点,有B,G,D三个都未被访问过的结点,按照字母顺序选择B,将B压入栈中,标记结点B为已访问;

数据结构学习笔记(七):图的深度优先和广度优先算法_第6张图片

3 .以此类推,将E,G压入栈中,输出并标记为已访问;

数据结构学习笔记(七):图的深度优先和广度优先算法_第7张图片

  1. 此时G连接的结点都是已经访问过了,于是将G弹栈,再判断栈顶元素连接的结点是否有未访问的结点;
  2. 一直进行弹栈操作,直到栈顶结点为B,接着访问结点F,重复上述步骤,直到栈为空。

数据结构学习笔记(七):图的深度优先和广度优先算法_第8张图片

数据结构学习笔记(七):图的深度优先和广度优先算法_第9张图片
实现代码:

public class DFS {
    Graph garph;
    boolean[] flag ;

    public DFS(Graph garph) {
        this.garph = garph;
        flag = new boolean[this.garph.getNumsOfVertex()];
    }
    
    //得到邻接结点的下标
    public int getAdjace(int index) {
        int numsOfVertex = garph.getNumsOfVertex();
        for (int i = 0; i < numsOfVertex; i++) {
            if (garph.getEdges(index, i) > 0 && flag[i] == false) {
                return i;
            }
        }
        return -1;
    }

    //深度优先遍历算法
    public void dfs(int index) {
        Stack<String> vertexStack = new Stack<>();
        vertexStack.push(garph.getValueByIndex(index));
        System.out.println(garph.getValueByIndex(index));
        flag[index] = true;
        while (!vertexStack.isEmpty()) {
            int v = getAdjace(garph.getIndexByValue(vertexStack.peek()));
            if (v == -1){
                vertexStack.pop();
            }else {
                flag[v] = true;
                vertexStack.push(garph.getValueByIndex(v));
                System.out.println(garph.getValueByIndex(v));
            }
        }
        for(int j = 0;j < garph.getNumsOfVertex();j++){
            flag[j] = false;
        }
    }
}

广度优先

​ 广度优先搜索一般用来解决最短路径问题,广度优先的搜索是从起点开始,一层一层的进行,每层当中的点距离起始点的步数都是相同的。

​ 广度优先遍历需要借用的数据结构是队列,因为其具有先进先出的特点。

​ 算法步骤:

  1. 选定顶点A,压入队列,标记为访问过。

数据结构学习笔记(七):图的深度优先和广度优先算法_第10张图片

  1. 将与A相连的尚未被访问过的点按照字母大小压入队列,并标记为已访问,此时将A结点输出并打印。

数据结构学习笔记(七):图的深度优先和广度优先算法_第11张图片

  1. 将与B相连的尚未被访问过的结点E,F压入结点,标记为已访问。再从结点头取出结点B打印输出。

数据结构学习笔记(七):图的深度优先和广度优先算法_第12张图片
4. 发现与D相连的结点都被访问过了,因此不需要再压入队列,接着从队列头取出D,打印输出。

数据结构学习笔记(七):图的深度优先和广度优先算法_第13张图片

  1. 从队列头取出结点G,E,与D同理。然后将C压入队列,标记为已访问,取出F打印输出。

数据结构学习笔记(七):图的深度优先和广度优先算法_第14张图片
6. 压入H,标记为已访问,取出C打印输出,判断H没有下一个未访问过的邻接结点,打印输出。此时队列为空,搜索结束。

简单总结一下步骤就是,每次先将队列头节点的未访问过的所有邻接结点压入队列,再取出当前头节点输出。

实现代码:

public class BFS {
    Graph graph;
    boolean[] flag;

    public BFS(Graph graph) {
        this.graph = graph;
        flag = new boolean[graph.getNumsOfVertex()];
    }

    public int getAdjace(int index) {
        int numsOfVertex = graph.getNumsOfVertex();
        for (int i = 0; i < numsOfVertex; i++) {
            if (graph.getEdges(index, i) > 0 && flag[i] == false) {
                return i;
            }
        }
        return -1;
    }

    public void bfs(int index){
        ArrayList<String> vertexlist = new ArrayList<>();
        vertexlist.add(graph.getValueByIndex(index));
        flag[index] = true;
        while (!vertexlist.isEmpty()){
            int v = getAdjace(graph.getIndexByValue(vertexlist.get(0)));
            while (v != -1){
                vertexlist.add(graph.getValueByIndex(v));
                flag[v] = true;
                v = getAdjace(graph.getIndexByValue(vertexlist.get(0)));
            }
            String str = vertexlist.remove(0);
            System.out.println(str);
        }
    }
}

你可能感兴趣的:(数据结构与算法)