Transcript Document

第九章 图
多对多的关系
§9.1 图的定义和术语
一、图
多对多的关系,非线性结构
G=(V,E)
V(G):顶点的非空有限集合
E(G):图中边的有限集合
二、几个术语
有向图:弧(有向边),
<x,y>:xy,x邻接到y,y邻接自x
<x,y> 与x、y相关联
<x,y> ≠ <y,x>
无向图:边(无向边),
(x,y):xy,x与y相邻接
(x,y) 与x、y相关联
(x,y) = (y,x)
二、几个术语2
有向完全图:每个顶点都与其它任意顶点有弧。
共 n*(n-1) 条边。
无向完全图:每个顶点都与其它任意顶点有边。
共 n*(n-1)/2 条边。
子图:V(B)属于V(A),且E(B)属于E(A),则B是A的子图。
(B是A中的一部分)
二、几个术语3
顶点的度:与顶点相关联的边的数目(出度、入度)。
有向图中度=出度+入度
设vi的度为di,边数e=1/2∑di(1<i<n)
路径:vp到vq经过的顶点序列,
经过的边的数目称为路径长度。
回路:起始点和终止点是同一顶点的路径。
简单路径(简单回路):顶点不重复。
二、几个术语4
连通图:无向图中任意两个顶点都是连通的。
强连通图:有向图中任意两个顶点都是连通的。
连通分量=极大连通子图
网络(带权图):边上附加数作为权(代价)。
(连通图的)生成树:极小连通子图。
问题:1、n个顶点的连通图至少 n-1
2、n个顶点的强连通图至少
条边。
n
条边。
§9.2 图的存储
一、邻接矩阵:用二维数组表示顶点的邻接关系
1、n顶点用n阶方阵
1(i与j相邻接)
A[ i ][ j ] =
0(i与j 无边)
2、邻接矩阵特点
① 无向图邻接矩阵对称,有向图不对称
② 无向图第i行(列)非零元素个数即为结点i的度
有向图第i行(列)非零元素个数即为结点i的出(入)度
③ 容易确认任两点之间是否有边,但扫描边数代价大
(对每个结点进行检测)
3、邻接矩阵建立图
main()
{ int i,j,n=5,adj[5][5],v1,v2;
for (i=0;i<n;i++)
for (j=0;j<n;j++) adj[i][j]=0;
scanf(“%d,%d”,&v1,&v2);
while (v1!=0 || v2!=0)
{ adj[v1][v2]=1; adj[v2][v1]=1;
scanf(“%d,%d”,&v1,&v2);
}
}
时间复杂度:O(n2+e)
二、邻接表
data
指向下一个邻接点
例子:
特点:① 每个顶点对应一个单链表(存储邻接点)
② 无向图n个顶点,e条边需n个表头结点和2e个边结点
③ 顶点对应的单链表长度为该顶点的度
(有向图则为出度)
问题:如何求有向图顶点的入度?
解:可建立有向图的逆邻接表
例子:
① 正邻接表:方便求顶点的出度
② 逆邻接表:方便求顶点的入度
用邻接表存储图结点定义:
struct gra
{int vertex;
struct gra *link;
};
typedef struct gra GRA;
GRA
adj[maxsize];
邻接表建立图(有向图):
main()
{ int i,j,v1,v2; GRA *ptr;
for (i=0;i<n;i++)
{adj[i].vertex=i+1; adj[i].link = NULL;}
scanf(“%d,%d”,&v1,&v2);
while (v1!=0 && v2!=0)
{ ptr=(GRA *) malloc (sizeof(GRA));
ptr->vertex=v2-1;
ptr->link=adj[v1-1].link;
adj[v1-1].link=ptr;
scanf(“%d,%d”,&v1,&v2); }
}
时间复杂度:O(n+e)
§9.3 图的遍历
一、深度优先遍历
利用栈结构
例子
遍历结果:n结点,(n-1)条边,无回路
按遍历过程经过的边为一棵深度优先生成树。
过程:① 指定起点q
② 访问q
⑤ 找栈顶元素未访问邻接点q
③ q进栈
a:找到,转②
④ 栈空否?
b:找不到,出栈,转④
a:不空,转⑤
⑥ 结束
b:空,转⑥
栈的变化: (过程)
访问:
输出顶点并记录经过的边,即有深度优先生成树
上机:
使用邻接表存储图,编写其深度优先遍历的非递
归算法。
过程:访问地点;起点进栈;
while (栈不空)
{找栈顶未访问邻接点p;
if 找到
{访问;进栈;}
else
出栈; }
§9.3 图的遍历2
二、广度优先遍历
利用队列结构
例子
遍历结果:n结点,(n-1)条边,无回路
按遍历过程经过的边为一棵广度优先生成树。
过程:① 访问指定起点q
② q所有未访问邻接点依次访问、入队列
③ 队列空否:
a:不空,出队列赋为q,转②
b:空,转④
④ 结束
队列的变化: (过程)
访问:
输出顶点并记录经过的边,即有广度优先生成树
注意:
深度优先遍历或者广度优先遍历能
遍历到指定起点所在的连通分量中的所
有顶点。
§9.4 最小生成树
一、概念
1、生成树
生成树=极小连通子图(n-1)条边
① 加边:形成回路
② 减边:不连通
生成树不唯一:深度优先生成树
广度优先生成树
……
§9.4 最小生成树2
2、最小生成树
网络中所有生成树中代价最小的树。
0 (i=j)
A[ i ][ j ] =
∞(ai到aj无边)
wij(ai与aj边的权值)
二、普里姆(Prim)算法
选取一个顶点作为起点,每次通过代价最小
的边选择一个顶点并入生成树中,直到包含所有
顶点。
例子:
总的时间复杂度:O(n2)
适合:与顶点数有关,适合边稠密的网络。
练习:
用普里姆算法(Prim)的思想,画
出该图生成最小生成树的过程。
三、克鲁斯卡尔(Kruskal)算法
所有边按权排序,每次取最短边,如不构成
回路,则并入生成树中,否则舍弃。直到并入了
(n-1)条边。
例子:
总的时间复杂度:与边排序算法有关
适合:与边有关,适合边稀疏的网络。
三、克鲁斯卡尔(Kruskal)算法2
问题:如何判断是否构成回路?
定义连通分量数组ring[n],初始时各个ring[i]各
不相同,找到边(i,j)时,判断ring[i]与ring[j]是
否相等:
相等:说明i,j在同一个连通分量,舍弃;
不相等: i,j在不同连通分量,边(i,j)并入生
成树中,并把所有与ring[i]相等的ring值改为ring[j]。
练习:
用克鲁斯卡尔算法(Kruskal)的
思想,画出该图生成最小生成树的过程。
§9.5 有向无环网及应用
(有起点,终点,不能包含回路)
一、AOV网(Activity On Vertex network)
1、用顶点表示活动,用弧表示优先关系的有向图。
例子:
概念:前驱、后继、直接前驱、直接后继
一、AOV网2
2、拓扑排序(Topological Sort)
为AOV网建立一个活动(顶点)的线性有序序列。
例子:
步骤:① 输出入度为0的顶点;
② 删除该顶点和相关边;
③ 返回① ,直到无入度为0的顶点。
(结果不唯一)
二、AOE网(Activity On Edge network)
1、用顶点表示事件,弧表示活动,权表示代价的网络。
例子:
问题:完成整个工期需要多少时间?
关键路径:从起点到终点的路径中代价最大的。
(不唯一)
关键活动:关键路径上的活动
缩短工期的关键:加快关键活动的进度。
(注意要受到其他关键活动的制约)
§9.6 最短路径(有向图)
一、问题的提出
A、B有若干路径相通,如何找寻一
代价最小的路径?
例子: