十三、图(Graph)
1、概念
二叉树最多有两个子节点,节点存在父子关系,当我们想表示更多的关联关系时,就需要使用图来表示。
一个图由多个顶点
(vertex)组成,每两个相连顶点间的通路被称之为边
(edge)。
从某个顶点到另外一个顶点所经过的所有顶点,被称之为路径。
无向图:顶点和顶点间的连接没有方向。即A-B,也可以是B-A。
A***B
**
* *
* *
C***D
有向图:顶点和顶点间的连接有方向。即只能是A-B,不能是B-A。
A-->B
↓↘
C-->D
带权图(网):图的边上有权值。
A--3--B
| \
2 9
| \
C--7--D
2、表示形式
A + + C
+ + +
+ + +
B + + D E
一种是使用**二维数组(邻接矩阵)**的形式。
图有n个顶点,矩阵(正方形)大小就是n × n
。使用既定的值来表示直接连通(例如1)和直接不通(例如0)。很直观,但是浪费内存空间。
A B C D E
A 0 1 1 1 0
B 1 0 0 1 0
C 1 0 0 0 1
D 1 1 0 0 0
E 0 0 1 0 0
另一种是使用一维数组+链表的形式。数组按照顺序存储了每个顶点,每个顶点都有其对应的链表,记录了它所直接连通的其它顶点。没有浪费内存空间,但不是很直观。
A B C D
B A D
C A E
D A B
E C
3、图的遍历
深度优先
(Depth First Search):从初始查找顶点开始,访问它的第一个邻接顶点,然后再查找这个邻接顶点的邻接顶点,直到所有的顶点都查找到。这是一个递归的过程。
- 先访问顶点V1,并标记为已遍历。
- 再访问顶点V1的第一个邻接顶点V2。
- 如果V2存在,且未被遍历,则对V2进行DFS,否则就继续遍历V1的下一个邻接顶点。
广度优先
(Broad First Search):从初始查找顶点开始,依次查找它的所有邻接顶点,然后再查找初始查找顶点的邻接顶点的邻接顶点。需要通过一个队列来记录顶点查找的次序。
- 先访问顶点V1,并标记为已遍历。
- 将顶点V1入队列。
- 当队列非空时,继续。
- 获取队列的头节点,并从队列中移除,然后查找头节点的邻接顶点V2。
- 如果V2存在,则遍历它,并标记为已遍历。
- 将该顶点入队列。
- 然后查找V1的下一个邻接顶点V3。
- 重复执行步骤4
4、示例
二维数组(邻接矩阵)
class Graph<T> {
private T[] vertexArray;
private final int vertexCount;
private int[][] edgeArray;
private int edgeCount;
private static final int EDGE_FLAG = 1;
public Graph(T[] vertexArray) {
this.vertexArray = vertexArray;
this.vertexCount = vertexArray.length;
this.edgeArray = new int[vertexCount][vertexCount];
this.edgeCount = 0;
}
public void setEdge(T vertex1, T vertex2) {
int x = findIndex(vertex1);
int y = findIndex(vertex2);
edgeArray[x][y] = EDGE_FLAG;
edgeArray[y][x] = EDGE_FLAG;
this.edgeCount++;
}
private int findIndex(T vertex) {
for (int i = 0; i < vertexCount; i++) {
if (vertexArray[i].equals(vertex)) {
return i;
}
}
return -1;
}
public int getEdgeCount() {
return this.edgeCount;
}
public void printGraph() {
System.out.print(" ");
for (int i = 0; i < vertexCount; i++) {
System.out.print(vertexArray[i] + " ");
}
System.out.println();
for (int j = 0; j < vertexCount; j++) {
System.out.print(vertexArray[j] + " ");
for (int k = 0; k < vertexCount; k++) {
System.out.print(edgeArray[j][k] + " ");
}
System.out.println();
}
}
public void dfs() {
boolean[] searchArray = new boolean[vertexCount];
for (int i = 0; i < vertexCount; ) {
if (!searchArray[i