涉及到递归的算法都挺麻烦的,优化了一点点老韩的代码,注释了一些个人理解
package org.example._20图;
import lombok.Data;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
public class GraphDemo {
public static void main(String[] args) {
int n = 8;
//String Vertexs[] = {"A", "B", "C", "D", "E"};
String Vertexs[] = {"1", "2", "3", "4", "5", "6", "7", "8"};
Graph graph = new Graph(n);
for (String vertex : Vertexs) {
graph.insertVertex(vertex);
}
// graph.insertEdge(0, 1, 1);
// graph.insertEdge(0, 2, 1);
// graph.insertEdge(1, 2, 1);
// graph.insertEdge(1, 3, 1);
// graph.insertEdge(1, 4, 1);
//更新边的关系
graph.insertEdge(0, 1, 1);
graph.insertEdge(0, 2, 1);
graph.insertEdge(1, 3, 1);
graph.insertEdge(1, 4, 1);
graph.insertEdge(3, 7, 1);
graph.insertEdge(4, 7, 1);
graph.insertEdge(2, 5, 1);
graph.insertEdge(2, 6, 1);
graph.insertEdge(5, 6, 1);
graph.showGraph();
System.out.println("深度优先遍历");
graph.dfs(); //A→B→C→D→E
System.out.println();
System.out.println("广度优先遍历");
graph.bfs();
}
}
@Data //getter获取边数
class Graph {
private ArrayList<String> vertexList; //存顶点字母
private int[][] metrics; //矩阵显示对应边
private int numOfEdges; //整个图的边数
private boolean[] isVisited; //记录某个结点是否被访问
public Graph(int n) {
metrics = new int[n][n];
vertexList = new ArrayList<String>(n);
numOfEdges = 0;
}
//准备结点
public void insertVertex(String vertex) {
vertexList.add(vertex);
}
//为结点连接边
public void insertEdge(int v1, int v2, int weight) {
metrics[v1][v2] = weight;
metrics[v2][v1] = weight;
numOfEdges++;
}
//打印图矩阵
public void showGraph() {
for (int[] metric : metrics) {
System.out.println(Arrays.toString(metric));
}
}
//找到图第一层遍历结点的下一个连接结点
public int getFirstNeighbor(int index) {
for (int i = 0; i < getNumofVertex(); i++) {
if (metrics[index][i] == 1) {
return i;
}
}
return -1;
}
//当前结点找不到下一个连接的结点,所以这个点废了,于是找到下一个结点
//v2就是当前废了的结点,v1是基准结点,i=v2+1就是跳过了当前结点
public int getNextNeighbor(int v1, int v2) {
for (int i = v2 + 1; i < getNumofVertex(); i++) {
if (metrics[v1][i] == 1) {
return i;
}
}
return -1;
}
//深度优先遍历
public void dfs(boolean[] isVisited, int i) {
System.out.print(getValueByIndex(i) + "→"); //先打印
isVisited[i] = true;//打印完当前点设置为已访问
int w = getFirstNeighbor(i); //找到当前节点的第一个结点
while (w != -1) { //如果找到了
if (!isVisited[w]) { //并且没有被访问过
dfs(isVisited, w); //就递归:打印,然后以w为基准找下一个
}
w = getNextNeighbor(i, w); //不论有没有被访问过,都以w最近连接的点开始找下一个点,比如如果w是e点,那么从b开始往后找
//具体流程
// 1.A->B A为基准
// 2.B->C B为基准
// 3.C->x 回溯到B
// 4.B->C->D
// 5.D->x 回溯到B
// 6.B->C->D->E
// 7.E->x 回溯到B
// 8.回溯到A
// 9.AC不通 此时isVisited全是true,遍历结束
}
}
//调用dfs
public void dfs() {
isVisited = new boolean[getNumofVertex()];
for (int i = 0; i < getNumofVertex(); i++) {
if (!isVisited[i]) {
dfs(isVisited, i);
}
}
}
//广度优先遍历
public void bfs(boolean[] isVisited, int i) {
int u; //每次bfs的第一个结点
int w; //后续的结点
LinkedList<Integer> queue = new LinkedList<>(); //广度优先要用到队列
System.out.print(getValueByIndex(i) + "→"); //打印
isVisited[i] = true; //设置为已访问
queue.addLast(i); //加入队列
while (!queue.isEmpty()) { //如果队列非空
u = queue.removeFirst(); //取出头结点,被取完了就说明遍历完了
w = getFirstNeighbor(u); //尝试寻找该头结点后续的结点
while (w != -1) { //如果有
if (!isVisited[w]) { //并且没被访问过
System.out.print(getValueByIndex(w) + "→"); //打印
isVisited[w] = true; //设置为已访问
queue.addLast(w); //加入队列
}
//不论w有没有被访问过,都要回到u开始找w之后的下一个邻接点,如果访问过那么上面已经打印了,如果没有那就找下一个
w = getNextNeighbor(u, w);
}
}
//具体如下
// 1.A入队 [A]
// 2.A->B A出队做基准,[B]
// 3.A->(B)->C [B,C]
// 4.AD不通,A结束查找
// 5.B出队做基准 [C]
// 6.A->B->(C)->D [C,D]
// 7.A->B->(C)->(D)->E [C,D,E]
// 8.B没有更多相连的,C出队做基准[D,E]
// 9.CA CB已连通,A和B已标记为true,C没有更多相连的,D出队做基准 [E]
// 10.DB已连通,B已标记为true,D没有更多相连的,E出队做基准
// 11.EB已连通,B已标记为true,E没有更多相连的,E出队
// 12.队列为空,退出循环,isVisited全为true,遍历结束
}
//调用bfs,代码和调用dfs一模一样
public void bfs() {
isVisited = new boolean[getNumofVertex()];
for (int i = 0; i < getNumofVertex(); i++) {
if (!isVisited[i]) { //全都被访问过了就会退出
bfs(isVisited, i);
}
}
}
//获取图顶点数
public int getNumofVertex() {
return vertexList.size();
}
//获取图对应字母 0是A,1是B...
public String getValueByIndex(int i) {
return vertexList.get(i);
}
//从矩阵中获取边的权值
public int getWeight(int v1, int v2) {
return metrics[v1][v2];
}
}