图数据结构的定义
无向图
无向图的特点
邻接矩阵是对称的
有向图
图的存储
邻接矩阵存储方式
如下图所示,二维矩阵就可以实现图的存储,矩阵中的情况表示为行表示顶点V0与其他顶点的连接情况,若连接则为1,否则为0.
邻接表存储方式
一维数组存储每一个顶点,实现从数组中每一个顶点开始伸出一个链表表示此顶点与之相连的其他顶点
十字链表存储方式
- 十字链表的出现是为了弥补有向图在邻接表不足:不方便计算顶点的入度,需要增加一个逆邻接表。
- 方法:重新组合邻接表和逆邻接表的方法——十字链表
①重新定义顶点表结点结构
②重新定义边表结点结构
③生成的十字链表
十字链表的解释
①tailVex表示指向当前顶点的前一个顶点;
②headVex表示当前顶点
③headLink表示指向当前节点的其他顶点
④tailLink表示前一个顶点指向的下一个顶点
headLink比较复杂,主要是用来表明除了前一个顶点指向当前顶点外,其他指向当前顶点的顶点。
图数据结构的创建
class Graph{
static final int MaxNum=20;
static final int MaxValue=65535;
char[] Vertex = new char[MaxNum];
int GType;
int VertexNum;
int EdgeNum;
int[][] EdgeWeight = new int[][];
int[] isTrav = new int[MaxNum];
}
深度优先遍历
static void DFS(Graph g){
int i;
for(i = 0; i < g.VertexNum; i++){g.isTrav[i] = 0;}
System.out.println("深度优先遍历:");
for(i = 0; i < g.VertexNum; i++){ //遍历每一个顶点,实现从每一个顶点开始的深度优先遍历
if(g.isTrav[i] == 0) DFSTrav(g, i);
}
}
static void DFSTrav(Graph g, int n){ //从某一个顶点出发,实现深度优先遍历
int i;
g.isTrav[n] = 1;
System.out.println("->"+g.Vertex[n]);
//已知可以循环多少次,所以用for循环,如果不知道就用while
for(i = 0; i < g.VertexNum; i++){
if(g.EdgeWeight[n][i] != g.MaxValue && g.isTrav[i] == 0) DFSTrav(g, i);
}
}
广度优先遍历
static void BFS(Graph g){
Queue<Integer> queue = new LinkedList<Integer>(g.VertexNum);
int i,j;
for(i = 0; i < g.VertexNum; i++){ //初始化顶点的访问情况
g.isTrav[i] = 0;
}
for(i = 0; i < g.VertexNum; i++){ //遍历每一个顶点,实现从每一个顶点出发的广度优先遍历
g.isTrav[i] = 1;
System.out.println("->"+g.Vertex[i]);
queue.offer(i);
while(!queue.isEmpty()){ //队列空了才会跳出循环,意味着从某个顶点出发的广度优先遍历已经结束了,下次循环意味着要从另外一个顶点出发
vertex = queue.poll();
for(j = 0; j < g.VertexNum; j++){
if(g.EdgeWeight[i][j] != g.MaxValue && g.isTrav[i] == 0){
System.out.println(g.Vertex[i]);
g.isTrav[i] = 1;
queue.offer(i);
}
}
}
}
}
参考文献:
[1] 图的深度优先遍历和广度优先遍历(Python)(简单易懂,很好理解,可以对比java和Python的却别)
[2] 《图论》——图的存储与遍历(Java)(非常详细的文字解释和代码叙述,可以直接使用代码进行处理)
[3] 图的遍历之 深度优先搜索和广度优先搜索(图文结合很好理解,推荐阅读)
[4] 图的存储结构(十字链表、邻接多重表、边集数组)