图是由顶点集合V和边集合E组成的数据结构,记为G=(V,E),其中:
#define MAX_VERTEX_NUM 20
typedef struct {
VertexData vertex[MAX_VERTEX_NUM]; // 顶点数组
int arcs[MAX_VERTEX_NUM][MAX_VERTEX_NUM]; // 邻接矩阵
int vexnum, arcnum; // 顶点数和边数
GraphKind kind; // 图的类型
} AdjMatrix;
优点:
缺点:
// 判断顶点v1和v2是否邻接
int IsAdj(AdjMatrix G, VertexData v1, VertexData v2) {
int i, j;
i = LocateVertex(&G, v1);
j = LocateVertex(&G, v2);
return (G.arcs[i][j].adj == 1);
}
// 求顶点v的度
int VexDegree(AdjMatrix G, VertexData v) {
int k = LocateVertex(&G, v);
int degree = 0;
for(int j = 0; j < G.vexnum; j++)
degree += G.arcs[k][j];
return degree;
}
typedef struct ArcNode {
int adjvex; // 邻接点位置
struct ArcNode *nextarc; // 下一条边
OtherInfo info; // 边的其他信息
} ArcNode;
typedef struct VertexNode {
VertexData data; // 顶点数据
ArcNode *firstarc; // 第一条边
} VertexNode;
typedef struct {
VertexNode vertex[MAX_VERTEX_NUM];
int vexnum, arcnum;
GraphKind kind;
} AdjList;
优点:
缺点:
int CreateUDG(AdjList *G) {
scanf(&G->vexnum, &G->arcnum);
for(i=0; i<G->vexnum; i++) {
scanf(&G->vertex[i].data);
G->vertex[i].firstarc = NULL;
}
for(k=0; k<G->arcnum; k++) {
scanf("%c%c", &v1, &v2);
i = LocateVex(G, v1);
j = LocateVex(G, v2);
// 申请边结点,连接到相应的链条
p = (arcnode*)malloc(sizeof(arcnode));
p->adjvex = j;
p->nextarc = G->vertex[i].firstarc;
G->vertex[i].firstarc = p;
p = (arcnode*)malloc(sizeof(arcnode));
p->adjvex = i;
p->nextarc = G->vertex[j].firstarc;
G->vertex[j].firstarc = p;
}
}
从图中某一顶点v0出发,访问此顶点,然后依次从v0的未被访问的邻接点出发深度优先搜索,直至图中所有和v0有路径相通的顶点都被访问到。
void DFS(Graph g, int v0) {
visit(v0);
visited[v0] = true;
for(w = FirstNeighbor(g, v0); w >= 0; w = NextNeighbor(g, v0, w)) {
if(!visited[w])
DFS(g, w);
}
}
void TraverseGraph(Graph g) {
for(vi = 0; vi < g.vexnum; vi++)
visited[vi] = false;
for(vi = 0; vi < g.vexnum; vi++)
if(!visited[vi])
DFS(g, vi);
}
void DepthFirstSearch(Graph g, int v0) {
InitStack(S);
Push(S, v0);
while(!Empty(S)) {
v = Pop(S);
if(!visited[v]) {
visit(v);
visited[v] = true;
w = FirstAdjVertex(g, v);
while(w != -1) {
if(!visited[w])
Push(S, w);
w = NextAdjVertex(g, v, w);
}
}
}
}
void DepthFirstSearch(AdjMatrix g, int v0) {
visit(v0);
visited[v0] = true;
for(vj = 0; vj < g.vexnum; vj++)
if(g.arcs[v0][vj].adj == 1)
DepthFirstSearch(g, vj);
}
从图中某顶点v出发,访问v之后依次访问v的各个未曾访问过的邻接点,然后分别从这些邻接点出发依次访问它们的各个未被访问的邻接点。
void BreadthFirstSearch(Graph g, int v0) {
visit(v0);
visited[v0] = true;
InitQueue(&Q);
EnterQueue(&Q, v0);
while(!IsEmpty(Q)) {
DeQueue(&Q, &v);
w = FirstNeighbor(g, v);
while(w >= 0) {
if(!visited[w]) {
visit(w);
visited[w] = true;
EnterQueue(&Q, w);
}
w = NextNeighbor(g, v, w);
}
}
}
时间复杂度:
应用:
最小生成树(MST):在连通网的所有生成树中,各边权值之和最小的生成树。
基本思想:
设N=(V,E)是连通网,TE为最小生成树中边的集合。
算法实现:
void MiniSpanTree_Prim(AdjMatrix *gn, int u) {
for(i=0; i<gn->vexnum; i++) {
closeedge[i].adjvex = u;
closeedge[i].lowcost = gn->arcs[u][i].adj;
}
closeedge[u].lowcost = 0;
for(i=1; i<gn->vexnum; i++) {
k = minimum(closeedge);
printf("%c %c", gn->vertex[closeedge[k].adjvex].data,
gn->vertex[k].data);
closeedge[k].lowcost = 0;
for(j=0; j<gn->vexnum; j++)
if(gn->arcs[k][j].adj < closeedge[j].lowcost) {
closeedge[j].adjvex = k;
closeedge[j].lowcost = gn->arcs[k][j].adj;
}
}
}
基本思想:
假设N=(V,E)是连通网,将N中的边按权值从小到大的顺序排列。
时间复杂度:
基本思想:
将图G=(V,E)中的顶点分成两组:
算法步骤:
基本思想:
辅助数组D[][]、P[][],记录vi到vj的路径长度和路径。
算法思想:
定义:
对有向无环图G的所有顶点进行线性排序,使得对于任意一条边(vi,vj),在排序中vi都出现在vj之前。
基本思想:
算法实现:
int TopoSort(AdjList *G) {
Stack S;
int indegree[MAX], count, k;
ArcNode *p;
FindID(G, indegree); // 求各顶点入度
InitStack(&S);
for(i=0; i<G->vexnum; i++)
if(indegree[i] == 0)
Push(&S, i); // 将入度为0的顶点入栈
count = 0;
while(!StackEmpty(S)) {
Pop(&S, &i);
printf("%c", G->vertex[i].data);
count++; // 输出顶点并计数
p = G->vertex[i].firstarc;
while(p != NULL) {
k = p->adjvex;
indegree[k]--; // 每个邻接点的入度减1
if(indegree[k] == 0)
Push(&S, k); // 若入度减为0,则入栈
p = p->nextarc;
}
}
if(count < G->vexnum)
return 0; // 该有向图含有回路
else
return 1;
}
AOE网(Activity On Edge Network):
用顶点表示事件,用弧表示活动,弧上权值表示活动的持续时间。
重要定义:
关键路径求取算法思想:
首先调用修改后的拓扑排序算法,求出各个事件的最早发生时间ve(i)
将各顶点的最早发生时间ve[]初始化为0
只要栈S不空,则重复下面处理:
找出e(i)=l(i)的活动,即为关键活动
算法步骤:
时间复杂度:O(n+e)
本章主要内容包括:
算法设计要点: