Graph
图由顶点(
vertex
/ˈvɜːrteks/)和边(edge
/edʒ/)组成的一种结构。
顶点的集合V
,边的集合是E
,所以图记为G =(V,E)
总结:
顶点是一维数组,而边是二维数组; 假如顶点个数是n,则一维数组的长度是n,二维数组长度n的平方
无向图
图中存在任意两个顶点之间的边都是没有方向的
有向图
图中存在任意两个顶点之间的边都是有方向的
有向完全图
任意两个顶点之间都存在方向互为相反的两条弧
图的权
有些图的边或弧具有与他相关的数字
连通图
从顶点v到顶点v‘存在路径
度
无向图顶点边的条数叫度,有向图顶点的边叫入度和出度
图的存储结构(邻接矩阵)
如果图G有n个顶点,则邻接矩阵是一个n*n的方阵
无向图的边数组(二维数组)是一个对称矩阵
带权的邻接矩阵
图的深度优先
遍历和广度优先
遍历算法
构建张图的矩阵信息, Graph.java
,代码如下:
package com.tangkun.graph;
/**
* 图
* 定义图的结构
*/
public class Graph {
/**
* 结点个数
*/
protected int size;
/**
* 定义数组,保存顶点信息
*/
protected String nodes[];
/**
* 定义矩阵,保存顶点信息
*/
protected int edges[][];
/**
* 首先将图转换成邻接矩阵,也就是任意两个顶点之间的边信息
*
* A B C D E F G
* A 0 0 1 1 0 1 0
* B 0 0 1 0 0 0 0
* C 1 1 0 1 0 0 0
* D 1 0 1 0 0 0 0
* E 0 0 0 0 0 0 1
* F 1 0 0 0 0 0 1
* G 0 0 0 0 1 1 0
*/
public Graph() {
//重新定义顶点大小,顶点数组,边的矩阵
nodes = new String[]{"A", "B", "C", "D", "E", "F", "G"};
size = nodes.length;
edges = new int[size][size];
//为每一个顶点定一个一个int型变量,方便在边的二维数组中表示
int A = 0;
int B = 1;
int C = 2;
int D = 3;
int E = 4;
int F = 5;
int G = 6;
//二维数组中第1个索引就表示上面矩阵中的纵坐标,二维数组中第2个索引就表示上面矩阵中的横坐标
edges[A][C] = 1;
edges[A][D] = 1;
edges[A][F] = 1;
edges[B][C] = 1;
edges[C][A] = 1;
edges[C][D] = 1;
edges[C][B] = 1;
edges[D][A] = 1;
edges[D][C] = 1;
edges[E][G] = 1;
edges[F][A] = 1;
edges[F][G] = 1;
edges[G][F] = 1;
edges[G][E] = 1;
}
}
深度优先和广度优先算法, GraphTraverse.java
,代码如下:
package com.tangkun.graph;
/**
* 图的遍历
*/
public class GraphTraverse extends Graph {
/**
* 定义一个数组,用于记录已经访问过的顶点
*/
private int visited[] = new int[size];
/**
* 图的深度优先遍历
*
* @param start 开始的结点索引
*/
private void deepFirst(int start) {
//将这个顶点存到visited数组中,并标记这个顶点已经被访问过
visited[start] = 1;
System.out.println("遍历到 " + nodes[start] + " 顶点");
//从start顶点开始,与顶点集合中的顶点进行循环比较,判断这两个顶点之间是否存在有边(判断edges[start][i]是否==1)
for (int i = 0; i < nodes.length; i++) {
//判断这两个顶点之间是否存在边,并且判断这个i顶点是否已经被访问过,访问过则无须再次遍历
if (edges[start][i] == 1 && visited[i] == 0) {
//如果这两个顶点之间存在一条边,则递归遍历这个新的顶点下面顶点
deepFirst(i);
}
}
}
/**
* 定义数组,根据根结点的长度分批次存储邻接结点,首先存储根结点,然后存储距离根结点路径为1的邻接结点,然后存储距离根结点路径为2的邻接结点。。。直至没有邻接结点
*/
private int queue[] = new int[size];
/**
* 图的广度优先遍历
* 广度优先搜索遍历图的过程中以v为起始点,由近至远,
* 依次访问和v有路径相通且路径长度为1,2,…的顶点
* 第一批节点的邻接点
*/
private void breadthFisrt(int front, int tail) {
//最后访问的邻接结点索引,初始值为第一批邻接结点的尾结点
int last = tail;
for (int index = front; index <= tail; index++) {
//打印当前遍历到的结点
int node = queue[index];
System.out.println("遍历到 " + nodes[node] + " 顶点");
//找出所有邻接结点
for (int i = 0; i < size; i++) {
if (edges[index][i] == 1 && visited[i] == 0) {
//标记该邻接结点已访问
visited[i] = 1;
//将遍历到的第二批邻接结点存起来
queue[++last] = i;
}
}
}
//如果上面遍历出来的第二批邻接结点个数大于0,递归遍历第二批邻接结点的下一批结点
if (last > tail) {
//递归遍历第二批邻接结点的下一批邻接结点
breadthFisrt(tail + 1, last);
}
}
private void breadthFirst(int start) {
queue[0] = start;
visited[start] = 1;
breadthFisrt(0, 0);
}
public static void main(String[] args) {
GraphTraverse graphTraverse = new GraphTraverse();
System.out.println("图的深度优先遍历");
graphTraverse.deepFirst(0);
System.out.println();
GraphTraverse graphTraverse2 = new GraphTraverse();
System.out.println("图的广度度优先遍历");
graphTraverse2.breadthFirst(0);
}
}
打印日志:
> Task :javalib:GraphTraverse.main()
图的深度优先遍历
遍历到 A 顶点
遍历到 C 顶点
遍历到 B 顶点
遍历到 D 顶点
遍历到 F 顶点
遍历到 G 顶点
遍历到 E 顶点
图的广度度优先遍历
遍历到 A 顶点
遍历到 C 顶点
遍历到 D 顶点
遍历到 F 顶点
遍历到 B 顶点
遍历到 G 顶点