写给新手看的dfs和bfs

图的介绍
图(graph)是一种数据结构,它表明了物件与物件之间的“多对多”的一种复杂关系。其中结点可以具有零个或多个相邻元素。两个结点之间的连接称为边(edge)。 结点也可以称为顶点(vertex)。图分为无向图和有向图,以及带权图,如下图:

无向图: 顶点之间的连接没有方向,比如A-B,即可以是 A-> B 也可以 B->A .
有向图: 顶点之间的连接有方向,比如A-B,只能是 A-> B 不能是 B->A .
带权图:即边带权值的图,也叫网.

图的存储方式

图的表示方式有两种:邻接矩阵和邻接表
邻接矩阵
邻接矩阵是表示图形中顶点之间相邻关系的矩阵,用二维数组实现,如图:
在这里插入图片描述
邻接表
邻接矩阵需要为每个顶点都分配n个边的空间,但有很多边都是不存在,会造成空间的一定损失.而邻接表的实现只关心存在的边,不关心不存在的边。因此没有空间浪费,邻接表由数组+链表组成,如图:
在这里插入图片描述
图的遍历
图的遍历,即是对结点的访问。一个图有那么多个结点,如何遍历这些结点,我们采取图的遍历方式有两种:深度优先遍历和广度优先遍历

图的深度优先搜索(Depth First Search)
深度优先遍历,从初始访问结点出发,初始访问结点可能有多个邻接结点,深度优先遍历的策略就是首先访问第一个邻接结点,然后再以这个被访问的邻接结点作为初始结点,访问它的第一个邻接结点, 可以这样理解:每次都在访问完当前结点后首先访问当前结点的第一个邻接结点。

深度优先遍历步骤
①访问初始结点v,并标记结点v为已访问。
②查找结点v的第一个邻接结点w。
③若w存在,则继续执行④,如果w不存在,则回到第①步,将从v的下一个结点继续。
④若w未被访问,对w进行深度优先遍历递归(即把w当做另一个v,然后进行步骤①②③)。
⑤查找结点v的w邻接结点的下一个邻接结点,转到步骤③。

dfs例子:
要求:对下图进行深度优先搜索, 从A 开始遍历.
在这里插入图片描述
代码实现:

mport java.util.ArrayList;
import java.util.Arrays;

public class Graph {

    private ArrayList<String>vertexList;//存储顶点集合
    private int[][]edges;//存储图对应的邻接矩阵
    private int edgesNum;//边的数目
    private boolean[] isVisited;//标记是否走过

    public static void main(String[] args) {
       int n=5;//节点个数
        String Vertexs[]={"A","B","C","D","E"};
       //创建图对象
        Graph graph=new Graph(n);
        for(String vertex:Vertexs){
            graph.insertVertex(vertex);
        }
            //添加边
        graph.insertEdge(0,1,1);
        graph.insertEdge(0,2,1);
        graph.insertEdge(1,2,1);
        graph.insertEdge(1,3,1);
        graph.insertEdge(1,4,1);

        //显示邻接矩阵
        graph.showGraph();

        //深度优先遍历
        graph.dfs();

    }

    //构造器
    public Graph(int n){
       //初始化矩阵和vertexList
        edges=new int[n][n];
        vertexList=new ArrayList<String>(n);
        edgesNum=0;
    }

    //得到第一个邻接节点的下标w
    public int getFirst(int index){
        for(int j=0;j<vertexList.size();j++){
            if(edges[index][j]>0){
                return j;
            }
        }
        return -1;
    }
    //根据前一个邻接节点的下标来获取下一个邻接节点
    public int getNext(int v1,int v2){
        for(int j=v2+1;j<vertexList.size();j++){
            if(edges[v1][j]>0){
                return j;
            }
        }
        return -1;
    }
    //dfs,i第一次是0
    private void dfs(boolean[] isVisited, int i) {
        //先访问该节点并输出
        System.out.print(getValue(i)+"->");
        //将节点设置为已访问
        isVisited[i]=true;
        //查找节点i上午第一个邻接节点
        int w=getFirst(i);
        while (w!=-1){//找到
            if(!isVisited[w]){
                dfs(isVisited,w);
            }
            //如果w已经被访问过
            w=getNext(i,w);
        }
    }

    //dfs重载
    public void dfs(){
        isVisited=new boolean[vertexList.size()];
        //遍历所有的节点,进行dfs
        for(int i=0;i<getVertexNum();i++){
            if(!isVisited[i]){
                dfs(isVisited,i);
            }
        }
    }



    //返回节点个数
    public int getVertexNum(){
        return vertexList.size();
    }
    //显示图对应的矩阵
    public void showGraph(){
        for(int[]link:edges){
            System.out.println(Arrays.toString(link));
        }
    }
    //得到边的数目
    public int getEdgesNum(){
        return edgesNum;
    }
    //返回节点i(下标)对应的数据
    public String getValue(int i){
        return vertexList.get(i);
    }
    //返回v1和v2的权值
    public int getWeight(int v1,int v2){
        return edges[v1][v2];
    }
    //插入节点
    public void insertVertex(String vertex){
        vertexList.add(vertex);
    }


    /**
     * 添加边
     * @param v1 表示点的下标
     * @param v2 表示第二个顶点对应的下标
     * @param weight 表示权值
     */
    public void insertEdge(int v1,int v2,int weight){
        edges[v1][v2]=weight;
        edges[v2][v1]=weight;
        edgesNum++;
    }
}

广度优先遍历(Board First Search)
图的广度优先搜索类似于一个分层搜索的过程,广度优先遍历需要使用一个队列以保持访问过的结点的顺序,以便按这个顺序来访问这些结点的邻接结点

广度优先遍历步骤
1访问初始结点v并标记结点v为已访问。
2结点v入队列
3当队列非空时,继续执行,否则算法结束。
4出队列,取得队头结点u。
5查找结点u的第一个邻接结点w。
6若结点u的邻接结点w不存在,则转到步骤3;否则循环执行以下三个步骤:
6.1 若结点w尚未被访问,则访问结点w并标记为已访问。
6.2 结点w入队列
6.3 查找结点u的继w邻接结点后的下一个邻接结点w,转到步骤6。

bfs例子:
要求:对下图进行深度优先搜索, 从A 开始遍历.
在这里插入图片描述
代码实现:

package cn.gb.demo06;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;

public class Graph {

    private ArrayList<String>vertexList;//存储顶点集合
    private int[][]edges;//存储图对应的邻接矩阵
    private int edgesNum;//边的数目
    private boolean[] isVisited;//标记是否走过

    public static void main(String[] args) {
       int n=5;//节点个数
        String Vertexs[]={"A","B","C","D","E"};
       //创建图对象
        Graph graph=new Graph(n);
        for(String vertex:Vertexs){
            graph.insertVertex(vertex);
        }
            //添加边
        graph.insertEdge(0,1,1);
        graph.insertEdge(0,2,1);
        graph.insertEdge(1,2,1);
        graph.insertEdge(1,3,1);
        graph.insertEdge(1,4,1);

        //显示邻接矩阵
        graph.showGraph();

        //深度优先遍历
        graph.bfs();

    }

    //构造器
    public Graph(int n){
       //初始化矩阵和vertexList
        edges=new int[n][n];
        vertexList=new ArrayList<String>(n);
        edgesNum=0;
    }

    //得到第一个邻接节点的下标w
    public int getFirst(int index){
        for(int j=0;j<vertexList.size();j++){
            if(edges[index][j]>0){
                return j;
            }
        }
        return -1;
    }
    //根据前一个邻接节点的下标来获取下一个邻接节点
    public int getNext(int v1,int v2){
        for(int j=v2+1;j<vertexList.size();j++){
            if(edges[v1][j]>0){
                return j;
            }
        }
        return -1;
    }

    //对一个节点进行bfs的方法
    private void bfs(boolean[]isVisited,int i){
        int u;//表示队列的头结点对应下标
        int w;//邻接节点w
        //队列,记录访问的顺序
        LinkedList queue=new LinkedList();
        //访问节点,输出节点信息
        System.out.println(getValue(i));
        //标记已访问
        isVisited[i]=true;
        //将节点加入队列
        queue.addLast(i);
        while (!queue.isEmpty()){
            //取出队头的节点下标
            u=(Integer)queue.removeFirst();
            //得到第一个邻接节点的下标W
            w=getFirst(u);
            while(w!=-1){
                if(!isVisited[w]){
                    System.out.print(getValue(w));
                    isVisited[w]=true;
                    queue.addLast(w);
                }
                //以u为前驱节点,找W后面的下一个节点
                w=getNext(u,w);
            }
        }
    }
    //
    public  void bfs(){
        isVisited=new boolean[vertexList.size()];
        for(int i=0;i<getVertexNum();i++){
            if(!isVisited[i]){
                bfs(isVisited,i);
            }
        }
    }



    //返回节点个数
    public int getVertexNum(){
        return vertexList.size();
    }
    //显示图对应的矩阵
    public void showGraph(){
        for(int[]link:edges){
            System.out.println(Arrays.toString(link));
        }
    }
    //得到边的数目
    public int getEdgesNum(){
        return edgesNum;
    }
    //返回节点i(下标)对应的数据
    public String getValue(int i){
        return vertexList.get(i);
    }
    //返回v1和v2的权值
    public int getWeight(int v1,int v2){
        return edges[v1][v2];
    }
    //插入节点
    public void insertVertex(String vertex){
        vertexList.add(vertex);
    }


    /**
     * 添加边
     * @param v1 表示点的下标
     * @param v2 表示第二个顶点对应的下标
     * @param weight 表示权值
     */
    public void insertEdge(int v1,int v2,int weight){
        edges[v1][v2]=weight;
        edges[v2][v1]=weight;
        edgesNum++;
    }
}


发布了60 篇原创文章 · 获赞 135 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/weixin_42369886/article/details/105097044