图的遍历

二、图的遍历

1.深度优先DFS(使用递归)

2.广度优先BFS(使用队列)

3.DFS与BFS的比较

注:在邻接表与邻接矩阵的基础上实现DFS/BFS差距不大,这里都用邻接矩阵
核心代码:

	//得到结点i的第一个邻接点
    public int getFirstneighbor(int i){
        for (int j=0;j<vertexNum;j++){
            if (isVisited[j]==false && edgeMatrix[i][j]!=0){
                return j;
            }
        }
        return -1;
    }

	//得到结点i的下一个邻接点  j为i的上一个邻接点
    public int getNextNeighbor(int i,int j){

        for(int k=j+1; k<vertexNum; k++){
            if (isVisited[k]==false && edgeMatrix[i][k]!=0){
                return k;
            }
        }
        return -1;
    }

1.深度优先(使用递归)

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

 public void dfs(){
        isVisited = new boolean[vertexNum];
        //无向图中 for循环的目的 是为了遍历非连通图   如果不是非连通图  一次循环就能遍历完毕
        for (int i=0;i<vertexNum;i++){
            if (isVisited[i]==false){
                dfs(isVisited,i);
            }
        }
        System.out.println();
    }

    public void dfs(boolean[] isVisited,int i  ){
        System.out.print(vertexList.get(i)+"=>");
        isVisited[i] = true;

        int j = getFirstneighbor(i);
        while (j!=-1){
            dfs(isVisited,j);
            j=getNextNeighbor(i,j);
        }

    }

1.广度优先(使用队列)

广度优先算法步骤
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。

核心代码如下

  public void bfs(){
        isVisited = new boolean[vertexNum];
        for (int i=0;i<vertexNum;i++){
            if(isVisited[i]==false){
                bfs(isVisited,i);
            }
        }
        System.out.println();
    }

    public void bfs(boolean[] isVisited,int i){
        int u;//头结点下标
        int w;//邻接结点下标
        System.out.print(vertexList.get(i)+"->");
        isVisited[i] = true;
        LinkedList<Integer> queue = new LinkedList<>();
        queue.addLast(i);

        while (!queue.isEmpty()){
            //取出队列头
            u=queue.removeFirst();
            w=getFirstneighbor(u);
            while (w!=-1){
                System.out.print(vertexList.get(w)+"->");
                isVisited[w] = true;
                queue.addLast(w);

                w=getNextNeighbor(u,w);
            }

        }

    }

对于如下图(加深理解作用)
在这里插入图片描述
DFS结果:A->B->C->G->D->F->H->P->E->I->J->K->N->O->L->M

BFS结果:A=>B=>C=>D=>E=>F=>G=>P=>I=>H=>J=>K=>L=>M=>N=>O

问:DFS中:当遍历到G点之后,下一个要遍历的是D F H中的哪一个?
答:遍历的是D,因为在创建图的时候是按照字母的顺序进行添加的。我们写的算法getFirstNeighbor()中,也是按照添加的顺序进行扫描的。(以上回答是基于邻接矩阵存储方式的,如果是邻接表,则情况不同(逆序)

在使用邻接表与邻接矩阵对同一个图进行DFS与BFS时,输出的顺序会不同:
在邻接矩阵中,对同一层的遍历是按照顶点数组的顺序进行的。
在邻接表中,为了减小时间复杂度,存储图时,我们都是使用的头插法添加结点,导致链表的结点顺序为数组中的逆序。这就会导致同层遍历时的顺序与邻接矩阵不一致。


下面贴完整代码(邻接矩阵方式):

package matrix;

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

public class Graph2 {
    ArrayList<Vertex> vertexList;
    int[][] edgeMatrix; //初始化后都为0
    int vertexNum;      //顶点数量
    int edgeNum;        //边的数量
    boolean[]   isVisited;

    public Graph2(int vertexNum) {
        this.vertexNum = vertexNum;
        vertexList = new ArrayList<>(vertexNum);
        edgeMatrix = new int [vertexNum][vertexNum];
    }

    /**
     * 插入顶点
     * @param vertex
     */
    public void insertVertex(Vertex vertex){
        vertexList.add(vertex);
        //this.vertexNum++;
    }

    /**\
     * 添加边
     * @param v1 第一个顶点的下标
     * @param v2    第二个顶点的下标
     * @param weight    边的权值
     */
    public void insertEdge(int v1,int v2,int weight){
        edgeMatrix[v1][v2] =weight;
        edgeMatrix[v2][v1] =weight; //无向图就加上这一句
        edgeNum++;
    }
    //图中常用的方法
    //返回结点的个数
    public int getNumOfVertex() {
        return vertexNum;
    }
    //显示图对应的矩阵
    public void showGraph() {
        System.out.println("  " +vertexList);
        int i=0;
        for(int[] link : edgeMatrix) {
            System.out.println(vertexList.get(i)+" "+Arrays.toString(link));
            i++;
        }
    }
    //得到边的数目
    public int getNumOfEdges() {
        return edgeNum;
    }
    //返回结点i(下标)对应的数据 0->"A" 1->"B" 2->"C"
    public Vertex getValueByIndex(int i) {
        return vertexList.get(i);
    }
    //返回v1和v2的权值
    public int getWeight(int v1, int v2) {
        return edgeMatrix[v1][v2];
    }



    //=============================DFS=================================
    public void dfs(){
        isVisited = new boolean[vertexNum];
        //无向图中 for循环的目的 是为了遍历非连通图   如果没有孤立点  一次循环就能遍历完毕
        for (int i=0;i<vertexNum;i++){
            if (isVisited[i]==false){
                dfs(isVisited,i);
            }
        }
        System.out.println();
    }

    public void dfs(boolean[] isVisited,int i  ){
        System.out.print(vertexList.get(i)+"=>");
        isVisited[i] = true;

        int j = getFirstneighbor(i);
        while (j!=-1){
            dfs(isVisited,j);
            j=getNextNeighbor(i,j);
        }

    }

    public void bfs(){
        isVisited = new boolean[vertexNum];
        for (int i=0;i<vertexNum;i++){
            if(isVisited[i]==false){
                bfs(isVisited,i);
            }
        }
        System.out.println();
    }

    public void bfs(boolean[] isVisited,int i){
        int u;//头结点下标
        int w;//邻接结点下标
        System.out.print(vertexList.get(i)+"->");
        isVisited[i] = true;
        LinkedList<Integer> queue = new LinkedList<>();
        queue.addLast(i);

        while (!queue.isEmpty()){
            //取出队列头
            u=queue.removeFirst();
            w=getFirstneighbor(u);
            while (w!=-1){
                System.out.print(vertexList.get(w)+"->");
                isVisited[w] = true;
                queue.addLast(w);

                w=getNextNeighbor(u,w);
            }

        }

    }

    public int getFirstneighbor(int i){
        for (int j=0;j<vertexNum;j++){
            if (isVisited[j]==false && edgeMatrix[i][j]!=0){
                return j;
            }
        }
        return -1;
    }

    public int getNextNeighbor(int i,int j){

        for(int k=j+1; k<vertexNum; k++){
            if (isVisited[k]==false && edgeMatrix[i][k]!=0){
                return k;
            }
        }
        return -1;
    }








    public static void main(String[] args) {
        Graph2 g = new Graph2(8);
        g.insertVertex(new Vertex("A"));
        g.insertVertex(new Vertex("B"));
        g.insertVertex(new Vertex("C"));
        g.insertVertex(new Vertex("D"));
        g.insertVertex(new Vertex("E"));
        g.insertVertex(new Vertex("F"));
        g.insertVertex(new Vertex("G"));
        g.insertVertex(new Vertex("H"));

        g.insertEdge(0, 1, 1);//A-B
        g.insertEdge(0, 2, 1);//A-C
        g.insertEdge(0, 3, 1);//A-D
        g.insertEdge(0, 4, 1);//A-E
        g.insertEdge(1, 2, 1);//B-C
        g.insertEdge(1, 5, 1);//B-F
        g.insertEdge(2, 6, 1);//C-G
        g.insertEdge(3, 6, 1);//D-G
        g.insertEdge(5, 6, 1);//F-G
        g.insertEdge(7, 6, 1);//H-G

        g.showGraph();
        System.out.println("DFS:");
        g.dfs();
        System.out.println("BFS:");
        g.bfs();
    }


}

/**
 * 顶点类
 */
class Vertex {
    String vertexName;

    public Vertex(String vertexName) {
        this.vertexName = vertexName;
    }

    @Override
    public String toString() {
        return  vertexName ;
    }
}

邻接表:

package edge;

import java.util.Scanner;

/**
 * 数组加链表形式  数组是vertexList ,用ArrayList来代替
 * 邻接链表使用顶点所在索引(当作边的对象)来构建图
 *
 * @author King
 */
public class CreateGraph {

    /**
     * 根据顶点信息String,返回边的对象
     *
     * @param graph 图
     * @param str   顶点名称
     * @return 返回的是边对象的标签
     */
    public int vtoe(Graph graph, String str) {
        for (int i = 0; i < graph.vertexNum; i++) {
            if (graph.vertexList.get(i).vertexName.equals(str)) {
                return i;
            }
        }
        return -1;
    }

    /**
     * 该函数用于图的初始化的实现
     *
     * @param graph 图
     */
    public void initialGraph(Graph graph) {
        @SuppressWarnings("resource")
        Scanner scan = new Scanner(System.in);
        System.out.println("请输入顶点数和边数:");
        graph.vertexNum = scan.nextInt();
        graph.edgeNum = scan.nextInt();

        System.out.println("请依次输入定点名称:");
        for (int i = 0; i < graph.vertexNum; i++) {
            Vertex vertex = new Vertex();
            String name = scan.next();
            vertex.vertexName = name;
            vertex.firstEdge = null;
            graph.vertexList.add(vertex);
        }

        System.out.println("请依次输入每个边:");
        for (int i = 0; i < graph.edgeNum; i++) {
            String preV = scan.next();
            String folV = scan.next();
            int v1 = vtoe(graph, preV);
            int v2 = vtoe(graph, folV);
            if (v1 == -1 || v2 == -1)
                System.out.println("输入顶点数据错误!");

//下面代码是图构建的核心:链表操作
            Edge edge1 = new Edge();
            edge1.edgeName = v2;      //当第一次输入v1 v2时 v2=1 因为List数组从0开始
            //使用头插法插入顶点V1的链表中
            edge1.next = graph.vertexList.get(v1).firstEdge;
            graph.vertexList.get(v1).firstEdge = edge1;

//          下面代码加上便是构建无向图,不加便是构建有向图
            Edge edge2=new Edge();
            edge2.edgeName=v1;
            edge2.next=graph.vertexList.get(v2).firstEdge;
            graph.vertexList.get(v2).firstEdge=edge2;
        }
    }

    /**
     * 输出图的邻接链表表示
     *
     * @param graph 图
     */
    public void outputGraph(Graph graph) {
        Edge edge = new Edge();
        for (int i = 0; i < graph.vertexNum; i++) {
            System.out.print(graph.vertexList.get(i).vertexName);

            edge = graph.vertexList.get(i).firstEdge;
            while (edge != null) {
                System.out.print("-->" + graph.vertexList.get(edge.edgeName).vertexName);
                edge = edge.next;
            }
            System.out.println();
        }
    }



    public static void main(String[] args) {
        CreateGraph createGraph = new CreateGraph();
        Graph graph = new Graph();
        createGraph.initialGraph(graph);
        createGraph.outputGraph(graph);
        graph.dfs();
        graph.bfs();
    }
}
-------------------------------------------------
package edge;

/**
 * 图的边对象类
 * @author King
 */
public class Edge {
    int edgeName=-1;
    Edge next;

    public Edge(){
    }
}
-------------------------------------------------
package edge;

import com.sun.scenario.effect.impl.sw.sse.SSEBlend_SRC_OUTPeer;

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

/**
 * 数组加链表形式  数组是vertexList ,用ArrayList来代替
 */
public class Graph {

    ArrayList<Vertex> vertexList=new ArrayList<Vertex>();
    int vertexNum=0;
    int edgeNum=0;
    boolean[] isVisited;
    public Graph(){}

    public void dfs(){
        isVisited = new boolean[vertexNum];
        System.out.println("DFS:");
        for(int i=0;i<vertexNum;i++){
            if(isVisited[i]==false){
                dfs(isVisited,i);
            }
        }
        System.out.println();
    }

    private void dfs(boolean[] isVisited, int i) {
        System.out.print(vertexList.get(i)+"->");
        isVisited[i] = true;

        Edge edge =vertexList.get(i).firstEdge;
        while(edge!=null&&edge.edgeName!=-1 ){
            if (isVisited[edge.edgeName]==false){
                dfs(isVisited,edge.edgeName);
            }
            edge=edge.next;
        }
    }

    public void bfs(){
        isVisited = new boolean[vertexNum];
        System.out.println("BFS:");
        for(int i=0;i<vertexNum;i++){
            if(isVisited[i]==false){
                bfs(isVisited,i);
            }
        }
        System.out.println();
    }

    private void bfs(boolean[] isVisited, int i) {
        LinkedList<Integer> queue = new LinkedList<>();//队列
        System.out.print(vertexList.get(i)+"->");
        isVisited[i] = true;
        queue.addLast(i);

        while (!queue.isEmpty()){
            int w =queue.removeFirst();
            Edge edge =vertexList.get(w).firstEdge;
            while (edge!=null){
                if (isVisited[edge.edgeName]==false ){
                    System.out.print(vertexList.get(edge.edgeName));
                    isVisited[edge.edgeName]=true;
                    queue.addLast(edge.edgeName);
                }
                edge=edge.next;
            }
        }
    }
}
----------------------------------------------
package edge;

/**
 *
 *
 * Vertex对象类<这里顶点对象只是辅助边构建图,不是作为邻接链表的对象>
 *
 *
 * 图的点对象类
 * @author King
 */
public class Vertex {

    String vertexName;
    Edge firstEdge=new Edge();
    public Vertex(){
    }

    @Override
    public String toString() {
        return vertexName ;
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值