/**
* 图(无向网)的邻接表存储结构
*/
public class AdjacencyListGraph {
// 邻接表的顶点数组
vertexNode[] vexs;
int vexNums,arcNums;
// 顶点数组的元素类
class vertexNode{
// 数据域
char data;
// 存放由该结点的邻接点组成的单链表的头指针
// head只是为了方便操作的一个单链表头指针,其中不存放任何有意义的值,head之后的结点存储的才是真正的邻接点域
ListNode head;
public vertexNode(char data){
this.data=data;
this.head=new ListNode();
}
}
// 邻接点单链表的元素类
class ListNode{
// 存放当前邻接点在顶点数组中的下标位置
int pos;
// 存放指向下一个邻接点的指针
ListNode next;
// 存放权值
int info;
public ListNode(){}
public ListNode(int pos, ListNode next, int info){
this.pos=pos;
this.next=next;
this.info=info;
}
}
public AdjacencyListGraph(int n){
vexs=new vertexNode[n];
}
/**
* 初始化邻接表
* @param vexStr 邻接表的顶点
* @param arcStr 邻接表的边
*/
public void init(String vexStr, String[] arcStr){
// 初始化顶点数组
for (int i = 0; i < vexs.length; i++) {
vexs[i]=new vertexNode(vexStr.charAt(i));
// 顶点的数量+1
vexNums++;
}
// 初始化邻接链表
// 先根据边的两个顶点的值,确定两个顶点在顶点表中的下标
for (String s : arcStr) {
// 顶点v1
char v1=s.charAt(0);
// 顶点v2
char v2=s.charAt(2);
// 权值
int w=s.charAt(4)-'0';
// 得到两个顶点的下标i,j
int i=locateVex(v1);
int j=locateVex(v2);
ListNode p = null;
// 给下标为i的顶点添加邻接点,邻接点下标为j
ListNode listNode_i = new ListNode(j, null, w);
p=vexs[i].head;
listNode_i.next=p.next;
p.next=listNode_i;
// 因为无向网的边的两个顶点是互为邻接点的,所以也要给下标为j的顶点添加邻接点,它的邻接点下标为i
ListNode listNode_j = new ListNode(i, null, w);
p=vexs[j].head;
listNode_j.next=p.next;
p.next=listNode_j;
// 边的数量+1
arcNums++;
}
}
// 查找某一顶点在顶点数组中的下标位置
private int locateVex(char v){
for (int i = 0; i < vexs.length; i++) {
if(v==vexs[i].data){
return i;
}
}
return -1;
}
/**
* 邻接表的深度优先遍历
* 从代码实现逻辑可以看出,深度优先遍历类似于树的先序遍历,
* 1.每次先访问顶点(根节点),然后访问它的邻接点(子树),直至当前顶点的邻接点都被访问过(到达叶子结点),
* 2.然后返回上一层,递归地进行第1步,直至所有顶点都被遍历过
* @param vex 起点
* @param visited 辅助数组,用以标识顶点是否被访问过
*/
public void DFS(int vex,int[] visited){
visited[vex]=1;
System.out.println("访问了"+vexs[vex].data);
ListNode p = vexs[vex].head;
while(p.next!=null){
p=p.next;
if(visited[p.pos] == 0){
DFS(p.pos, visited);
}
}
}
/**
* 邻接表的广度优先遍历
* 1.将起始点vertexNode入队,再将vertexNode出队
* 2.先将对头出队,并将出队顶点设为p,遍历p的邻接点单链表,将所有未访问过的元素入队
* 3.队列不为空,则重复第2步,直至队列为空,此时邻接表的广度优先遍历完成
* 不同于邻接矩阵的是,在遍历邻接表并将邻接点入队时,只需判断该邻接点是否访问过即可
* 因为邻接表只存储有效的邻接点
* @param vex 起始顶点
* @param queue 辅助队列
* @param visited 辅助数组
*/
public void BFS(int vex, Queue queue, int[] visited){
// 起始点
vertexNode vertexNode = vexs[vex];
queue.add(vertexNode);
visited[vex]=1;
System.out.println("访问了"+vexs[vex].data);
// 如果队列不为空
while(!queue.isEmpty()){
vertexNode p = (vertexNode) queue.poll();
// 头指针
ListNode head=p.head;
while(head.next!=null){
head=head.next;
if(visited[head.pos]==0){
queue.add(vexs[head.pos]);
visited[head.pos]=1;
System.out.println("访问了"+vexs[head.pos].data);
}
}
}
}
}
测试类:
public class Test {
public static void main(String[] args) {
AdjacencyListGraph adjacencyListGraph=new AdjacencyListGraph(4);
// 无向网的顶点集合
String vexStr="ABCD";
// 无向网的边集合,例如"A,B,4",A,B分别为边的两个顶点,4为此条边的权值
String arcStr[]={"A,B,4","A,C,3","A,D,9","B,C,1","B,D,7","C,D,2"};
adjacencyListGraph.init(vexStr,arcStr);
System.out.println("图(无向网)初始化完成!");
System.out.println("深度优先遍历:");
adjacencyListGraph.DFS(3,new int[4]);
System.out.println("广度优先遍历:");
adjacencyListGraph.BFS(0,new ArrayDeque(),new int[4]);
}
}
测试结果: