在Java中用邻接表实现图(Graph)的存储、搜索和遍历
用邻接表存储图,是使用了数组+链表的结构。
上面这张图就是一个用邻接表存储图的例子。图片上方就是这个链表所存储的图。下半部分是存储的结构。
构造存储数据的类
首先,数组的长度和图中的顶点个数相同,数组中的每个元素代表一个图中的顶点,**数组元素的下标就是该顶点的编号。**如G[0]就代表顶点0,以此类推。
数组中的一个元素也就是一个顶点,同时也是这一条链表的头部。我们给他取名叫Head
。它可以包含如下内容:
int data
: 顶点自身的数据(可选)。可以是任何数据类型,这里先写作int
。Edge next
:指向与它相连的边的链表。也就是一条边的引用。(边Edge
在后面会定义)
在图片中,一个浅紫红色的方块代表的就是这个顶点。
表头的next
指向的就是一条由与该表头所代表的的顶点相连的边(edge)组成的链表。在图片中,它们就是那些深紫色的框框组成的链表。一个深紫色的框框代表一条边。
那么一个边Edge
对象所包含的内容有:
int v1
:这条边连接的第一个顶点编号。int v2
:这条边连接的第二个顶点编号。int weight
:边的权重(可选)。Edge next
:指向v1
与这条边相同的下一条边。
v1
决定了这些边要被串在哪条链表上。比如,如果在一条边中,v1 = 0; v2 = 1
,那么,他就应该出现在G[0]指向的那条链表上。如果这个图是一个有向图,**它代表顶点v1
通往顶点v2
的一条路径。**但是如果它是一个无向图,那么同时也会存在一条边有v1 = 1; v2 = 0
,被串在G[1]的链表上,说明从顶点0也可以走向顶点1。我们可以在上面的图片中找到链表的这连个结点。图片中紫色框框里的数字代表该边的v2
。
那么我们可以先写出Edge类:
public class Edge {
int v1;
int v2;
int weight;
Edge next;
/* Constructor */
public Edge(int v1, int v2, int weight){
this.v1 = v1;
this.v2 = v2;
this.weight = weight;
next = null; //它指向的下一条边初始化为空。
}
}
然后写出Head类,它的next指针是一个Edge的引用:
public class Head {
//int data; //顶点存储的数据。也可以是其他数据类型。(可选)
Edge next;
/* Constructor */
public Head(){
next = null;
}
}
接下来写出存储整个图,也就是存储Head[]
的真正的Graph类。因为是邻接表存储,我们将它命名为LGraph
。除了Head[]
数组,它还要包含一些其他的属性:
numV
:顶点的数量,初始化时就要确定,决定Head[]
的大小。numE
:统计边的数量,初始化时为0,每加入一条边时+1。boolean[] visited
:一个用于记录是否访问过某结点的数组。长度和Head[]
相同,第i个元素代表第i号结点有没有被访问过。在遍历时使用。
import java.util.LinkedList;
public class LGraph {
int numV;
int numE;
Head[] graph;
boolean[] visited;
/* Constructor */
public LGraph(int numV){
this.numV = numV;
numE = 0;
graph = new