C04-图论算法
合集下载
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
一笔画问题
如果一个图存在一笔画,则一笔画的路径叫做 欧拉路,如果最后又回到起点,那这个路径叫 做欧拉回路 定理 1 :存在欧拉路的条件:图是连通的,有 且仅有2个奇点 定理 2 :存在欧拉回路的条件:图是连通的, 有0个奇点
一笔画问题
求欧拉路的算法:采用深度优先遍历即可 寻找欧拉路:对一个奇点执行DFS 寻找欧拉回路:对任意一个点执行DFS
V1 V4 V1 V1 8 5 V2 V3 1 2 3 4 V2 V3 1 2 3 V2 6 2 1 2 3 V3 4 3 V5 11 V4
10
4 5
i\j
1 2 3 4
i\j
1 2 3
i\j
1 2
0 1 1
1
1 0 1
1
1 1 0
0
1 1 0
0
0 0 0
1 0 1
1 1 0
3 4
5
∞ 5 8 ∞
3
5 4 3
2
5 4 3
2
单点也算一个强连通分量 共有3个强连通分量:1-2-5、3、4
图的存储结构——二维数组邻接矩阵存储
定义g:array[0..100,0..100] of longint; g[i,j]的值,表示从点i到点j的边的权值 1或权值 当Vi与Vj之间有边时 g[i,j]= 0或∞ 当Vi与Vj之间无边时
Procedure dfs(i:integer); //深度优先遍历 begin visited[i]:=true; for j:=1 to num[i] do if (not visited[g[i,j]]) then dfs(j); end; Procedure bfs(i); //广度优先遍历 var qu:array[1..maxn] of longint; //队列 j,t,front,rear:longint; //front: 队头指针,rear: 队尾指针 begin visited[i]:=true; front:=0; rear:=1; qu[rear]:=i; //初始化队列 while (front<>rear) do begin inc(front); t:=qu[front]; // 出队 for j:=1 to num[t] do if not visited[g[t,j] then begin visited[g[t,j]:=true; inc(rear); qu[rear]:=g[t,j];//入队 end; end; 主程序调用: fillchar(visited,sizeof(visited),false); for i:=1 to n do if (not visited[i]) then dfs(i); fillchar(visited,sizeof(visited),false); for i:=1 to n do if (not visited[i]) then bfs(i);
什么是图
简单地说,点用边连起来就叫做图 严格来说,图是一种数据结构,定义为: graph=(V,E)。其中:
1)V是一个非空有限集合,它的元素称为顶点; 2)E 也是一个集合,它是顶点之间关系的有穷集 合,也叫做边集合。
图的一些定义和概念
1、有向图:图的边有方向,只能按箭头从一点到另一点 2、无向图:图的边没有方向,可以双向
图的存储结构——数组模拟邻接表存储
V1 8 3 V5 11
5
V2 6 2 1 2
4
V3 3 4 10
V4
i
1 2 3 4
i\j 3 3 4 2
1 2 3
i\j
1
1
2
3
4
4
5
2 1 1 3 1
3 3 2 5 2
5 5 4 3
2
5 4
3 4 5
5 8 3 5 2 6 8 2 10 10 11 3 6 4
5 8 ∞ 2 2 ∞ ∞ 10
6 4
∞ 3 ∞ 6 10 4 ∞ 11
11 ∞
图的存储结构——二维数组邻接矩阵存储
建立图的邻接矩阵的参考程序段:
of longint;
Program graph1_1; Var i,j,k,e:longint; g:array[0..100,0..100] w:longint; Begin for i:=0 to 100 do for j:=0 to 100 do g[i,j]:=maxlongint; readln(e); for k:=1 to e do begin read(i,j,w); g[i,j]:=w; g[j,i]:=w; end; …… End.
//如果是不带权的图则用g[i,j]:=0 //读入边数
//读入一条边的两个顶点序号和权值 //如果是不带权的图,则g[i,j]:=1 //如果是有向图则不能有此句
图的存储结构——二维数组邻接矩阵存储
初始化的技巧:可以不用两重 for 循环,而是 采用fillchar语句 (1)longint数组:
图的一些定义和概念
3、结点的度:无向图中与结点相连的边的数目,有向图 中等于该结点的入度与出度之和 4、结点的的入度:在有向图中,以该顶点为终点的有向 边的数目 5、结点的的出度:在有向图中,以该顶点为起点的有向 边的数目
1 无向图 4 3 2 4 3 1
2 有向图
•定理1:图G中所有顶点的度数之和等于边数的2倍。 •定理2:任意一个图有偶数个度为奇数的点。
柯尼斯堡桥问题
欧拉把这个问题转化为右图。图上用A、B、C、D共4个顶点分 别表示4个地区,用两点间的线段表示连接各地的桥。这样原来 的问题就转化为:从某起点出发经过其中每一条线段一次,而 且仅一次的“一笔画”问题。 欧拉对该问题作出了否定的结论。因为对于每一个顶点,不论 如何经过,必须有一条进路和一条出路,所以对每一个顶点(除 起点和终点 ) 来说与它有关的线段 ( 称为边 ) 必须是偶数条。而右 图顶点有关的线段都是奇数条,因此不可能一笔画出。 欧拉通过对柯尼斯堡桥问题的研究,于 1736 年发表了著名的关 于图论的论文,从而创立了图论的学说。右图一类的问题就是 图论中所指的图。
A
B
D
C
又如,有6个足球队之间进行循环赛,他们比赛的场次可以用图(1)来表示。有3 个人相互写信,可以用图(2)来表示。
从上面两个例子可看出,我们这里所说的图(graph),与人们通常所熟悉的 图,如圆、四边形、函数图象等是很不相同的。是指某些具体事物和这些 事物之间的联系。如果我们用点来表示事物(如地点、队),用线段来表示 两事物之间的联系,那么一个图就是表示事物的点集和表示事物之间联系 的线段集合所构成。其中线段仅表示两点的关系,它的长短与曲直是无关 紧要的。例如下图中3个图,被认为是同一个图。
图论算法
柯尼斯堡桥问题
问题远溯至 18 世纪初。背景是位于普雷盖尔河岸的柯 尼斯堡城。河中的两个岛是城的部分,由七座桥与城 连接。柯尼斯堡的居民中有一个欢乐的传统:星期天 沿着城市的河岸和岛屿散步 ,同时试图找到一条路 线,可以经过所有七座桥,但不重复经过任一座桥。 虽然当时大多数人都把这当做有趣的娱乐,但是瑞士 数学家伦哈德· 欧拉(1707~1783)发现这娱乐可以导 向一个另外的契机,他抓住了这个契机并加以发展, 开辟了图论的领域 。
图的存储结构——数组模拟邻接表存储
图的邻接表存储法,又叫链式存储法。本来是 要采用链表实现的,但经常采用数组模拟即可
定义g:array[1..100,1..100] of longint; num:array[1..100] of longint; g[i,j]:与点i连接的第j个点的编号 num[i]:与点i相连的点的数目 如果边有权值,还需定义一个数组用于存储权值 val:array[1..100,1..100] of longint;
1 有向图 5 4 3 2 1
5 4 3
2
无向图
•在无向图中,边的两个顶点在边的表示中可以互换,如边(1,4) 与边(4,1)是等价的,表示的是同一条边。(边的表示用圆括号) •在有向图中,边的走向不同就认为是不同的边。如在边的集合 E={<1,2>,<1,3>,<1,4>,<2,5>,<5,1>}中,其中<1,2>表示该 边是由顶点1出发,到顶点2结束,即边<1,2>表明了该边的方向性, 且两个顶点的顺序不能颠倒。(边的表示用尖括号)
4 11
5
4
num
g
val
图的存储结构——数组模拟邻接表存储
用数组模拟邻接链表存储的参考程序段:
Program graph1_2; Var n,m,i,j,e:longint g,val:array[1..100,1..100] of longint; num:array[1..100] of longint; Begin fillchar(g,sizeof(g),$7f); fillchar(num,sizeof(num),0); for i:=1 to n do begin readln(m);//第i行说明点i的链接情况,每行的开头读入与之相连的点的数目 num[i]:=m; for j:=1 to m do begin read(c); //第j个与点i相连的点是c g[i,j]:=c; //记录下第j个与i相连的点 val[i,j]:=权值;//有权图还要用val存储权值 end; end; ……
1 1 1 2 3 √ √
5
4 3
2
1
1 2 2 2
4
5 1 3 4
√
√ √ √ √
2
3 ……
5
1 ……
√
× ……
图的一些定义和概念
8、回路:如果起点和终点完全相同的路径,称 为回路,或“环”
1 5 4 3 2 2 1 3
6
4 5 7
图的一些定义和概念
9、完全图:每一对不同的结点都有一条边相连 的图
从顶点1开始深度遍历: 1 2 5 3 4 从顶点1开始广度遍历: 1 2 3 4 5
图的遍历
并不是从任何一个点开始都可以遍历整个图的 ,所以应该以每个点为起点都尝试一次
1 5 4 1 3 2
从顶点3开始,不管深度遍历或是 广度遍历,都无法遍历整个图
5 4 3
2
非连通无向图,任何一个点为起 点都不能遍历整个图
fillchar(g,sizeof(g),$7f) fillchar(g,sizeof(g),0)
初始化为maxlongint 初始化为0 初始化为1.38*10306 初始化为0
(2)real数组:
fillchar(g,sizeof(g),127) fillchar(g,sizeof(g),0)
图的一些定义和概念
6、权值:边的“费用”,可以形象地理解为边 的长度
1 1 4 3
5 3
5
1 2
2 2 4
6
3
7
4
7
•路径长度: •非带权图的路径长度是指此路径上边的条数。 •带权图的路径长度是指路径上各边的权之和。
图的一些定义和概念
7、连通:如果图结点U、V之间存在一条从U通 过若干边、点到达 V 的通路,则称 U 、 V 是连 通的 是否连通 U V
Program Euler; Const maxn=100; Var g:array[1..maxn,1..maxn] of integer; //此图用邻接矩阵存储 du:array[1..maxn] of longint; //记录每个点的度 circuit:array[1..maxn] of longint; //记录找到的欧拉路 n,e,circuitpos,i,j,x,y,start:longint; Procedure find_circuit(i:integer); var j:longint; begin for j:=1 to n do if g[i,j]=1 then begin g[i,j]:=0; g[j,i]:=0; find_circuit(j); end; circuitpos:=circuitpos+1; circuit[circuitpos]:=i; end; //从i点开始DFS寻找欧拉路
1 无向图 2 1 2 有向图
4
3
3பைடு நூலகம்
•在n个结点的无向图中,共有n(n-1)/2条边。 •在n个结点的有向图中,共有n(n-1) 条边。 •稠密图:一个边数接近完全图的图 •稀疏图:一个边数远远少于完全图的图
图的一些定义和概念
10、强连通分量:有向图中任意两点都连通的 最大子图
1 1 1-2-5,强连通分量
图的遍历
遍历:从图中某一顶点出发,系统的访问图中 所有顶点,使每个顶点恰好被访问一次 图的遍历分为深度优先遍历( dfs )和广度优 先(bfs)遍历两种。两者效率都为O(n*n) 遍历时,设置一个标志数组 visited[i] ,未访问 时值为false,访问一次后就改为true
1 5 4 3 2