[ 数据结构 ] 图(Graph)--------深度优先、广度优先遍历

0 基本介绍

  1. 为什么要有图?

    无论是线性表还是树结构,局限于表示一个直接前驱和一个直接后继的关系(一对一/一对多),当我们需要表示多对多的关系时, 这里我们就用到了图

  2. 节点间的连接成为边,节点称为顶点,一个顶点到另一个顶点所经过的边叫路径,边有方向的叫有向图,边没有方向的叫无向图,边带权值的叫带权图也叫网

  3. 图的表示方式有两种:邻接矩阵/邻接表,分别使用二维数组/链表,简单理解:顶点3所表示的一维数组/单链表中,顶点5表示的元素/节点大小就是顶点3和5间边的权值,权值为1表示可直接连接,否则无法直连(通常不用0而使用极大值65535表示)

image-20230109132754621.png

00000000000000000000000上图为邻接矩阵,下图为邻接表000000000000000000000000

image-20230109132856023.png

1 深度优先遍历

  1. 首先创建图Graph类,属性包括邻接矩阵(二维数组)、顶点集合、边总数
  2. 传入以上参数即可构造图对象
  3. 编写对单个顶点的深度优先遍历方法dfs:传参为顶点在集合中的下标index,以及表示是否访问顶点的数组isVisited,方法体为打印index顶点并更新其为已访问,获取index的首个未访问过的邻接节点w,对w顶点递归调用dfs,方法结束
  4. 考虑到对单个顶点深度优先遍历,可能会有深度不够的情况,因此循环对所有顶点调用dfs

image-20230109135715326.png

//封装调用dfs
    //虽是深度优先遍历,但考虑邻接中断(深度不够),因此起点有序调用dfs
    public void dfs() {
        boolean[] isVisited = new boolean[vertexes.size()];
        for (int i = 0; i < vertexes.size(); i++) {
            if (!isVisited[i]) {
                dfs(i,isVisited);
            }
        }
    }
    //深度优先遍历
    //对当前节点的处理(打印+已读)+向邻接结点递归
    public void dfs(int index, boolean[] isVisited) {
        System.out.print(getValueByIndex(index) + "->");
        isVisited[index] = true;
        int w = getFirstNeighbor(index);
        while (w != -1) {
            if (isVisited[w]) {
                w = getNextNeighbor(index, w);
            } else {
                dfs(w,isVisited);
            }
        }
    }

2 广度优先遍历

  1. 沿用1、2步,编写单个顶点的广度优先遍历方法bfs:对index顶点打印且标记为已访问,遍历其所有未访问过的邻接节点,并打印和加入队列
  2. 取出队列头部的顶点,同样遍历其所有未访问过的邻接节点,并打印和加入队列
  3. 5和6步骤采用内外两层while循环实现
  4. 考虑到对单个顶点广度度优先遍历,可能会有广度不够的情况,因此循环对所有顶点调用dfs
//封装调用bfs
    //虽是广度优先遍历,但考虑广度不够,因此起点有序调用dfs
    public void bfs() {
        boolean[] isVisited = new boolean[vertexes.size()];
        for (int i = 0; i < vertexes.size(); i++) {
            if (!isVisited[i]) {
                bfs(i,isVisited);
            }
        }
    }
    //广度优先遍历
    public void bfs(int index, boolean[] isVisited) {
        int u = 0;
        int w = 0;
        System.out.print(getValueByIndex(index) + "->");
        isVisited[index] = true;
        LinkedList<Integer> queue = new LinkedList<>();
        queue.addLast(index);

        while (!queue.isEmpty()) {
            //下一个节点的广度遍历
            u = queue.removeFirst();
            w = getFirstNeighbor(u);
            //遍历队列头的所有邻接节点
            while (w != -1) {
                if (!isVisited[w]) {
                    System.out.print(getValueByIndex(w) + "->");
                    isVisited[w] = true;
                    //填充队列
                    queue.addLast(w);
                }
                w = getNextNeighbor(u, w);
            }
        }
    }

3 代码实现

//图
public class Graph {
    public static void main(String[] args) {

        //测试
        int n = 5;
        Graph graph = new Graph(5);
        String[] vertexValue = {"A", "B", "C", "D", "E"};
        for (String s : vertexValue) {
            graph.insertVertex(s);
        }
        graph.insertEdge(1,0,1);
        graph.insertEdge(1,2,1);
        graph.insertEdge(1,3,1);
        graph.insertEdge(1,4,1);
        graph.insertEdge(2,0,1);
        graph.show();
        graph.dfs(0,new boolean[5]);
//        graph.bfs();
    }

    List<String> vertexes;
    int[][] edges;
    int edgesNum;

    public Graph(int n) {
        vertexes = new ArrayList<>(n);
        edges = new int[n][n];
        edgesNum = 0;
    }

    //封装调用bfs
    //虽是广度优先遍历,但考虑广度不够,因此起点有序调用dfs
    public void bfs() {
        boolean[] isVisited = new boolean[vertexes.size()];
        for (int i = 0; i < vertexes.size(); i++) {
            if (!isVisited[i]) {
                bfs(i,isVisited);
            }
        }
    }
    //广度优先遍历
    public void bfs(int index, boolean[] isVisited) {
        int u = 0;
        int w = 0;
        System.out.print(getValueByIndex(index) + "->");
        isVisited[index] = true;
        LinkedList<Integer> queue = new LinkedList<>();
        queue.addLast(index);

        while (!queue.isEmpty()) {
            //下一个节点的广度遍历
            u = queue.removeFirst();
            w = getFirstNeighbor(u);
            //遍历队列头的所有邻接节点
            while (w != -1) {
                if (!isVisited[w]) {
                    System.out.print(getValueByIndex(w) + "->");
                    isVisited[w] = true;
                    //填充队列
                    queue.addLast(w);
                }
                w = getNextNeighbor(u, w);
            }
        }
    }

    //封装调用dfs
    //虽是深度优先遍历,但考虑邻接中断(深度不够),因此起点有序调用dfs
    public void dfs() {
        boolean[] isVisited = new boolean[vertexes.size()];
        for (int i = 0; i < vertexes.size(); i++) {
            if (!isVisited[i]) {
                dfs(i,isVisited);
            }
        }
    }
    //深度优先遍历
    //对当前节点的处理(打印+已读)+向邻接结点递归
    public void dfs(int index, boolean[] isVisited) {
        System.out.print(getValueByIndex(index) + "->");
        isVisited[index] = true;
        int w = getFirstNeighbor(index);
        while (w != -1) {
            if (isVisited[w]) {
                w = getNextNeighbor(index, w);
            } else {
                dfs(w,isVisited);
            }
        }
    }
    //获取第一个邻接结点
    public int getFirstNeighbor(int index) {
        for (int i = 0; i < vertexes.size(); i++) {
            if (edges[index][i] > 0) {
                return i;
            }
        }
        return -1;
    }
    //获取下一个邻接节点
    public int getNextNeighbor(int v1, int v2) {
        for (int i = v2+1; i < vertexes.size(); i++) {
            if (edges[v1][i] > 0) {
                return i;
            }
        }
        return -1;
    }

    //插入节点
    public void insertVertex(String vertex) {
        vertexes.add(vertex);
    }

    //插入边
    public void insertEdge(int v1, int v2, int weight) {
        edges[v1][v2] = weight;
        edges[v2][v1] = weight;
        edgesNum++;
    }

    //返回节点数
    public int getVertexNum() {
        return vertexes.size();
    }

    //返回边个数
    public int getEdgesNum() {
        return edgesNum;
    }

    //返回节点
    public String getValueByIndex(int index) {
        return vertexes.get(index);
    }

    //返回边的权值
    public int getWeight(int v1, int v2) {
        return edges[v1][v2];
    }

    //显示图对应的矩阵
    public void show() {
        for (int[] edge : edges) {
            System.out.println(Arrays.toString(edge));
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值