6.1 什么是图
6.1.1 定义
图的定义:表示多对多的关系
抽象数据类型定义
怎么在程序中表示一个图:邻接矩阵和邻接表。
6.1.2 邻接矩阵表示法
定义
邻接矩阵的优点:
邻接矩阵的缺点:
C 语言实现
#define MaxVertexNum 100 /* 最大顶点数设为100 */
#define INFINITY 65535 /* ∞设为双字节无符号整数的最大值65535*/
typedef int Vertex;
typedef int WeightType;
typedef char DataType;
typedef struct ENode *PtrToENode;
struct ENode{
Vertex V1, V2;
WeightType Weight;
};
typedef PtrToENode Edge;
typedef struct GNode *PtrToGNode;
struct GNode{
int Nv;
int Ne;
WeightType G[MaxVertexNum][MaxVertexNum];
DataType Data[MaxVertexNum];
};
typedef PtrToGNode MGraph;
MGraph CreateGraph(
int VertexNum )
{
Vertex V, W;
MGraph Graph;
Graph = (MGraph)
malloc(
sizeof(
struct GNode));
Graph->Nv = VertexNum;
Graph->Ne =
0;
for (V=
0; V<Graph->Nv; V++)
for (W=
0; W<Graph->Nv; W++)
Graph->G[V][W] = INFINITY;
return Graph;
}
void InsertEdge( MGraph Graph, Edge E )
{
Graph->G[E->V1][E->V2] = E->Weight;
Graph->G[E->V2][E->V1] = E->Weight;
}
MGraph BuildGraph()
{
MGraph Graph;
Edge E;
Vertex V;
int Nv, i;
scanf(
"%d", &Nv);
Graph = CreateGraph(Nv);
scanf(
"%d", &(Graph->Ne));
if ( Graph->Ne !=
0 ) {
E = (Edge)
malloc(
sizeof(
struct ENode));
for (i=
0; i<Graph->Ne; i++) {
scanf(
"%d %d %d", &E->V1, &E->V2, &E->Weight);
InsertEdge( Graph, E );
}
}
for (V=
0; V<Graph->Nv; V++)
scanf(
" %c", &(Graph->Data[V]));
return Graph;
}
6.1.3 邻接表表示法
定义
C 语言实现
#define MaxVertexNum 100 /* 最大顶点数设为100 */
typedef int Vertex;
typedef int WeightType;
typedef char DataType;
typedef struct ENode *PtrToENode;
struct ENode{
Vertex V1, V2;
WeightType Weight;
};
typedef PtrToENode Edge;
typedef struct AdjVNode *PtrToAdjVNode;
struct AdjVNode{
Vertex AdjV;
WeightType Weight;
PtrToAdjVNode Next;
};
typedef struct Vnode{
PtrToAdjVNode FirstEdge;
DataType Data;
} AdjList[MaxVertexNum];
typedef struct GNode *PtrToGNode;
struct GNode{
int Nv;
int Ne;
AdjList G;
};
typedef PtrToGNode LGraph;
LGraph CreateGraph(
int VertexNum )
{
Vertex V;
LGraph Graph;
Graph = (LGraph)
malloc(
sizeof(
struct GNode) );
Graph->Nv = VertexNum;
Graph->Ne =
0;
for (V=
0; V<Graph->Nv; V++)
Graph->G[V].FirstEdge = NULL;
return Graph;
}
void InsertEdge( LGraph Graph, Edge E )
{
PtrToAdjVNode NewNode;
NewNode = (PtrToAdjVNode)
malloc(
sizeof(
struct AdjVNode));
NewNode->AdjV = E->V2;
NewNode->Weight = E->Weight;
NewNode->Next = Graph->G[E->V1].FirstEdge;
Graph->G[E->V1].FirstEdge = NewNode;
NewNode = (PtrToAdjVNode)
malloc(
sizeof(
struct AdjVNode));
NewNode->AdjV = E->V1;
NewNode->Weight = E->Weight;
NewNode->Next = Graph->G[E->V2].FirstEdge;
Graph->G[E->V2].FirstEdge = NewNode;
}
LGraph BuildGraph()
{
LGraph Graph;
Edge E;
Vertex V;
int Nv, i;
scanf(
"%d", &Nv);
Graph = CreateGraph(Nv);
scanf(
"%d", &(Graph->Ne));
if ( Graph->Ne !=
0 ) {
E = (Edge)
malloc(
sizeof(
struct ENode) );
for (i=
0; i<Graph->Ne; i++) {
scanf(
"%d %d %d", &E->V1, &E->V2, &E->Weight);
InsertEdge( Graph, E );
}
}
for (V=
0; V<Graph->Nv; V++)
scanf(
" %c", &(Graph->G[V].Data));
return Graph;
}
6.2 图的遍历
6.2.1 图的遍历-深度优先搜索 DFS
思路:访问一个顶点的每个邻接点,然后再原路返回。DFS 类似于树的先序遍历。
C语言实现:DFS 邻接表存储
void Visit( Vertex V )
{
printf(
"正在访问顶点%d\n", V);
}
void DFS( LGraph Graph, Vertex V,
void (*Visit)(Vertex) )
{
PtrToAdjVNode W;
Visit( V );
Visited[V] =
true;
for( W=Graph->G[V].FirstEdge; W; W=W->Next )
if ( !Visited[W->AdjV] )
DFS( Graph, W->AdjV, Visit );
}
6.2.2 图的遍历-广度优先搜索 BFS
思路:相当于树里面的层次遍历。程序实现的时候借助了队列。
C语言实现:BFS 邻接矩阵存储
bool IsEdge( MGraph Graph, Vertex V, Vertex W )
{
return Graph->G[V][W]<INFINITY ?
true :
false;
}
void BFS ( MGraph Graph, Vertex S,
void (*Visit)(Vertex) )
{
Queue Q;
Vertex V, W;
Q = CreateQueue( MaxSize );
Visit( S );
Visited[S] =
true;
AddQ(Q, S);
while ( !IsEmpty(Q) ) {
V = DeleteQ(Q);
for( W=
0; W<Graph->Nv; W++ )
if ( !Visited[W] && IsEdge(Graph, V, W) ) {
Visit( W );
Visited[W] =
true;
AddQ(Q, W);
}
}
}
6.2.3 为什么需要两种遍历
遍历就是把图中的每一个顶点访问一次,但是两种遍历各有特点与应用场景。
BFS:这是一种基于队列这种数据结构的搜索方式,它的特点是由每一个状态可以扩展出许多状态,然后再以此扩展,直到找到目标状态或者队列中头尾指针相遇,即队列中所有状态都已处理完毕。
DFS:基于递归的搜索方式,它的特点是由一个状态拓展一个状态,然后不停拓展,直到找到目标或者无法继续拓展结束一个状态的递归。
优缺点:
BFS:对于解决最短或最少问题特别有效,而且寻找深度小,但缺点是内存耗费量大(需要开大量的数组单元用来存储状态)。 DFS:对于解决遍历和求所有问题有效,对于问题搜索深度小的时候处理速度迅速,然而在深度很大的情况下效率不高
总结:不管是BFS还是DFS,它们虽然好用,但由于时间和空间的局限性,以至于它们只能解决数据量小的问题。
例子:
走迷宫问题,假设黑格子是走不通的路。从左上角为起点,顺时针方向走。小人是走的轨迹。
DFS:
BFS:
6.2.4 图的遍历-图的连通