数据结构笔记整理第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相关文章推荐
- Tomcat端口被占用解决方法(不用重启)
- 文件遍历排序函数
- “传奇”图象数据存储方式
- C#数据结构之顺序表(SeqList)实例详解
- 渗透技术一瞥(图)
- 图片引发的溢出危机(图)
- Lua 学习笔记之C API 遍历 Table实现代码
- Lua教程(七):数据结构详解
- 解析从源码分析常见的基于Array的数据结构动态扩容机制的详解
- 超大数据量存储常用数据库分表分库算法总结
- C#遍历文件夹后上传文件夹中所有文件错误案例分析
- C#数据结构之队列(Quene)实例详解
- C#数据结构揭秘一
- C#中遍历Hashtable的4种方法
- C#数据结构之单链表(LinkList)实例详解
- SQL Server误区30日谈 第18天 有关FileStream的存储,垃圾回收以及其它
- Erlang中遍历取出某个位置的最大值代码
- 数据结构之Treap详解
- C++实现图的邻接矩阵存储和广度、深度优先遍历实例分析
- 【数据结构与算法】数组应用4:多项式计算Java版