0 基本介绍
-
为什么要有图?
无论是线性表还是树结构,局限于表示一个直接前驱和一个直接后继的关系(一对一/一对多),当我们需要表示多对多的关系时, 这里我们就用到了图
-
节点间的连接成为边,节点称为顶点,一个顶点到另一个顶点所经过的边叫路径,边有方向的叫有向图,边没有方向的叫无向图,边带权值的叫带权图也叫网
-
图的表示方式有两种:邻接矩阵/邻接表,分别使用二维数组/链表,简单理解:顶点3所表示的一维数组/单链表中,顶点5表示的元素/节点大小就是顶点3和5间边的权值,权值为1表示可直接连接,否则无法直连(通常不用0而使用极大值65535表示)
00000000000000000000000上图为邻接矩阵,下图为邻接表000000000000000000000000
1 深度优先遍历
- 首先创建图Graph类,属性包括邻接矩阵(二维数组)、顶点集合、边总数
- 传入以上参数即可构造图对象
- 编写对单个顶点的深度优先遍历方法dfs:传参为顶点在集合中的下标index,以及表示是否访问顶点的数组isVisited,方法体为打印index顶点并更新其为已访问,获取index的首个未访问过的邻接节点w,对w顶点递归调用dfs,方法结束
- 考虑到对单个顶点深度优先遍历,可能会有深度不够的情况,因此循环对所有顶点调用dfs
//封装调用dfs
//虽是深度优先遍历,但考虑邻接中断(深度不够),因此起点有序调用dfs
public void dfs() {
boolean[] isVisited = new boolean[vertexes.size()];
for (int i = 0; i < vertexes.size(); i++) {
if (!isVisited[i]) {
dfs(i,isVisited);
}
}
}
//深度优先遍历
//对当前节点的处理(打印+已读)+向邻接结点递归
public void dfs(int index, boolean[] isVisited) {
System.out.print(getValueByIndex(index) + "->");
isVisited[index] = true;
int w = getFirstNeighbor(index);
while (w != -1) {
if (isVisited[w]) {
w = getNextNeighbor(index, w);
} else {
dfs(w,isVisited);
}
}
}
2 广度优先遍历
- 沿用1、2步,编写单个顶点的广度优先遍历方法bfs:对index顶点打印且标记为已访问,遍历其所有未访问过的邻接节点,并打印和加入队列
- 取出队列头部的顶点,同样遍历其所有未访问过的邻接节点,并打印和加入队列
- 5和6步骤采用内外两层while循环实现
- 考虑到对单个顶点广度度优先遍历,可能会有广度不够的情况,因此循环对所有顶点调用dfs
//封装调用bfs
//虽是广度优先遍历,但考虑广度不够,因此起点有序调用dfs
public void bfs() {
boolean[] isVisited = new boolean[vertexes.size()];
for (int i = 0; i < vertexes.size(); i++) {
if (!isVisited[i]) {
bfs(i,isVisited);
}
}
}
//广度优先遍历
public void bfs(int index, boolean[] isVisited) {
int u = 0;
int w = 0;
System.out.print(getValueByIndex(index) + "->");
isVisited[index] = true;
LinkedList<Integer> queue = new LinkedList<>();
queue.addLast(index);
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 = getNextNeighbor(u, w);
}
}
}
3 代码实现
//图
public class Graph {
public static void main(String[] args) {
//测试
int n = 5;
Graph graph = new Graph(5);
String[] vertexValue = {"A", "B", "C", "D", "E"};
for (String s : vertexValue) {
graph.insertVertex(s);
}
graph.insertEdge(1,0,1);
graph.insertEdge(1,2,1);
graph.insertEdge(1,3,1);
graph.insertEdge(1,4,1);
graph.insertEdge(2,0,1);
graph.show();
graph.dfs(0,new boolean[5]);
// graph.bfs();
}
List<String> vertexes;
int[][] edges;
int edgesNum;
public Graph(int n) {
vertexes = new ArrayList<>(n);
edges = new int[n][n];
edgesNum = 0;
}
//封装调用bfs
//虽是广度优先遍历,但考虑广度不够,因此起点有序调用dfs
public void bfs() {
boolean[] isVisited = new boolean[vertexes.size()];
for (int i = 0; i < vertexes.size(); i++) {
if (!isVisited[i]) {
bfs(i,isVisited);
}
}
}
//广度优先遍历
public void bfs(int index, boolean[] isVisited) {
int u = 0;
int w = 0;
System.out.print(getValueByIndex(index) + "->");
isVisited[index] = true;
LinkedList<Integer> queue = new LinkedList<>();
queue.addLast(index);
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 = getNextNeighbor(u, w);
}
}
}
//封装调用dfs
//虽是深度优先遍历,但考虑邻接中断(深度不够),因此起点有序调用dfs
public void dfs() {
boolean[] isVisited = new boolean[vertexes.size()];
for (int i = 0; i < vertexes.size(); i++) {
if (!isVisited[i]) {
dfs(i,isVisited);
}
}
}
//深度优先遍历
//对当前节点的处理(打印+已读)+向邻接结点递归
public void dfs(int index, boolean[] isVisited) {
System.out.print(getValueByIndex(index) + "->");
isVisited[index] = true;
int w = getFirstNeighbor(index);
while (w != -1) {
if (isVisited[w]) {
w = getNextNeighbor(index, w);
} else {
dfs(w,isVisited);
}
}
}
//获取第一个邻接结点
public int getFirstNeighbor(int index) {
for (int i = 0; i < vertexes.size(); i++) {
if (edges[index][i] > 0) {
return i;
}
}
return -1;
}
//获取下一个邻接节点
public int getNextNeighbor(int v1, int v2) {
for (int i = v2+1; i < vertexes.size(); i++) {
if (edges[v1][i] > 0) {
return i;
}
}
return -1;
}
//插入节点
public void insertVertex(String vertex) {
vertexes.add(vertex);
}
//插入边
public void insertEdge(int v1, int v2, int weight) {
edges[v1][v2] = weight;
edges[v2][v1] = weight;
edgesNum++;
}
//返回节点数
public int getVertexNum() {
return vertexes.size();
}
//返回边个数
public int getEdgesNum() {
return edgesNum;
}
//返回节点
public String getValueByIndex(int index) {
return vertexes.get(index);
}
//返回边的权值
public int getWeight(int v1, int v2) {
return edges[v1][v2];
}
//显示图对应的矩阵
public void show() {
for (int[] edge : edges) {
System.out.println(Arrays.toString(edge));
}
}
}