目录
一、图的基本概念
二、图的存储结构
1.邻接矩阵
2.邻接表
一、图的基本概念
注意:概念性的文字不列出太多,记不住也不便于理解
图(Graph)是由节点(Node)和边(Edge)组成的数据结构。图可以用来表示对象之间的关系,其中节点表示对象,边表示对象之间的连接或关联。
图的基本概念包括以下几个要素:
-
节点:也称为顶点(Vertex),表示图中的对象。节点可以具有属性或标签,用于描述对象的特征。
-
边:也称为弧(Arc)或连接(Link),表示节点之间的关系或连接。边可以是有向的(从一个节点指向另一个节点)或无向的(没有方向)。
-
权重:边可以带有权重(Weight),表示节点之间的某种度量或距离。权重可以表示关系的强度、路径的长度等。
-
邻接:两个节点之间存在一条边,则它们被称为邻接(Adjacent)节点。如果图是有向的,那么邻接节点分为入度邻接节点和出度邻接节点。无向图某顶点的度=入度=出度,有向图某顶点的度=入度+出度。
-
路径:路径(Path)是由边连接的节点序列,表示从一个节点到另一个节点的通路。路径的长度可以通过边的数量或权重之和来衡量。
-
连通性:图中的节点通过边相互连接,如果任意两个节点之间都存在路径,那么图被称为连通图。如果图中存在不连通的部分,则称为非连通图。
-
图的类型:根据边的属性和连接方式,图可以分为有向图(Directed Graph)和无向图(Undirected Graph)。有向图的边具有方向性,无向图的边没有方向。此外,还有其他特殊类型的图,如加权图、带环图等。
不带权值的图:
带权值的图:
二、图的存储结构
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)
2.邻接表
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
假设无向图: 有向图: