2018图的存储与遍历及拓扑排序

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。


add_edge(u,v);

}

int j,chudu[maxn];

for(int i=0;i<n;i++){ //求出每一个顶点的出度

int tot=0;

j=head[i];
0123 4
head[maxn]

while (j!=0){
135 6

tot++;j=edge[j].next;
234ຫໍສະໝຸດ 55、路径和回路图G’
G
路径:对于图G=(V,E),对于顶点a、b,如果存在一些顶点序列 x1=a,x2,……,xk=b(k>1),且(xi,xi+1)∈E,i=1,2…k-1,则称顶点序列 x1,x2,……,xk为顶点a到顶点b的一条路径,而路径上边的数目(即k-1) 称为该路径的长度。并称顶点集合{x1,x2,……,xk}为一个连通集。
简单路径:如果一条路径上的顶点除了起点和终点可以相同外,其它顶 点均不相同,则称此路径为一条简单路径;起点和终点相同的简单路径 称为回路(或环)。
路径和简单路径的举例:
左图1—2—3是一条简单路径,长度为2, 而1—3—4—1—3就不是简单路径; 右图1—2—1为一个回路。
6、连通和连通分量
连通:在一个图中,如果从顶点U到顶点V有路径,则称U和V是连通的;



}
}
chudu[i]=tot;
edge[maxm]
012 3 456
next
00 2 040

return 0;
to
40 2 034
•}
• 两种方法各有用武之地,需按具体情况,具体选用。
邻接表存储特点
1、对于稀疏图,用链表实现的邻接表,可以节省内存。 2、可以快速找到与当前顶点相连的点。 3、判断两点是否相连不如邻接矩阵快速。
图的存储遍历 及拓扑排序
李云军
引例
有n个岛屿,给出他们的坐标, 任意两个岛屿间可以铺设一条通信电缆, 如果需要实现任意两个岛屿间都能 直接或间接通讯,求最少需要铺设的 电缆长度是多少?
400 600
250 1500
2500
750
Main lsland
500
某大学计算机专业的必修课及其先修课程如下表所示:
课程
代号 C0
1
C2 C3 C4 C5 C6 C7
课程 高等 名称 数学
程序 设计 语言
离散 数据 编译 操作 普通 数学 结构 技术 系统 物理
计算 机原 理
先修 课程
C0, C1
C1, C2
C3
C3, C7
C0
C6
请给出一种合理的课程安排方案
图的相关概念
如果数据元素集合中的各元素之间存在任 意的关系,则此数据结构称为图。如果将数据 元素抽象为顶点,元素之间的关系用边表示, 则图亦可以表示为G=(V,E),其中V是顶点 的有穷(非空)集合,E为边的集合。
using namespace std;
const int maxn=1001,maxm=100001;
struct Edge
{ int next; int to;
//下一条边的编号 //这条边到达的点
}edge[maxm];
head[maxn]
int head[maxn],num_edge,n,m,u,v;
• for (int j = 1; j <= g[i][0]; j++) • ......g[i][j]..... • 这样就可以处理i发出的每条边,也就能找到顶点i有边相连的顶点。
数组模拟邻接表方法二
数组模拟邻接表存储: 大多数情况下只要用数组模拟即可。
56
参考程序段:
#include <iostream>
提问1:一个图中,全部顶点的度数为所有边数的 2 倍;
√ 提问2:有向图中所有顶点的入度之和是(大于、等于、小于)
所有顶点的出度之和;
√ 提问3:任意一个无向图一定有(偶数、奇数)个奇点;
1
a
b
2 4
3 5
c e
d f
3、完全图、稠密图、稀疏图
完全图:若是无向图中,则每两个顶点之间都存在 着一条边;若是有向图,则每两个顶点之间都存在 着方向相反的两条边。
04 10 12 20 23 34
0123 4
135 6
void add_edge(int from,int to) //加入一条从from到to的单向边
{
edge[++num_edge].next=head[from];
edge[num_edge].to=to;
012 3 456
head[from]=num_edge;

{ cin >> i >> j >> w;

g[i][j] = w;
//对于不带权的图g[i][j]=1

g[j][i] = w; //无向图的对称性,如果是有向图则不要有这句!

}
…………

return 0;
•}
邻接矩阵存储的特点
• 1、占用的存储单元数只与顶点数有关而与边数无关,n*n的二维数组。 • 2、方便度数的计算。 • 3、容易判断两点之间是否有边相连。 • 4、寻找一个点相连的所有边需要一个一到n的循环。
3、边集数组
边集数组:是利用一维数组存储图中所有边的一种图的表 示方法。
12
V1
V4
78
5
V2
V3
3
起点 终点

无向带权图的边集数组表示法
11 1 2 2 23 4 3 4 5 7 12 3 8
图的遍历
从图中某一顶点出发系统地访问图中所有顶点,使 每个顶点恰好被访问一次,这种运算操作被称为图的遍 历。
printf("%d\n",u); vis[u]=1; for(int i=1;i<=n;i++)
if(a[u][i]==1&&vis[i]==0) dfs(i); }}
2、图的广度优先遍历
对下面两个图分别从a和V1出发进行广度优先 遍历,写出遍历结果。
左图从顶点a出发,进行广度优先遍历的结果为: a,b,d,e,f,c,g 右图从V1出发进行广度优先遍历的结果为: V1,V2,V3,V4,V5,V6,V7,V8
BFS( 顶点 i ){ //从顶点 i 进行广度优先搜索 visited[i] = 1; //将顶点 i 的访问标志置为 1 将顶点 i 入队列; while( 队列不为空 ){ 取出队列头的顶点,设为 k for( j=0; j<n; j++ ){ //对其他所有顶点 j //j是k的邻接顶点,且顶点j没有访问过
int n,m; void bfs(int u){
int head=0,tail=1; q[0]=u;
如果是非连通图,主程序做如下修改:
int main()
{
……
memset(vis,0,sizeof(vis));
for (int i = 1; i <= n; i++)
if (vis[i]==0)
遍历可以采取两种方法进行: 深度优先遍历 广度优先遍历
图的遍历分为深度优先遍历和广度(宽度)优先遍历两种方法。 1、图的深度优先遍历
对下面两个图分别进行深度优先遍历,写出 遍历结果。注意:分别从a和V1出发。
左图从顶点a出发,进行深度优先遍历的结果为: a,b,c,d,e,g,f 右 V1,图V从2,V1出V4发,进V8行,深V5度,优V3先,遍V6历,的V7结果为:
if( a[k][j]==1 && !visited[j] ){ 将顶点 j 的访问标志置为 1 将顶点 j 入队列
}
} //end of for
//BFS参考代码 #include <cstdio> #include <iostream> using namespace std; const int maxn=1010; int q[maxn]; int a[maxn][maxn]; int vis[maxn];
连通图:如果一个无向图中,任意两个顶点之间都是连通的,则称该 无向图为连通图。否则称为非连通图;左图为一个连通图。
连通分量:一个无向图的连通分支定义为该图的最大连通子图,左图 的连通分量是它本身。
注意:任何连通图的连通分量只有一个,即本身,而非
连通图有多个连通分量。
7、强连通图和强连通分量
强连通图:在一个有向图中,对于任意两个顶点U和V,都存在着一条从U到V 的有向路径,同时也存在着一条从V到U的有向路径,则称该有向图为强连通 图;右图不是一个强连通图。
1、邻接矩阵 相邻矩阵是表示顶点间相邻关系的矩阵。若G=(V,E) 是一个具有n个顶点的图,则G的相邻矩阵是如下定义的 二维数组a,其规模为n*n
a[i,
j]
1(或权) 0 ()
(vi , v j ) E (vi , v j ) E
上图中的3个图对应的邻接矩阵分别如下:
0111 G(A)= 1 0 1 1
2、邻接表
1
2
23
结点 邻接点指针
2 13
4
邻接点 边权值 下一个邻接点指针 3 2 4
5
头指针
邻接点指针
1
22
32
43^
2
12
31
53^
3
12
21
52^
4
13
54^
5
23
32
44^
17
数组模拟邻接表方法一
• int g[101][101];
• 寻找顶点i有边相连的点可以这样来做,g[i][0]表示i发出的边的数量, g[i][j]表示i发出的第j条边是通向哪个顶点的。
1、图的分类:无向图、有向图、 带权图
无向图:边集E(G)中为无向边。 有向图:边集E(G)中为有向边。
带权图 :边上带有权的图。也称为网。(有向带权图、 无向带权图)
2、顶点的度、入度、出度 顶点的度:与该顶点相关联的边的数目,有奇点、偶点之分。
对于有向图:有入度和出度之分 入度:该顶点的入边的数目。 出度:该顶点的出边的数目。
1100 1100
011 G(B)= 0 0 1
010
∞5 8 ∞ 3 5 ∞2 ∞ 6 G(C)= 8 2 ∞ 10 4 ∞ ∞ 10 ∞ 11 3 6 4 11 ∞
• 下面是建立图的邻接矩阵的参考程序段:
• int i,j,k,e,n;double g[101][101];double w;
• int main()
• { int i,j;

for (i = 1; i <= n; i++)

for (j = 1; j <= n; j++)

g[i][j] = 0x7fffffff; //初始化,对于不带权的图g[i][j]=0,表示
没有边连通。这里用0x7fffffff代替无穷大。

cin >> e;

for (k = 1; k <= e; k++)
强连通分量:一个有向图的强连通分量定义为该图的最大的强连通子图, 右图含有两个强连通分量,一个是1和2构成的一个子图,一个是3独立构成 的一个子图。
注意:强连通图只有一个强连通分量,即本身,非强连
通图有多个强连通分量。
图的存储
图型结构的存储分为静态存储和动态存储。我们介绍下面三种:邻 接矩阵、邻接表(用数组模拟),边集数组。
edge[maxm]
}
next to
00 2 040 40 2 034
计算每个点的出度
• int main(){

num_edge=0;

scanf("%d %d",&n,&m);
//读入点数和边数

for(int i=1;i<=m;i++){

scanf("%d %d",&u,&v); //u、v之间有一条边
dfs(i);
……
return 0;
}
1
1
2
5
2
5
3
43
4
以3为起点根本不能 遍历整个图
这个非连通无向图任何一 个点为起点都不能遍历整
个图
例1:公司数量 【问题描述】
在某个城市里住着n个人,现在给定关 于 n个人的m条信息(即某2个人认识),假 设所有认识(直接或间接认识都算认识)的 人一定属于同一个公司。
为了避免重复访问某个顶点,可以设一个标志数组vis[i],
未访问时值为0,访问一次后就改为1。
//DFS参考代码 #include <cstdio> const int maxn=1010; int a[maxn][maxn]; int vis[maxn]; int n,m; void dfs(int u){
广度优先遍历的实现:
为避免重复访问,也需要一个状态数组 vis[n],用来 存储各顶点的访问状态。如果 vis[i] = 1,则表示顶点 i 已 经访问过;如果 vis[i] = 0,则表示顶点 i 还未访问过。初 始时,各顶点的访问状态均为 0。
为了实现逐层访问, BFS 算法在实现时需要使用一个 队列.
注意:1)一个n阶的完全无向图含有 n*(n-1)/2条边;
2)一个n阶的完全有向图含有 n*(n-1) 条边;
稠密图:当一个图的边数接近完全图时。 稀疏图:当一个图的边数远远少于完全图时。
1
4、子图
1
设两个图G=(V,E)和
2
3
G’=(V’,E’),若V’是V的子集,且E’ 是E的子集,则称G’是G的子图。
相关文档
最新文档