您的位置:首页 > 理论基础 > 数据结构算法

数据结构笔记整理第6章:图

2016-05-29 22:59 309 查看

第6章 图

本章内容

本章主要介绍图的概念,存储结构、图的遍历方法、计算最短路径、最小生成树的方法等,本章在考研中是重点内容。

6.1 图相关的基本概念

图由结点(顶点)的有穷集合V和边的有穷集合E组成。边是顶点的有序偶对,若两个顶点之间存在一条边,就表示这两个顶点具有相邻关系。

有向图:边带有方向。边称为弧,含箭头的一端称为弧头,另一端称为弧尾。

6.2 图的存储结构

邻接矩阵:表示顶点之间相邻关系的矩阵,图的顺序存储结构。设G = (V, E)是具有n个顶点的图,则G的邻接矩阵是具有如下定义的n阶方阵A:

A[i][j] = 1表示顶点i与顶点j邻接;

A[I][j] = 0表示顶点i与顶点j不邻接;

对于无向图:邻接矩阵是对称的,第i行或者第i列的元素之和即为顶点i的度;

对于有向图:第i行元素之和为i的出度,第j列元素之和为j的入度。

邻接表:图的链式存储结构。第一个结点存放有关顶点的信息,其余存放边的信息。

//代表边的结点
typedef struct ArcNode {
int adjvex;//该边指向的顶点
struct ArcNode *nextarc;//指向下一条边的指针
int info;//权重等
} ArcNode;

//代表顶点
typedef struct VNode {
char data;
ArcNode *firstarc;//指向第一条边的指针
} VNode;

//邻接表
typedef struct AGraph {
VNode adjlist[maxSize];
int n, e;
} AGraph;


【例子1】:邻接表



6.3 图的遍历算法

深度优先搜索遍历(DFS): 类似二叉树的先序遍历:首先访问出发点V,并将其标记为已访问过,然后选取与V邻接的未被访问的任意一个顶点W,并访问它;再选取与W邻接的未被访问的任意顶点并访问,依次重复进行。当一个顶点所有的邻接顶点都被访问过,则依次退回附近被访问过的顶点,若该顶点还有其他邻接顶点未被访问,则选取一个重复进行直到图中所有顶点都被访问。

int visit[maxSize];//标记顶点是否被访问过,初始化为0
/*
* @param   g       The graph to be visited.
* @param   v       The start point of the graph.
* Using DFS to visit graph.
*/
void DFS(AGraph *g, int v) {
ArcNode *p;
visit[v] = 1;//设置v号顶点(起点)被访问过
Visit(v);//代表对该结点进行访问操作
p = G->adjlist[v].firstarc;//p指向v的第一条边
while(p != null) {
//如果该顶点未被访问过
if (visit[p->adjvex] == 0) {
DFS(g, p->adjvex);
p = p->nextarc;
}
}
}


【例子2】:DFS



广度优先搜索遍历(BFS): 类似二叉树的层次遍历,需要用到一个队列实现:

1.任取图中一个顶点,访问后入队,标记为已访问;

2.队列不空的时候,循环执行:出队、依次检查出队顶点的所有邻接顶点。访问没有被访问过的所有邻接顶点并将其入队;

3.当队列为空的时候,跳出循环,广度优先搜索完成。

int visit[maxSize]//标记顶点是否被访问过,初始化为0
/*
* @param   g       The graph to be visited.
* @param   v       The start point of the graph.
* Using BFS to visit graph.
*/
void BFS(AGraph *g, int v) {
ArcNode *P;
Queue <int> q;
int temp;//存储当前出队的顶点
visit[v] = 1;
Visit(v);
q.initial();
q.push(v);
while(!q.empty()) {
q.pop(temp);
p = G->adjlist[temp].firstarc;//p指向出队顶点的第一条边
//p所有邻接点中,未被访问的顶点入队
while(p != null) {
if (visit[p->adjvex] == 0) {
Visit(p->adjvex);
visit[p->adjvex] = 1;
q.push(p->adjvex);
}
p = p->nextarc;
}
}
}


【例子3】:BFS



利用邻接表存储图的时候,两种算法的时间复杂度是O(n+e)

利用邻接矩阵存储的时候,两种算法的时间复杂度是O(n^2)

6.4 最小代价生成树算法

一个有 n 个结点的连通图的生成树是原图的极小连通子图,且包含原图中的所有 n 个结点,并且有保持图连通的最少的边。[1]这里我们介绍两种生成最小生成树的算法:普里姆算法和克鲁斯卡尔算法。这两个算法,在考研中比较要求思路和过程,对于代码的实现方式考察不是很多,所以重点要理解建树的过程,这里就不贴代码了。

普里姆算法: 从图中选出一个作为顶点,把它当作一棵树,然后从与这棵树相接的边中选择一条路径最短(权值最小)的边,并将这条边及其所连接的顶点也并入树中,以此类推,直到图中所有顶点都被并入树中为止。

【例子4】:普里姆算法求最小生成树



克鲁斯卡尔算法: 一句话来描述该算法:在不构成环的情况下,每次选权值最小边加入生成树。

【例子5】:克鲁斯卡尔算法求最小生成树



两种算法的比较



6.5 最短路径算法

求一个顶点到其他各顶点的最短路径:迪杰斯特拉算法和弗洛伊德算法。

迪杰斯特拉算法: 求一个顶点到其他各顶点的最短路径。

(1)初始化:用起点V到该顶点W的直接边初始化为最短路径,否则设为∞;

(2)从未求得最短路径的终点中选择路径长度最小的终点U:即求得V到U的最短路径;

(3)修改最短路径:计算U的邻接点的最短路径,替代之;

(4)重复以上步骤,直到求得V到其余所有顶点的最短路径。

算法复杂度:O(n^2)

【例子6】:迪杰斯特拉算法求最短路径



dist[]:存储当前已找到的从起点V0到其他顶点Vi的最短路径长度。

path[]:保存从起点V0到其他顶点Vi最短路径中,Vi的前一个顶点。



由上述过程可知:从顶点0到顶点1~6**最短路径长度**分别为:4,5,6,10,9,16

弗洛伊德算法: 求图中任意一对顶点之间的最短路径。(考的概率不大;即使考,内容和形式也不难)

(1)设矩阵A,Path,初始化将图的邻接矩阵赋值给A,将Path全置为-1;

(2)以顶点K为中间顶点,J取0~n-1(n为图中顶点个数),对图中所有顶点对{i, j}进行如下检测和修改:

如果A[i][j] > A[i][k] + A[k][j],则将A[i][j]改为A[i][k] + A[k][j]的值,将Path[i][j]改为k。

/* 应该不会考白板默写代码,这里贴出来仅仅供大家进一步理解算法 */
void Floyed(AGraph g, int A[][maxSize], int Path[][maxSize]) {
int i, j, k;
for (i = 0; i < g.n; i++) {
for (j = 0; j < g.n; j++) {
A[i][j] = g.ed
4000
ges[i][j];
Path[i][j] = -1;
}
}
for (k = 0; k < g.n; k++) {
for (i = 0; i < g.n; i++) {
for (j = 0; j < g.n; j++) {
if (A[i][j] > A[i][k] + A[k][j]) {
A[i][j] = A[i][k] + A[k][j];
Path[i][j] = k;
}
}
}
}
}


6.6 拓扑排序与关键路径

AOV网:一种以顶点表示活动,以边表示活动的先后次序并且没有回路的有向图。

有向无环图(DAG),AOV网进行拓扑排序:将图中所有顶点排成一个线性序列,使得图中任意一对儿顶点U和V,若存在由U到V的路径,则在拓扑排序序列中一定是U出现在V的前面。若一个有向图能够被拓扑排序,则它一定是一个有向无环图。

即:每次删除入度为0的顶点并输出之。

**时间复杂度:**O(n+e),n代表顶点个数,e代表边的条数。

AOE网:边表示活动,边有权值,边代表活动持续时间。

关键路径:路径长度最长的路径。

【例子7】:拓扑排序例子



拓扑排序的结果不一定是唯一的,如:ACBDE也是以上DAG图的拓扑有序序列。

关键路径算法:

事件(顶点)i:最早发生事件ve(i),最晚发生时间vl(i)

活动(边)a(i, j):最早开始时间e(e, j),最晚开始时间l(i, j)

(1)按拓扑有序排列顶点:对顶点拓扑排序;

(2)计算ve(j):ve(1)=0,ve(j)=max{ve(*)+a(*, j)},其中*为任意前驱事件;

(3)计算vl(i):vl(n)=ve(n),vl(i)=min{vl(*)-a(i, *)},其中*为任意后继事件;

(4)计算e(i, j)和l(i, j):e(i, j)=ve(i),l(i, j)=vl(j)-a(i, j)

(5)工程总用时ve(n),关键活动是e(i, j)=l(i, j)的活动是a(i, j)

【例子8】:关键路径求法





工程完工需要15,关键路径是:1->2->5->6->8->9

参考资料

1.严蔚敏《数据结构与算法分析》:清华大学出版社,2011
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息