图的遍历
- 图的遍历是和树的遍历类似,我们希望从图中某一顶点出发访遍图中其余顶点,且使每一个顶点仅被访问一次,这一过程就叫做图的遍历(Traversing Graph)。
- 重复访问顶点就不叫做遍历了。
- 关于图的基本概念,理论知识不想说了。太繁琐~
- 直接上图,这个应该都能看懂。
- 左图是一个图,右图是根据图生成的矩阵。
[v0][v1]代表v0顶点到v1顶点的路径权重为10
可以看见右图,权重为0的都是顶点自己到自己,这根本就没有意义。
而无限大符号表示到达不了,比如[v0][v2]。可以看左图,v0只能到达v1和v5,到不了v2。
第一幅图的矩阵可以看到有9个顶点,组成二维数组。
我们可以先生成这个矩阵:
public class Graph {
private int vertexSize; // 顶点数量
private int[] vertexs; // 顶点数组
private int[][] matrix; // 包含所有顶点的数组
// 路径权重
// 0意味着顶点自己到自己,无意义
// MAX_WEIGHT也意味着到目的顶点不可达
private static final int MAX_WEIGHT = 1000;
public Graph(int vertextSize) {
this.vertexSize = vertextSize;
matrix = new int[vertextSize][vertextSize];
vertexs = new int[vertextSize];
for (int i = 0; i < vertextSize; i++) {
vertexs[i] = i;
}
}
public static void main(String[] args) {
Graph graph = new Graph(9);
// 顶点的矩阵设置
int[] a1 = new int[] { 0, 10, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, 11, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT };
int[] a2 = new int[] { 10, 0, 18, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, 16, MAX_WEIGHT, 12 };
int[] a3 = new int[] { MAX_WEIGHT, MAX_WEIGHT, 0, 22, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, 8 };
int[] a4 = new int[] { MAX_WEIGHT, MAX_WEIGHT, 22, 0, 20, MAX_WEIGHT, 24, 16, 21 };
//int[] a4 = new int[] { MAX_WEIGHT, MAX_WEIGHT, 22, 0, 20, MAX_WEIGHT, MAX_WEIGHT, 16, 21 };
int[] a5 = new int[] { MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, 20, 0, 26, MAX_WEIGHT, 7, MAX_WEIGHT };
int[] a6 = new int[] { 11, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, 26, 0, 17, MAX_WEIGHT, MAX_WEIGHT };
int[] a7 = new int[] { MAX_WEIGHT, 16, MAX_WEIGHT, 24, MAX_WEIGHT, 17, 0, 19, MAX_WEIGHT };
//int[] a7 = new int[] { MAX_WEIGHT, 16, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, 17, 0, 19, MAX_WEIGHT };
int[] a8 = new int[] { MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, 16, 7, MAX_WEIGHT, 19, 0, MAX_WEIGHT };
int[] a9 = new int[] { MAX_WEIGHT, 12, 8, 21, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, 0 };
graph.matrix[0] = a1;
graph.matrix[1] = a2;
graph.matrix[2] = a3;
graph.matrix[3] = a4;
graph.matrix[4] = a5;
graph.matrix[5] = a6;
graph.matrix[6] = a7;
graph.matrix[7] = a8;
graph.matrix[8] = a9;
}
}
- 可以看到我们用一个
matrix
二维数组存放所有顶点 -
0
表示自己到自己,MAX_WEIGHT
表示无限大 - 然后就是一些实例化和设置矩阵
- 有了这些,我们才能进行遍历
深度优先遍历
- 深度优先遍历(Depth First Search),也有称为深度优先搜索,简称为DFS。
- 遍历规则:不断的沿着顶点的深度方向遍历。顶点的深度方向是指它的邻接点方向。
- 它从图中某个顶点v出发,访问此顶点,然后从顶点的未被访问的邻接点出发深度优先遍历图,直至图中所有和v有路径相通的顶点都被访问到。
- 简单说,就是顶点将第一个邻接点当作左孩子,其它邻接点都当做右孩子。最后排成一棵树。
- 深度优先遍历是指先遍历到最深层次,然后再探索邻接点,接着又遍历最深层次。二叉树的先序遍历就是一种深度优先遍历。
======================================
- 思想与步骤:
- 看上图右图,我们先访问A,然后访问A的第一个邻接点B。接着访问B的第一个邻接点C。。。。最后访问到F,F想访问第一个邻接点A。但是A已经访问过了,只能访问F的下一个邻接点G。
- 这就是深度优先遍历的访问顺序。
- 在代码中,依照分析。获取某顶点的第一个邻接点和下一个临界点是经常使用的方法。访问过程中,我们要判断该顶点是否已访问过,这个也需要辅助变量。
private boolean[] isVisited = new boolean[vertextSize];
/**
* 获取指定顶点的第一个邻接点
*
* @param index
* 指定邻接点
* @return
*/
private int getFirstNeighbor(int index) {
for (int i = 0; i < vertexSize; i++) {
if (matrix[index][i] < MAX_WEIGHT && matrix[index][i] > 0) {
return i;
}
}
return -1;
}
/**
* 获取指定顶点的下一个邻接点
*
* @param v
* 指定的顶点
* @param index
* 从哪个邻接点开始
* @return
*/
private int getNextNeighbor(int v, int index) {
for (int i = index+1; i < vertexSize; i++) {
if (matrix[v][i] < MAX_WEIGHT && matrix[v][i] > 0) {
return i;
}
}
return -1;
}
核心代码很简单,经上述分析过后:
/**
* 图的深度优先遍历算法
*/
private void depthFirstSearch(int i) {
isVisited[i] = true;
int w = getFirstNeighbor(i);
while (w != -1) {
if (!isVisited[w]) {
// 需要遍历该顶点
System.out.println("访问到了:" + w + "顶点");
depthFirstSearch(w); // 进行深度遍历
}
w = getNextNeighbor(i, w); // 第一个相对于w的邻接点
}
}
- 传
0
进去,表示v0
。 - 设置
v0
已访问过,获取v0
的第一个邻接点。w != -1
说明有这个邻接点,然后对这个临界点进行判断。- 已访问,那就找下一个临界点
- 未访问,进行访问,然后对该邻接点进行深度优先遍历。
- 算法还是很简单的!
广度优先遍历
- 思想(感悟):
- 广度优先遍历表示把每一层都遍历完才能遍历下一层。
- 我们来思考:假设
v0
有3个邻接点,v1 v2 v3
。- 我们访问v0后,然后访问v1 v2 v3。完毕后我们要从v1开始遍历它的邻接点,接着从v2开始遍历它的邻接点,最后是从v3开始遍历它的邻接点。
- 也就是说,3个邻接点访问完后。我们要回过头逐个遍历它们的邻接点。这一点我觉得要用个容器把它们顺序存储下来。然后每次从容器首部取出一个顶点开始遍历。这里我想到
LinkedList
,因为它适合增删。而且这里不需要遍历集合。
- 整体步骤:
- 我们可以把第一个顶点放进集合,然后
while(!queue.isEmpty())
或while(queue.size() > 0)
都行。开始循环。 - 然后取出并删除集合中第一个顶点元素的第一个邻接点。对这个顶点进行访问,
- 如果该顶点未访问过,就访问!然后将该顶点放入集合。
- 如果该顶点已访问过,就找该顶点的下一个邻接点。
- 我们可以把第一个顶点放进集合,然后
核心代码:
/**
* 图的广度优先遍历算法
*/
private void boardFirstSearch(int i) {
LinkedList<Integer> queue = new LinkedList<>();
System.out.println("访问到了:" + i + "顶点");
isVisited[i] = true;
queue.add(i);
while (queue.size() > 0) {
int w = queue.removeFirst().intValue();
int n = getFirstNeighbor(w);
while (n != -1) {
if (!isVisited[n]) {
System.out.println("访问到了:" + n + "顶点");
isVisited[n] = true;
queue.add(n);
}
n = getNextNeighbor(w, n);
}
}
}
完整代码
复制即可运行
import java.util.LinkedList;
public class Graph {
private int vertexSize; // 顶点数量
private int[] vertexs; // 顶点数组
private int[][] matrix; // 包含所有顶点的数组
// 路径权重
// 0意味着顶点自己到自己,无意义
// MAX_WEIGHT也意味着到目的顶点不可达
private static final int MAX_WEIGHT = 1000;
private boolean[] isVisited; // 某顶点是否被访问过
public Graph(int vertextSize) {
this.vertexSize = vertextSize;
matrix = new int[vertextSize][vertextSize];
vertexs = new int[vertextSize];
for (int i = 0; i < vertextSize; i++) {
vertexs[i] = i;
}
isVisited = new boolean[vertextSize];
}
/**
* 获取指定顶点的第一个邻接点
*
* @param index
* 指定邻接点
* @return
*/
private int getFirstNeighbor(int index) {
for (int i = 0; i < vertexSize; i++) {
if (matrix[index][i] < MAX_WEIGHT && matrix[index][i] > 0) {
return i;
}
}
return -1;
}
/**
* 获取指定顶点的下一个邻接点
*
* @param v
* 指定的顶点
* @param index
* 从哪个邻接点开始
* @return
*/
private int getNextNeighbor(int v, int index) {
for (int i = index+1; i < vertexSize; i++) {
if (matrix[v][i] < MAX_WEIGHT && matrix[v][i] > 0) {
return i;
}
}
return -1;
}
/**
* 图的深度优先遍历算法
*/
private void depthFirstSearch(int i) {
isVisited[i] = true;
int w = getFirstNeighbor(i);
while (w != -1) {
if (!isVisited[w]) {
// 需要遍历该顶点
System.out.println("访问到了:" + w + "顶点");
depthFirstSearch(w); // 进行深度遍历
}
w = getNextNeighbor(i, w); // 第一个相对于w的邻接点
}
}
/**
* 图的广度优先遍历算法
*/
private void boardFirstSearch(int i) {
LinkedList<Integer> queue = new LinkedList<>();
System.out.println("访问到了:" + i + "顶点");
isVisited[i] = true;
queue.add(i);
while (queue.size() > 0) {
int w = queue.removeFirst().intValue();
int n = getFirstNeighbor(w);
while (n != -1) {
if (!isVisited[n]) {
System.out.println("访问到了:" + n + "顶点");
isVisited[n] = true;
queue.add(n);
}
n = getNextNeighbor(w, n);
}
}
}
public static void main(String[] args) {
Graph graph = new Graph(9);
// 顶点的矩阵设置
int[] a1 = new int[] { 0, 10, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, 11, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT };
int[] a2 = new int[] { 10, 0, 18, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, 16, MAX_WEIGHT, 12 };
int[] a3 = new int[] { MAX_WEIGHT, MAX_WEIGHT, 0, 22, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, 8 };
int[] a4 = new int[] { MAX_WEIGHT, MAX_WEIGHT, 22, 0, 20, MAX_WEIGHT, 24, 16, 21 };
//int[] a4 = new int[] { MAX_WEIGHT, MAX_WEIGHT, 22, 0, 20, MAX_WEIGHT, MAX_WEIGHT, 16, 21 };
int[] a5 = new int[] { MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, 20, 0, 26, MAX_WEIGHT, 7, MAX_WEIGHT };
int[] a6 = new int[] { 11, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, 26, 0, 17, MAX_WEIGHT, MAX_WEIGHT };
int[] a7 = new int[] { MAX_WEIGHT, 16, MAX_WEIGHT, 24, MAX_WEIGHT, 17, 0, 19, MAX_WEIGHT };
//int[] a7 = new int[] { MAX_WEIGHT, 16, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, 17, 0, 19, MAX_WEIGHT };
int[] a8 = new int[] { MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, 16, 7, MAX_WEIGHT, 19, 0, MAX_WEIGHT };
int[] a9 = new int[] { MAX_WEIGHT, 12, 8, 21, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, 0 };
graph.matrix[0] = a1;
graph.matrix[1] = a2;
graph.matrix[2] = a3;
graph.matrix[3] = a4;
graph.matrix[4] = a5;
graph.matrix[5] = a6;
graph.matrix[6] = a7;
graph.matrix[7] = a8;
graph.matrix[8] = a9;
graph.depthFirstSearch(0);
//graph.boardFirstSearch(0);
}
}
网友评论