图-java语言描述

本文介绍了图的基本概念,包括无向图、有向图和带权图。接着详细阐述了图的两种遍历方法:深度优先遍历和广度优先遍历,分别给出了它们的步骤和应用场景。最后,提到了图的表示方法,如邻接矩阵和邻接表,并提及将提供完整的Java代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、图的一些基本概念

  • 图:每个节点都可以有零个或多个前驱,也可以有零个或多个后继,也就是说,元素之间的关系是任意的。也可以称为多对多。
  • 节点:组成图的重要部分
  • 边:两个节点的连接
  • 无向图:节点之间的连接没有方向
  • 有向图:节点之间的连接有方向
  • 带权图(网):权就是节点中的值
  • 路径:一个节点到另一个节点所经过的路程
  • 图的表示方法
    • 邻接矩阵(用二维数组表示):表示图形之中节点间相邻关系的矩阵
    • 邻接表(用数组+链表表示):数组之中存放了这个图中所有的节点,每一个数组又是一个链表的头节点,该链表表示这个节点与多少个节点有连接
  • 图的深度优先遍历:先遍历每一个节点的第一个邻接节点,也就是纵向的遍历
    • 步骤:
    • 1、访问初始节点 i,并将该节点标记为已访问
    • 2、查找节点 i的第一个邻接节点 j
    • 3、若 j存在:
      • 若 j未被访问,则对 j进行深度优先遍历,也就是把 j当作新的 i,递归
      • 若 j被访问,则对 i的的其他邻接节点继续重复上述步骤
    • 4、若 j不存在:则对 i的其他邻接节点继续重复上述步骤,若没有其他邻接节点,就结束该递归,回到上一层
    • 5、直至所有的节点都已判断完毕,结束
  • 图的广度优先遍历:先遍历一个节点的所有邻接节点,再去遍历其他节点,也就是横向的遍历,类似于一个分层搜索,需要先使用一个队列保持访问过的节点的顺序,以便于按照这个顺序访问这些节点的邻接节点。
    • 步骤:
    • 1、访问初始节点 i,并将该节点标记为已访问,该节点入队列
    • 2、当队列为非空的时候,继续执行,否则算法结束(注意:这里的算法结束是指对当前 i的算法)
    • 3、出队列,取得该队列的头部节点 j
    • 4、查找节点 j的第一个邻接节点 k
      • 1、若节点 k不存在,则回到 3
      • 2、若节点 k存在,则:
        • 1、若节点 k未被访问,则将 k标记为已访问,k入队列
        • 2、查找节点 j的继节点 k的下一个邻接节点 k,回到步骤 5

2、一些常用的方法

	//1、得到节点的数目
    public int getNumOfVertex() {
        return vertexList.size();
    }

    //2、得到边的数目
    public int getNumOfEdges() {
        return numOfEdges;
    }

    //3、得到第几个节点对应的数据
    public String getValueByIndex(int i) {
        return vertexList.get(i);
    }

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

    //5、显示图对应的矩阵
    public void showGraph() {
        for (int[] link : edges) {
            System.out.println(Arrays.toString(link));
        }
    }

    /**
     * 6、得到当前节点的邻接节点的下标
     *
     * @param i 传入的节点的下标
     * @return 该节点的第一个邻接节点的下标,没有的话就返回 -1
     */
    public int getFirstNeighbor(int i) {
        for (int j = 0; j < vertexList.size(); j++) {
            if (edges[i][j] > 0) {
                return j;
            }
        }
        return -1;
    }

    /**
     * 7、根据前一个邻接节点的下标获取到下一个邻接节点的下标
     *
     * @param v1 前一个邻接节点
     * @param v2 要查找邻接节点的节点
     * @return 返回当前节点的邻接节点
     */
    public int getNextNeighbor(int v1, int v2) {
        for (int j = v2 + 1; j < vertexList.size(); j++) {
            if (edges[v1][j] > 0) {
                return j;
            }
        }
        return -1;
    }

3、深度优先遍历

	/**
     * 深度优先遍历
     *
     * @param isVisited 该图所有的节点是否被访问的数组
     * @param i         从第 i个节点开始
     */
    private void dfs(boolean[] isVisited, int i) {
        //首先输出第一个被访问的节点
        System.out.print(getValueByIndex(i) + "->");
        //将节点设置为已访问
        isVisited[i] = true;
        //查找第 i个节点的邻接节点
        int j = getFirstNeighbor(i);

        //判断该节点是否有邻接节点
        while (j != -1) {
            //判断该节点是否被访问过
            if (!isVisited[j]) {
                //递归 dfs方法
                dfs(isVisited, j);
            }
            //得到该邻接节点的邻接节点
            j = getNextNeighbor(i, j);
        }
    }

    //对 dfs方法进行重载,遍历图中的每一个节点并进行 dfs
    public void dfs() {
        //遍历所有的节点
        for (int i = 0; i < getNumOfVertex(); i++) {
            //判断该节点是否被访问
            if (!isVisited[i]) {
                //进行 dfs[回溯]
                dfs(isVisited, i);
            }
        }
    }

4、广度优先遍历

	/**
     * 广度优先遍历
     *
     * @param isVisited 该图所有的节点是否被访问的数组
     * @param i         从第 i个节点开始
     */
    private void bfs(boolean[] isVisited, int i) {
        //表示头节点的下标
        int j;
        //表示当前节点的邻接节点
        int k;
        //记录节点访问顺序的队列
        LinkedList queue = new LinkedList();
        //访问节点,输出节点的信息
        System.out.print(getValueByIndex(i) + "->");
        //标记为已访问
        isVisited[i] = true;
        //将该节点加入队列
        queue.add(i);

        //判断该队列是否为空
        while (!queue.isEmpty()) {
            //取出队列的头节点的下标
            j = (Integer) queue.removeFirst();
            //得到该节点的邻接节点的下标
            k = getFirstNeighbor(i);
            while (k != -1) {
                //是否访问过
                if (!isVisited[i]) {
                    System.out.print(getValueByIndex(i) + "->");
                    //标记已访问
                    isVisited[i] = true;
                    //入队
                    queue.addLast(k);
                }
                //以 j为前驱点,找 k后面的下一个邻接节点
                k = getNextNeighbor(j, k);
            }
        }
    }

    //对 bfs方法进行重载,遍历图中的每一个节点并进行 bfs
    public void bfs() {
        for (int i = 0; i < getNumOfVertex(); i++) {
            if (!isVisited[i]) {
                bfs(isVisited, i);
            }
        }
    }

5、完整的代码

public class GraphDemo {
    //存储节点的集合
    private ArrayList<String> vertexList;
    //存储图对应的邻接矩阵
    private int[][] edges;
    //表示边的数目
    private int numOfEdges;
    //记录每个节点是否被访问
    private boolean[] isVisited;

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

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

        //进行深度优先遍历
        System.out.println("深度优先遍历:");
        graph1.dfs();

        System.out.println();
        System.out.println("------------------------");

        //创建图对象
        GraphDemo graph2 = new GraphDemo(n);
        //循环的添加节点
        for (String vertex : vertexs) {
            graph2.insertVertex(vertex);
        }
        //添加边
        graph2.insertEdge(0, 1, 1);
        graph2.insertEdge(0, 2, 1);
        graph2.insertEdge(1, 2, 1);
        graph2.insertEdge(1, 3, 1);
        graph2.insertEdge(1, 4, 1);

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

        //进行广度优先遍历
        System.out.println("广度优先遍历:");
        graph2.bfs();
    }

    public GraphDemo(int n) {
        edges = new int[n][n];
        vertexList = new ArrayList<String>(n);
        numOfEdges = 0;
        isVisited = new boolean[n];
    }

    //添加节点
    public void insertVertex(String vertex) {
        vertexList.add(vertex);
    }

    /**
     * 添加边
     *
     * @param v1     表示第一个节点对应的下标,也就是第几个节点
     * @param v2     表示第二个节点对应的下标
     * @param weight 传入的值为 1时,表示两个节点建立了连接,传入的值为 0时,表示两个节点不能建立连接
     */
    public void insertEdge(int v1, int v2, int weight) {
        edges[v1][v2] = weight;
        edges[v2][v1] = weight;
        numOfEdges++;
    }

    //一些常用的方法
    //1、得到节点的数目
    public int getNumOfVertex() {
        return vertexList.size();
    }

    //2、得到边的数目
    public int getNumOfEdges() {
        return numOfEdges;
    }

    //3、得到第几个节点对应的数据
    public String getValueByIndex(int i) {
        return vertexList.get(i);
    }

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

    //5、显示图对应的矩阵
    public void showGraph() {
        for (int[] link : edges) {
            System.out.println(Arrays.toString(link));
        }
    }

    /**
     * 6、得到当前节点的邻接节点的下标
     *
     * @param i 传入的节点的下标
     * @return 该节点的第一个邻接节点的下标,没有的话就返回 -1
     */
    public int getFirstNeighbor(int i) {
        for (int j = 0; j < vertexList.size(); j++) {
            if (edges[i][j] > 0) {
                return j;
            }
        }
        return -1;
    }

    /**
     * 7、根据前一个邻接节点的下标获取到下一个邻接节点的下标
     *
     * @param v1 前一个邻接节点
     * @param v2 要查找邻接节点的节点
     * @return 返回当前节点的邻接节点
     */
    public int getNextNeighbor(int v1, int v2) {
        for (int j = v2 + 1; j < vertexList.size(); j++) {
            if (edges[v1][j] > 0) {
                return j;
            }
        }
        return -1;
    }

    /**
     * 深度优先遍历
     *
     * @param isVisited 该图所有的节点是否被访问的数组
     * @param i         从第 i个节点开始
     */
    private void dfs(boolean[] isVisited, int i) {
        //首先输出第一个被访问的节点
        System.out.print(getValueByIndex(i) + "->");
        //将节点设置为已访问
        isVisited[i] = true;
        //查找第 i个节点的邻接节点
        int j = getFirstNeighbor(i);

        //判断该节点是否有邻接节点
        while (j != -1) {
            //判断该节点是否被访问过
            if (!isVisited[j]) {
                //递归 dfs方法
                dfs(isVisited, j);
            }
            //得到该邻接节点的邻接节点
            j = getNextNeighbor(i, j);
        }
    }

    //对 dfs方法进行重载,遍历图中的每一个节点并进行 dfs
    public void dfs() {
        //遍历所有的节点
        for (int i = 0; i < getNumOfVertex(); i++) {
            //判断该节点是否被访问
            if (!isVisited[i]) {
                //进行 dfs[回溯]
                dfs(isVisited, i);
            }
        }
    }

    /**
     * 广度优先遍历
     *
     * @param isVisited 该图所有的节点是否被访问的数组
     * @param i         从第 i个节点开始
     */
    private void bfs(boolean[] isVisited, int i) {
        //表示头节点的下标
        int j;
        //表示当前节点的邻接节点
        int k;
        //记录节点访问顺序的队列
        LinkedList queue = new LinkedList();
        //访问节点,输出节点的信息
        System.out.print(getValueByIndex(i) + "->");
        //标记为已访问
        isVisited[i] = true;
        //将该节点加入队列
        queue.add(i);

        //判断该队列是否为空
        while (!queue.isEmpty()) {
            //取出队列的头节点的下标
            j = (Integer) queue.removeFirst();
            //得到该节点的邻接节点的下标
            k = getFirstNeighbor(i);
            while (k != -1) {
                //是否访问过
                if (!isVisited[i]) {
                    System.out.print(getValueByIndex(i) + "->");
                    //标记已访问
                    isVisited[i] = true;
                    //入队
                    queue.addLast(k);
                }
                //以 j为前驱点,找 k后面的下一个邻接节点
                k = getNextNeighbor(j, k);
            }
        }
    }

    //对 bfs方法进行重载,遍历图中的每一个节点并进行 bfs
    public void bfs() {
        for (int i = 0; i < getNumOfVertex(); i++) {
            if (!isVisited[i]) {
                bfs(isVisited, i);
            }
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值