图
文章目录
- 图
-
- 1. 图基本介绍
-
- 1.1 为什么要有图
- 1.2 图的举例说明
- 1.3 图的常用概念
- 2. 图的表示方式
-
- 3. 图的邻接矩阵存储方式的代码实现
- 4. 图的深度优先遍历
-
- 5. 图的广度优先遍历
-
1. 图基本介绍
1.1 为什么要有图
- 线性表局限于一个直接前驱和一个直接后继的关系。
- 树也只能有一个直接前驱也就是父节点。
- 当我们需要表示多对多的关系时, 这里我们就用到了图。
1.2 图的举例说明
- 图是一种数据结构,其中结点可以具有零个或多个相邻元素。两个结点之间的连接称为边。 结点也可以称为顶点。
28 图、图的存储、图的深度优先遍历和广度优先遍历图
1.3 图的常用概念
- 顶点
- 边
- 路径
- 无向图
28 图、图的存储、图的深度优先遍历和广度优先遍历图 - 有向图
- 带权图
28 图、图的存储、图的深度优先遍历和广度优先遍历图
2. 图的表示方式
- 图的表示方式有两种:二维数组表示(邻接矩阵);链表表示(邻接表)。
2.1 邻接矩阵
- 邻接矩阵是表示图形中顶点之间相邻关系的矩阵,对于 n 个顶点的图而言,矩阵的 row 和 col 表示的是 1…n 个点。
28 图、图的存储、图的深度优先遍历和广度优先遍历图
2.2 邻接表
- 邻接矩阵需要为每个顶点都分配 n 个边的空间,其实有很多边都是不存在,会造成空间的一定损失。
- 邻接表的实现只关心存在的边,不关心不存在的边。因此没有空间浪费,邻接表由数组+链表组成。
28 图、图的存储、图的深度优先遍历和广度优先遍历图
3. 图的邻接矩阵存储方式的代码实现
package com.guli.graph;
import java.util.ArrayList;
import java.util.Arrays;
public class GraphDemo {
public static void main(String[] args) {
String[] vertexs = {"A", "B", "C", "D", "E"};
// 创建图
Graph graph = new Graph(5);
// 给图中插入顶点
for (String vertex : vertexs) {
graph.insertVertex(vertex);
}
// 给图添加边 A-B A-C B-C B-D B-E
graph.insertEdge("A", "B", 1);
graph.insertEdge("A", "C", 1);
graph.insertEdge("B", "C", 1);
graph.insertEdge("B", "D", 1);
graph.insertEdge("B", "E", 1);
// 输出图的邻接矩阵
graph.showAdjacencyMatrix();
}
}
class Graph {
private ArrayList<String> vertexs;
private int[][] edges;
private int numOfEdges;
/**
* 初始化图
*
* @param numOfVertex 顶点个数
*/
public Graph(int numOfVertex) {
vertexs = new ArrayList<>(numOfVertex);
edges = new int[numOfVertex][numOfVertex];
numOfEdges = 0;
}
/**
* 插入顶点
*
* @param vertex 顶点
*/
public void insertVertex(String vertex) {
vertexs.add(vertex);
}
/**
* 插入边
*
* @param v1 顶点1
* @param v2 顶点2
* @param weight 两点之间的权重
*/
public void insertEdge(String v1, String v2, int weight) {
int index1 = vertexs.indexOf(v1);
int index2 = vertexs.indexOf(v2);
edges[index1][index2] = weight;
edges[index2][index1] = weight;
numOfEdges++;
}
/**
* 返回边的条数
*
* @return 边的条数
*/
public int getNumOfEdges() {
return numOfEdges;
}
/**
* 返回顶点的个数
*
* @return 顶点的个数
*/
public int getNumOfVertexs() {
return vertexs.size();
}
/**
* 输出图的邻接矩阵
*/
public void showAdjacencyMatrix() {
for (int[] edge : edges) {
System.out.println(Arrays.toString(edge));
}
}
}
4. 图的深度优先遍历
4.1 步骤
- 访问初始结点 v,并标记结点 v 为已访问。
- 查找结点 v 的第一个邻接结点 w。
- 若 w 存在,则继续执行 4,如果 w 不存在,则回到第 1 步,将从 v 的下一个结点继续。
- 若 w 未被访问,对 w 进行深度优先遍历递归(即把 w 当做另一个 v,然后进行步骤 123)。
- 查找结点 v 的 w 邻接结点的下一个邻接结点,转到步骤 3。
4.2 代码实现
/**
* 寻找第一个与当前顶点邻接的未被访问过的节点的下标
*
* @param vertex 当前节点
* @return 第一个与当前顶点邻接的未被访问过的节点的下标
*/
private int getFirstNeighbor(String vertex) {
// 得到当前节点的下标
int index = vertexs.indexOf(vertex);
// 寻找第一个与当前顶点邻接的未被访问过的节点的下标
for (int i = 0; i < edges[0].length; i++) {
if (edges[index][i] > 0 && !isVisited.contains(vertexs.get(i))) {
return i;
}
}
return -1;
}
/**
* 图的深度优先遍历
*
* @param startVertex 开始遍历的节点
*/
public ArrayList<String> dfs(String startVertex) {
isVisited = new ArrayList<>();
// 存放最终序列
ArrayList<String> seq = new ArrayList<>();
// 记录指向要获取邻接节点的节点的下标
int temp = 0;
seq.add(startVertex);
// 记录访问过的节点
isVisited.add(startVertex);
int num = 0;
// 得到序列
while (seq.size() < vertexs.size()) {
// 寻找第一个与当前顶点邻接的未被访问过的节点的下标,并将节点存放进最终序列中
if ((num = getFirstNeighbor(seq.get(temp))) != -1) {
seq.add(vertexs.get(num));
isVisited.add(vertexs.get(num));
temp = seq.size() - 1;
} else {
temp--;
}
}
return seq;
}
5. 图的广度优先遍历
5.1 步骤
- 访问初始结点 v 并标记结点 v 为已访问。
- 结点 v 入队列。
- 当队列非空时,继续执行,否则算法结束。
- 出队列,取得队头结点 u。
- 查找结点 u 的所有未被访问过的邻接结点,并入队列。
- 循环执行 456。
5.2 代码实现
/**
* 获得所有与当前顶点邻接且未被访问过的顶点
*
* @param vertex 当前顶点
* @return 所有与当前顶点邻接且未被访问过的顶点
*/
private ArrayList<String> getAllNeighbor(String vertex) {
ArrayList<String> list = new ArrayList<>();
int index = vertexs.indexOf(vertex);
for (int i = 0; i < edges.length; i++) {
if (edges[index][i] > 0 && !isVisited.contains(vertexs.get(i))) {
list.add(vertexs.get(i));
}
}
return list;
}
/**
* 图的广度优先遍历
*
* @param startVertex 开始遍历的节点
* @return 最终序列
*/
public ArrayList<String> bfs(String startVertex) {
isVisited = new ArrayList<>();
// 存放最终序列
ArrayList<String> seq = new ArrayList<>();
// 临时存放部分节点
Queue<String> queue = new LinkedList<>();
queue.add(startVertex);
isVisited.add(startVertex);
while (seq.size() < vertexs.size() && !queue.isEmpty()) {
String v = queue.remove();
seq.add(v);
queue.addAll(getAllNeighbor(v));
isVisited.addAll(getAllNeighbor(v));
}
return seq;
}