1. 图的相关术语
-
阶(Order)
图的顶点数。 -
子图(Sub-Graph)
如果一个图的顶点集包含于另一个图的顶点集,并且边集也包含于另一个图的边集,那么这个图就是另一个图的子图。 -
度(Degree)
与这个顶点关联的边的条数。 -
入度(In-Degree)和出度(Out-Degree)
所有邻接到这个顶点的边的个数叫入度;所有邻接于这个顶点的边的个数叫出度。 -
自环(Loop)
一条边的两个顶点是一个节点。 -
路径(Path)
-
桥(Bridge)
如果去掉一条边使整个图不连通,那么这条边叫做桥。
2. 图的分类
图分为无向图(Undirected Graph)和有向图(Directed Graph)。
3. 图的表示与存储方式
3.1 邻接矩阵
/**
* 邻接矩阵表示法
*/
public class MatrixGraph {
// 下标与实际值的映射
private int[] mapping;
//图的二维数组
private int[][] matrix;
/**
* 初始化图
*/
public MatrixGraph(int[] vertexes) {
int length = vertexes.length;
mapping = new int[length];
matrix = new int[length][length];
for (int i = 0; i < length; i++) {
mapping[i] = vertexes[i];
}
}
public void addEdge(int start, int end) {
int x = -1;
int y = -1;
//寻找坐标
for (int i = 0; i < mapping.length; i++) {
if (x != -1 && y != -1) {
//上一轮已给x y赋值
break;
}
if (start == mapping[i]) {
x = i;
}
if (end == mapping[i]) {
y = i;
}
}
//判断顶点是否存在
if (x == -1 || y == -1 || x > mapping.length - 1 || y > mapping.length - 1) {
return;
}
//赋值
matrix[x][y] = 1;
}
public void printMatrix() {
for (int i = 0; i < matrix.length; i++) {
for (int i1 = 0; i1 < matrix[i].length; i1++) {
System.out.print(matrix[i][i1]);
System.out.print(" ");
}
System.out.println();
}
}
/**
* DFS 深度优先遍历
* 使用栈的方式
*/
public void depthFirstTravel() {
Stack<Integer> stack = new Stack();
//初始化访问状态数组
int[] visited = new int[mapping.length];
//从未访问的顶点中选择一个作为起始顶点
int unvisited = getUnvisited(visited);
while (unvisited >= 0) {
//标记起始顶点已被访问
visited[unvisited] = 1;
//入栈
stack.push(unvisited);
System.out.print(mapping[unvisited] + ",");
while (!stack.isEmpty()) {
//获取栈顶元素,并不出栈
int index = stack.peek();
//遍历矩阵此行,找到未被访问的邻接节点
boolean found = false;
for (int i = 0; i < mapping.length; i++) {
if (index != i && visited[index] == 0 && matrix[index][i] > 0) {
//非自己 未被访问 邻接顶点
visited[i] = 1;
stack.push(i);
System.out.print(mapping[i]+",");
found = true;
break;
}
}
if (!found){
stack.pop();
}
}
unvisited = getUnvisited(visited);
}
}
/**
* BFS 广度优先遍历
*
*/
public void breadthFirstTravel() {
Queue<Integer> queue = new LinkedList<>();
int[] visited = new int[mapping.length];
int unvisited = getUnvisited(visited);
while (unvisited >= 0) {
queue.add(unvisited);
while (!queue.isEmpty()) {
//顶点出队
int index = queue.poll();
if (visited[index] == 1) {
//访问过了,继续
continue;
}
System.out.print(mapping[index] + ",");
visited[index] = 1;
for (int i = 0; i < mapping.length; i++) {
//非自己 未被访问 可到达
if (index != i && visited[i] == 0 && matrix[index][i] > 0) {
queue.add(i);
}
}
}
unvisited = getUnvisited(visited);
}
System.out.println();
}
private int getUnvisited(int[] visited) {
int index = -1;
for (int i = 0; i < visited.length; i++) {
if (visited[i] == 0) {
index = i;
break;
}
}
return index;
}
}
测试用例
public class MatrixGraphTest {
MatrixGraph graph;
@Before
public void setup() {
int[] vertexes = {1, 2, 3, 4};
graph = new MatrixGraph(vertexes);
graph.addEdge(1, 2);
graph.addEdge(1, 3);
graph.addEdge(1, 4);
graph.addEdge(2, 3);
graph.addEdge(4, 3);
}
@Test
public void main() {
graph.printMatrix();
}
@Test
public void dfs(){
graph.depthFirstTravel();
}
@Test
public void bfs(){
graph.breadthFirstTravel();
}
}
3.2 邻接表
类似散列表,同样有一个数组,每个元素为一个链表。 数组的每个元素是顶点,也是链表的头结点,而链表的每个元素是这个顶点邻接的顶点。
(待补充)
4. 深度优先遍历与广度优先遍历
4.1 时间复杂度
深度优先遍历:
邻接矩阵表示法为O(n^2)
,邻接表表示法为O(n+e)
。
广度优先遍历:
与DFS没有区别,邻接矩阵表示法为O(n^2)
,邻接表表示法为O(n+e)