6.2图的存储及基本操作
6.2.1邻接矩阵法
图的邻接矩阵存储结构定义如下:
#define MaxVertexNUm 100 //顶点数目的最大值
typedef char VertexType; //顶点的数据类型
typedef int EdgeType; //带权图中边上权值的数据类型
typedef struct{
VertexType Ver[MaxVertexNum]; //顶点表
EdgeType Edge[MaxVertexNum][MaxVertexNum]; //邻接矩阵,边表
int vexnum,arcnum; //图的当前顶点数和弧数
}MGraph;
6.2.2邻接表法
图的邻接表存储结构定义如下:
#define MaxVertexNum 100 //图中顶点数目的最大值
typedef struct ArcNode{ //边表结点
int adjvex; //该弧所指向的顶点的位置
struct ArcNode *next; //指向下一条弧的指针
//InfoType info; //网的边权值
}ArcNode;
typedef struct VNode{ //顶点表结点
VertexType data; //顶点信息
ArcNode *first; //指向第一条依附该结点的弧的指针
}VNode,AdjList[MaxVertexNum];
typedef struct{
AdjList vertices; //邻接表
int vexnum,arcnum; //图的顶点数和弧数
}ALGraph; //ALGraph是以邻接表存储的图类型
6.2.3十字链表
6.2.4邻接多重表
6.2.5图存储小结
6.3图的遍历
6.3.1广度优先搜索(BFS)
1.广度优先搜索算法的伪代码如下:
bool visited[MAX_VERTEX_NUM]; //访问标记数组
void BFSTraverse(Graph G){ //对图G进行广度优先遍历
for(i=0;i<G.vexnum;++i)
visited[i]=FALSE; //访问标记数组初始化
InitQueue(Q); //初始化辅助队列Q
for(i=0;i<G.vexnum;++i) //从0号顶点开始遍历
if(!visited[i]) //对每个连通分量调用一次BFS
BFS(G,i); //Vi未访问过,从Vi开始BFS
}
void BFS(Graph G,int v){ //从顶点v出发,广度优先遍历图G
visit(v); //访问初始顶点v
visited[v]=TRUE; //对v做已访问标记
EnQueue(Q,v); //顶点v入队列Q
while(!isEmpty(Q)){
DeQueue(Q,v); //顶点v出队列
for(w=FirstNeighbor(G,v);w>=0;w=NextNeighbor(G,v,w))
//检测v所有邻接点
if(!visited[w]){ //w为v的尚未访问的邻接顶点
visit(w); //访问顶点w
visited[w]=TRUE; //对w做已访问标记
EnQueue(Q,w); //顶点w入队列
}
}
}
2.BFS算法求解单源最短路径问题的算法如下:
void BFS_MIN_Distance(Graph G,int u){
for(i=0;i<G.vexnum;++i)
d[i]=∞; //初始化路径长度
visited[u]=TRUE; d[u]=0;
EnQueue(Q,u);
while(!isEmpty(Q)){ //BFS算法主过程
DeQueue(Q,u); //对头元素u出队
for(w=FirstNeighbor(G,u);w>=0;w=NextNeighbor(G,u,w))
if(!visited[w]){ //w为u的尚未访问的邻接顶点
visited[w]=TRUE; //设已访问标记
d[w]=d[u]+1; //路径长度加1
EnQueue(Q,w); //顶点w入队
}
}
}
6.3.2深度优先搜索(DFS)
1.用递归进行深度优先搜索算法过程如下:
bool visited[MAX_VERTEX_NUM]; //访问标记数组
void DFSTraverse(Graph G){ //对图G进行深度优先遍历
for(v=0;v<G.vexnum;++v)
visited[v]=FALSE; //初始化已访问标记数组
for(v=0;v<G.vexnum;++v) //本代码中是从v=0开始遍历
if(!visited[v])
DFS(G,v);
}
void DFS(Graph G,int v){ //从顶点v出发,深度优先遍历图G
visit(v); //访问顶点v
visited[v]=TRUE; //设已访问标记
for(w=FirstNeighbor(G,v);w>=0;w=NextNeighbor(G,v,w))
if(!visited[w]){ //w为v的尚未访问的邻接顶点
DFS(G,w);
}
}
6.4图的应用
6.4.1最小生成树
1.通用的最小生成树算法如下:
GENERIC_MST(G){
T=NULL;
while T 未形成一棵生成树;
do 找到一条最小代价边(u,v)并且加入T后不会产生回路;
T=T∪(u,v);
}
2.Prim算法
Prim算法的简单实现如下:
void Prim(G,T){
T=∅; //初始化空树
U={w}; //添加任意一个顶点w
while((V-U)!=∅){ //若树中不含全部顶点
设(u,v)是使u∈U与v∈(V-U),且权值最小的边;
T=T∪{(u,v)}; //边归入树
U=U∪{v}; //顶点归入树
}
}
3.Kruskal算法
Kruskal算法的步骤如下:
void Kruskal(V,T){
T=V; //初始化树T,仅含顶点
numS=n; //连通分量数
while(numS>1){ //若连通分量数大于1
从E中取出权值最小的边(v,u);
if{v和u属于T中不同的连通分量){
T=T∪{(v,u)}; //将此边加入生成树中
numS--; //连通分量数减1
}
}
}
6.4.2最短路径
1.Dijkstra算法求单源最短路径问题
2.Floyd算法求各顶点之间最短路径问题
Floyd算法实现如下:
//......准备工作,根据图的信息初始化矩阵A和path(如上图)
for(int k=0;k<n;k++){ //考虑以Vk作为中转点
for(int i=0;i<n;i++){ //遍历整个矩阵,i为行号,j为列号
for(int j=0;j<n;j++){
if(A[i][j]>A[i][k]+A[k][j]){ //以Vk为中转点的路径更短
A[i][j]=A[i][k]+A[k][j]; //更新最短路径长度
path[i][j]=k; //中转点
}
}
}
}
6.4.3求最短路径小结
6.4.4有向无环图描述表达式
6.4.5拓扑排序
拓扑排序算法的实现如下:
bool TopologicalSort(Graph G){
InitStack(S); //初始化栈,存储入度为0的顶点
int i;
for(i=0;i<G.vexnum;i++)
if(indegree[i]==0)
Push(S,i); //将所有入度为0的顶点进栈
int count=0; //计数,记录当前已经输出的顶点数
while(!isEmpty(S)){ //栈不空,则存在入度为0的顶点
Pop(S,i); //栈顶元素出栈
print[count++]=i; //输出顶点i
for(p=G.vertices[i].firstarc;p;p=p->nextarc){
//将所有i指向的顶点的入度减1,并且将入度减为0的顶点压入栈S
v=p->adjvex;
if(!(--indegree[v]))
Push(S,v); //入度为0,则入栈
}
}//while
if(count<G.vexnum)
return false; //排序失败,有向无环图中有回路
else
return true; //拓扑排序成功
}