java实现图(邻接矩阵)

目录

 

一、图的知识点:

二、图的实现(都包含在Graph类中)

三、图的遍历(包含在类Graph中)

(一) 深度优先遍历

(二) 广度优先遍历


一、图的知识点:

1. 图的概念:图是由顶点集合及顶点间的关系集合组成的一种数据结构。

2. 图的定义:

G = (V, E) // V代表顶点的集合, E代表边的集合
V = {x | x ∈ 某个数据元素集合}
E = {(x, y) | x, y ∈ V} // 无向图
或者
E = {<x, y> | x, y ∈ V 并且 Path(x, y)} // 有向图 Path代表单向通路

3. 顶点和边:图中的结点成为顶点;两个顶点相关联,则称两个顶点之间有一条边。

4. 有向图:边(也可称为弧)带有方向,在有向图中顶点对<x,y>是有序的。顶点对<x,y>称为从顶点x到顶点y的一条有向边。因此<x,y>和<y,x>并不是同一条边

5. 无向图:边不带有方向,在无向图中顶点对(x,y)称为与顶点x和顶点y相关联的一条边。因此(x,y)和(y,x)是同一条边

6. 完全图:在有n个顶点的无向图中,有n*(n-1)/2条边,即任意两个顶点之间有且只有一条边

7. 邻接顶点:若两个顶点有关联,则两个顶点邻接;

无向图两个顶点互为邻接点;在有向图中,<x,y>是其中的一条边,则称顶点x邻接到顶点y,顶点y邻接自顶点x。

8. 顶点的度:与顶点相关联边的的条数,如上图无向图中D的度为3,有向图中D的度 = 入度(2)+ 出度(1)= 3;

有向图中,顶点的度 = 入度(进入的边) + 出度(出去的边)。

9. 路径:在图中,从顶点v1出发到顶点v2所经过的顶点序列就称为顶点v1到顶点v2的路径,如上无向图 顶点A 到 顶点E 的其中一条路径:A->D->E。

10. 路径长度:对于不带权的图,一条路径的路径长度 = 该路径的边的条数总和

对于带权的图,一条路径的路径长度 = 该路径上的各个边的权值的总和

二、图的实现(都包含在Graph类中)

1.  图的基本实现:

import java.util.*;

public class Graph {
    private ArrayList<String> vertexList; //
    private int[][] arcs; // 存储图对应的邻接矩阵
    private int arcnum; // 边的数目
    private int GraphKind; //0有向图 1无向图
    private boolean[] visited;// 用于判断顶点是否被遍历

    //构造器
    public Graph(int n, int kind) {
        //初始化矩阵和顶点
        arcs = new int[n][n];
        vertexList = new ArrayList<>(n);
        arcnum = 0;
        GraphKind = kind;
        visited = new boolean[n];
    }

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

    //添加边
    //v1代表第一个顶点对应的下标
    //v2代表第二个顶点对应的下标
    public void insertArcs(int v1, int v2, int weight) {
        arcs[v1][v2] = weight;
        if(GraphKind != 0) {
            arcs[v2][v1] = weight;
        }
        arcnum++;
    }

    //图中常用的方法:
    //1. 返回结点的个数
    public int getNumOfVertex() {
        return vertexList.size();
    }

    //2. 得到边的数目
    public int getNumOfArcs() {
        return arcnum;
    }

    //3. 返回顶点对应的下标
    public String getValueByIndex(int index) {
        return vertexList.get(index);
    }

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

    //5. 显示图对应的矩阵
    public void showGraph() {
        System.out.print(" " + "\t");
        for (String s : vertexList) {
            System.out.print(s + "\t");
        }
        System.out.println();
        for (int i = 0; i < vertexList.size(); i ++) {
            System.out.print(vertexList.get(i) + "\t");
            for (int j = 0; j < vertexList.size(); j ++) {
                System.out.print(arcs[i][j] + "\t");
            }
            System.out.println();
        }
    }


}

2. 图的邻接点

    //得到第一个邻接节点
    public int getFirst(int index) {
        for (int i = 0; i < vertexList.size(); i ++) {
            if (arcs[index][i] > 0) {
                return i;
            }
        }
        return -1;
    }

    //得到下一个邻接节点
    public int getNext(int v1, int v2) {
        for (int i = v2 + 1; i < vertexList.size(); i ++) {
            if (arcs[v1][i] > 0) {
                return i;
            }
        }
        return -1;
    }

1)邻接节点的判断:

对于上图无向图所得到的邻接矩阵如下:

判断D的第一个邻接顶点,则观察D的那一行,然后看在D的那一行最前面第一个与D有关联的顶点,则可知,D的第一个邻接顶点是 顶点A,A的第一个邻接节点为 B;

判断D的下一个邻接顶点,则观察D的那一行,与D有关联的顶点都能算作D的下一个邻接节点

三、图的遍历(包含在类Graph中)

(一) 深度优先遍历

1. 原理:

以从顶点A开始遍历为例:

1)A 是第一个遍历的顶点,标记A,寻找A的第一个邻接顶点可知没有,故寻找A的下一个邻接顶点A与B、D都有边,但根据顶点顺序可知下一个邻接顶点为 B,所以标记B;

2)以B为新的遍历的顶点,寻找B的第一个邻接顶点可知为A,但A已经被遍历,所以寻找B的下一个邻接顶点为E,所以标记E;

3)以E为新的遍历的顶点,寻找E的第一个邻接顶点可知为C,且C并未被遍历,所以标记C;

4)以C为新的遍历的顶点,寻找C的第一个邻接顶点可知没有,所以寻找C的下一个邻接顶点为D,所以标记D;

5)以D为新的遍历的顶点,寻找D的第一个邻接顶点为A,但A已经被遍历,所以寻找D的下一个邻接顶点为E,但E已经被遍历,故整个遍历结束;

 遍历结果为: A B E C D

2. 代码实现:

    //深度优先遍历
    //对连通图
    public void dfs (int v) {
        System.out.print(getValueByIndex(v) + "\t");
        //将该节点设置为已访问
        visited[v] = true;
        int w;
        w = getFirst(v);
        //查找结点i的第一个邻接节点
        while (w != -1) {
            if (!visited[w]) {
                dfs(w);
            }
            w = getNext(v, w);
        }
    }

    //对深度优先遍历进行重载,遍历所有结点并进行深度优先遍历
    //即对非连通图的深度优先遍历
    public void dfs() {
        //对visited 进行初始化
        for (int i = 0; i < vertexList.size(); i ++) {
            visited[i] = false;
        }
        for (int i = 0; i < vertexList.size(); i ++) {
            if (!visited[i]) {
                dfs(i);
            }
        }
    }

对上图无向图从顶点A进行深度优先遍历,从顶点C进行深度优先遍历:

 

(二) 广度优先遍历

1. 原理:(借助队列)

以从顶点A开始遍历为例:

1)A 是第一个遍历的顶点,标记A并且使A进入队列,寻找A的第一个邻接顶点可知没有,故寻找A的下一个邻接顶点,A与B、D都有边,但根据顶点顺序可知下一个邻接顶点为 B,所以标记B并且使B进入队列;

2)此时A作为队列的头,将A出队列,继续以顶点A进行遍历,寻找B以后的邻接顶点可知为D,故标记D,使D进入队列,A所有有关联的顶点皆已经遍历完;

3)此时B作为队列的头,将B出队列,以顶点B进行遍历,寻找B的第一个邻接顶点可知为A,但A已经被遍历,因此寻找下一个邻接顶点为E,所以标记E,使E进入队列,B所有有关联的顶点皆已经遍历完;

4)此时D作为队列的头,将D出队列,以顶点D进行遍历,寻找D的第一个邻接顶点可知为A,所以寻找下一个邻接顶点为E,但E已经被标记;

5)此时E作为队列的头,将E出队列,以顶点E进行遍历,寻找E的第一个邻接顶点为C,C未被遍历因此标记C,故整个遍历结束;

故遍历结果为:A B D E C

2. 代码实现:

 //广度优先遍历
    //连通图的广度优先遍历
    public void bfs(int v) {
        int u, w; // u代表队列的头结点对应的下标。w代表邻接节点
        Queue<Integer> q = new ArrayDeque<>();
        System.out.print(getValueByIndex(v) + "\t");
        //标记已访问的结点
        visited[v] = true;
        //将已访问的结点加入队列
        q.add(v);
        while (!q.isEmpty()) {
            //取出队列的头
            u = q.poll();
            //得到第一个邻接点的下标
            w = getFirst(u);
            while (w != -1) {
                if (!visited[w]) {
                    //如果第一个邻接点未被访问则访问第一个邻接节点
                    System.out.print(getValueByIndex(w) + "\t");
                    visited[w] = true;
                    q.add(w);
                }
                //第一个邻接节点已经被访问,找u的下一个邻接节点
                w = getNext(u, w);
            }
        }
    }

    //遍历所有的结点 进行广度优先遍历
    //对非连通图的广度优先遍历
    public void bfs() {
        for (int i = 0; i < vertexList.size(); i ++) {
            visited[i] = false;
        }
        for (int i = 0; i <vertexList.size(); i ++) {
            if (!visited[i]) {
                bfs(i);
            }
        }
    }

对上图无向图从顶点A进行广度优先遍历,从顶点C进行广度优先遍历:

  • 3
    点赞
  • 45
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要使用Python和javalang库获取Java代码的AST(抽象语法树),并将其按照邻接矩阵的方式存储,可以按照以下步骤进行: 1. 使用javalang库解析Java代码文件,生成AST。可以使用javalang.parse.parse方法来解析Java代码文件,该方法返回的是一个javalang.ast.Node类型的对象,表示整个代码文件的AST。 2. 遍历AST,获取节点之间的连接关系,并将它们存储在邻接矩阵中。可以使用深度优先遍历(DFS)或广度优先遍历(BFS)的方式来遍历AST,获取节点之间的连接关系。在遍历AST时,可以使用一个字典来存储节点的编号和节点对象的映射关系,并使用一个二维数组来存储节点之间的连接关系。 以下是一个示例代码,演示了如何使用Python和javalang库获取Java代码的AST,并将其按照邻接矩阵的方式存储: ```python import javalang # 读取Java代码文件 with open("Test.java", "r") as f: java_code = f.read() # 解析Java代码,生成AST tree = javalang.parse.parse(java_code) # 构造节点编号和节点对象的映射关系 node_map = {} node_id = 0 for node in tree.filter(javalang.tree.Node): node_map[node_id] = node node_id += 1 # 构造邻接矩阵 adjacency_matrix = [[0] * node_id for _ in range(node_id)] for node_id, node in node_map.items(): for child_node in node.children: child_id = list(node_map.keys())[list(node_map.values()).index(child_node)] adjacency_matrix[node_id][child_id] = 1 # 输出邻接矩阵 for row in adjacency_matrix: print(row) ``` 在上面的示例中,我们首先读取一个Java代码文件,然后使用javalang.parse.parse方法解析Java代码文件,生成AST。然后,我们遍历AST,构造节点编号和节点对象的映射关系,并使用一个二维数组来存储节点之间的连接关系。最后,我们输出邻接矩阵。 需要注意的是,上面的示例代码只是简单地将AST按照节点之间的父子关系存储在邻接矩阵中,如果需要更精细的AST分析,可以根据具体需求进行修改。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值