【图(1)】:用邻接矩阵和邻接表实现图

【图(1)】:用邻接矩阵和邻接表实现图图 Graph 是由节点 Node 和边 Edge 组成的数据结构

大家好,欢迎来到IT知识分享网。

目录

一、图的基本概念

二、图的存储结构

1.邻接矩阵

2.邻接表


一、图的基本概念

注意:概念性的文字不列出太多,记不住也不便于理解

图(Graph)是由节点(Node)和边(Edge)组成的数据结构。图可以用来表示对象之间的关系,其中节点表示对象,边表示对象之间的连接或关联。

图的基本概念包括以下几个要素:

  1. 节点:也称为顶点(Vertex),表示图中的对象。节点可以具有属性或标签,用于描述对象的特征。
  2. 边:也称为弧(Arc)或连接(Link),表示节点之间的关系或连接。边可以是有向的(从一个节点指向另一个节点)或无向的(没有方向)。
  3. 权重:边可以带有权重(Weight),表示节点之间的某种度量或距离。权重可以表示关系的强度、路径的长度等。
  4. 邻接:两个节点之间存在一条边,则它们被称为邻接(Adjacent)节点。如果图是有向的,那么邻接节点分为入度邻接节点和出度邻接节点。无向图某顶点的度=入度=出度,有向图某顶点的度=入度+出度。
  5. 路径:路径(Path)是由边连接的节点序列,表示从一个节点到另一个节点的通路。路径的长度可以通过边的数量或权重之和来衡量。
  6. 连通性:图中的节点通过边相互连接,如果任意两个节点之间都存在路径,那么图被称为连通图。如果图中存在不连通的部分,则称为非连通图。
  7. 图的类型:根据边的属性和连接方式,图可以分为有向图(Directed Graph)和无向图(Undirected Graph)。有向图的边具有方向性,无向图的边没有方向。此外,还有其他特殊类型的图,如加权图、带环图等。

不带权值的图:

【图(1)】:用邻接矩阵和邻接表实现图

带权值的图:

【图(1)】:用邻接矩阵和邻接表实现图

简单路径与回路:
若路径上各顶点
v1

v2

v3



vm
均不重复,则称这样的路径为简单路径

若路径上
第一个顶点
v1
和最后一个顶点
vm
重合,则称这样的路径为回路或环。

【图(1)】:用邻接矩阵和邻接表实现图

子图:
设图
G = {V, E}
和图
G1 = {V1

E1}
,若
V1
属于
V

E1
属于
E
,则称
G1

G
的子图
【图(1)】:用邻接矩阵和邻接表实现图

 

二、图的存储结构

因为图中既有节点,又有边(节点与节点之间的关系),因此,在图的存储中,只需要保存:节点和边关系即可

1.邻接矩阵

因为节点与节点之间的关系就是连通与否,即为0或者1,因此邻接矩阵(二维数组)即是:先用一个数组将定点保存,然后采用矩阵来表示节点与节点之间的关系。 
注意:大多图的算法题面试题都是基于邻接矩阵的形式,因此邻接矩阵是重点。

【图(1)】:用邻接矩阵和邻接表实现图

拿G1这个图举例,
假设顶点个数为n,邻接矩阵为arr[n][n],arr[i][j]的值就表示权值(无向图则为1),而指向则为i -> j,即i下标对应顶点指向j下标对应的顶点。
无向图的邻接矩阵是对称的第i行(列)元素之和,就是顶点i的度有向图的邻接矩阵则不一定是对称 的,第i行(列)元素之后就是顶点i 的出(入)度

有向带权值图:
【图(1)】:用邻接矩阵和邻接表实现图

用邻接矩阵存储图的优点是能够快速知道两个顶点是否连通,缺陷是如果顶点比较多,边比较少时,矩阵中存储了大量的0成为系数矩阵,比较浪费空间,并且要求两个节点之间的路径不是很好求。

下面看代码实现(基于无向图G1)

  • printGraph方法用于打印邻接矩阵;
  • 顶点用HashMap存储 顶点-下标 的键值对,使获取下标的时间复杂度达到O(1);
  • addEdge方法表示v1指向v2。
//邻接矩阵 public class GraphByMatrix { private char[] arrayV; //存放顶点 private int[][] matrix; //存放权值或者边 private boolean isDirect; //是否是有向图 private HashMap<Character, Integer> indexMap; //定位顶点对应的下标 public GraphByMatrix(int size, boolean isDirect) { arrayV = new char[size]; matrix = new int[size][size]; this.isDirect = isDirect; indexMap = new HashMap<>(); } / * 初始化 * * @param array 顶点集合 */ public void initArrayV(char[] array) { for (int i = 0; i < array.length; i++) { arrayV[i] = array[i]; indexMap.put(array[i], i); } } / * 给两个顶点的边添加权重 * * @param v1 顶点1 * @param v2 顶点2 * @param weight 权值 */ public void addEdge(char v1, char v2, int weight) { int index1 = getIndexOfV(v1); int index2 = getIndexOfV(v2); if (index1 == -1 || index2 == -1) { throw new RuntimeException("v1或v2顶点不存在"); } matrix[index1][index2] = weight; //无向图则对称位置也设置权重 if (!isDirect) { matrix[index2][index1] = weight; } } //获取该顶点对应的下标 private int getIndexOfV(char v) { Integer index = indexMap.get(v); return index == null ? -1 : index;//hashMap定位 } / * 获取顶点的度 * * @param v * @return */ public int getDevOfV(char v) { //1.获取顶点在arrayV的下标 int index = getIndexOfV(v); //2.计算该顶点的出度 int count = 0; for (int i = 0; i < arrayV.length; i++) { if (matrix[index][i] != 0) { count++; } } //3.如果是有向图,还需要计算顶点的入度 if (isDirect) { for (int i = 0; i < arrayV.length; i++) { if (matrix[i][index] != 0) { count++; } } } return count; } public void printGraph() { System.out.print(" "); for (char c : arrayV) { System.out.print(c + " "); } System.out.println(); for (int i = 0; i < matrix.length; i++) { System.out.print(arrayV[i] + " "); for (int j = 0; j < matrix[i].length; j++) { System.out.print(matrix[i][j] + " "); } System.out.println(); } } }

代码测试:

 public static void main(String[] args) { GraphByMatrix graphByMatrix = new GraphByMatrix(4, false); char[] array = {'A', 'B', 'C', 'D'}; graphByMatrix.initArrayV(array); graphByMatrix.addEdge('A', 'B', 1); graphByMatrix.addEdge('A', 'D', 1); graphByMatrix.addEdge('B', 'A', 1); graphByMatrix.addEdge('B', 'C', 1); graphByMatrix.addEdge('C', 'B', 1); graphByMatrix.addEdge('C', 'D', 1); graphByMatrix.addEdge('D', 'A', 1); graphByMatrix.addEdge('D', 'C', 1); graphByMatrix.printGraph(); System.out.println("顶点A的度:" + graphByMatrix.getDevOfV('A')); }

假设无向图(第二参数为false)                              假设有向图(第二参数为true)

【图(1)】:用邻接矩阵和邻接表实现图 【图(1)】:用邻接矩阵和邻接表实现图

2.邻接表

邻接表:使用数组表示顶点的集合,使用链表表示边的关系
1. 无向图邻接表存储

【图(1)】:用邻接矩阵和邻接表实现图

注意:无向图中同一条边在邻接表中出现了两次。如果想知道顶点vi的度,只需要知道顶点vi边链表集
合中结点的数目即可
2. 有向图邻接表存储
【图(1)】:用邻接矩阵和邻接表实现图

注意:有向图中每条边在邻接表中只出现一次,与顶点vi对应的邻接表所含结点的个数,就是该顶点的出度,也称出度表,要得到vi顶点的入度,必须检测其他所有顶点对应的边链表,看有多少边顶点的dest 取值是i。总结就是看顶点V指向哪些顶点(出度表),加上哪些顶点也指向顶点V(入度表)
import java.util.ArrayList; import java.util.HashMap; //邻接表 public class GraphByTable { static class Node { public int src; //起点坐标 public int dest; //终点坐标 public int weight; //权重 public Node next; public Node(int src, int dest, int weight) { this.src = src; this.dest = dest; this.weight = weight; } } private ArrayList<Node> edgeList; //保存所有的顶点 private char[] arrayV; //顶点数组 private boolean isDirect; //是否是有向图 private HashMap<Character, Integer> indexMap; //快速定位顶点下标 public GraphByTable(int size, boolean isDirect) { arrayV = new char[size]; this.isDirect = isDirect; indexMap = new HashMap<>(); edgeList = new ArrayList<>(size); //给list添加元素,否则list初始为空 for (int i = 0; i < size; i++) { edgeList.add(null); } } public void initArrayV(char[] array) { for (int i = 0; i < array.length; i++) { arrayV[i] = array[i]; indexMap.put(array[i], i); } } / * 给两个顶点的边添加权重 * * @param v1 顶点1 * @param v2 顶点2 * @param weight 权值 */ public void addEdge(char v1, char v2, int weight) { //1.找出两个顶点在数组中的位置 int src = getIndexOfV(v1); int dest = getIndexOfV(v2); addEdgeChild(src, dest, weight); //连通 //2.如果是无向图,反方向连通 if (!isDirect) { addEdgeChild(dest, src, weight); } } private void addEdgeChild(int src, int dest, int weight) { //获取顶点集合中链表的头节点 Node cur = edgeList.get(src); while (cur != null) { //如果当前节点的dest就是要连通的dest,说明已经连通过了 if (cur.dest == dest) { return; } cur = cur.next; } //走到这说明顶点还没连通过,进行头插 Node node = new Node(src, dest, weight); node.next = edgeList.get(src); edgeList.set(src, node); } private int getIndexOfV(char v) { return indexMap.get(v); } / * 获取顶点的度 * * @param v * @return */ public int getDevOfV(char v) { //1.获取顶点的下标,得到链表 int index = getIndexOfV(v); Node cur = edgeList.get(index); //2.计算出度(无向图的度就是出度) int count = 0; while (cur != null) { count++; cur = cur.next; } //3.如果是有向图,还要计算顶点的入度 if (isDirect) { for (int i = 0; i < edgeList.size(); i++) { //出度的链表不能计算进去 if (i != index) { cur = edgeList.get(i); while (cur != null) { //cur的dest为当前顶点下标,就是当前顶点的入度 if (cur.dest == index) { count++; } cur = cur.next; } } } } return count; } public void printGraph() { for (int i = 0; i < edgeList.size(); i++) { System.out.print(arrayV[i] + " -> "); Node cur = edgeList.get(i); while (cur != null) { System.out.print(cur.dest + " -> "); cur = cur.next; } System.out.println(); } } public static void main(String[] args) { GraphByTable graphByTable = new GraphByTable(4, true); char[] array = {'A', 'B', 'C', 'D'}; graphByTable.initArrayV(array); graphByTable.addEdge('A', 'B', 1); graphByTable.addEdge('A', 'D', 1); graphByTable.addEdge('B', 'A', 1); graphByTable.addEdge('B', 'C', 1); graphByTable.addEdge('C', 'B', 1); graphByTable.addEdge('C', 'D', 1); graphByTable.addEdge('D', 'A', 1); graphByTable.addEdge('D', 'C', 1); graphByTable.printGraph(); System.out.println(graphByTable.getDevOfV('A')); } }

测试图仍为前面的无向图G1

假设无向图:                                                       有向图:

【图(1)】:用邻接矩阵和邻接表实现图【图(1)】:用邻接矩阵和邻接表实现图

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/125391.html

(0)
上一篇 2025-09-27 18:33
下一篇 2025-09-27 18:45

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注微信