图(graph):
表示“多对多”的关系
六度空间理论(Six Degrees Separation)
包含:
1.一组顶点:通常用V(Vertex)表示顶点集合
2.一组边:通常用E(edge)表示边的集合
边是顶点对:(v, w) 属于E, v , w 属于V
有向边<v, w>表示v 指向w的边(单行线)
不考虑重边和自回路
抽象数据类型定义:
1.类型名称:图
2.数据对象集:G(V, E)由一个非空的有限顶点集合v 和一个有限边集合Ezucheng
2.操作集:
常见术语:
有向图:边是有方向的,可能是单向或双向
无向图:边没有方向
带权重的图叫“网络”
怎么在程序中表示一个图:
1.邻接矩阵G[N][N] --- N个顶点从0到N-1编号
1 若<Vi, Vj>是G中的边
G[i][j] = 0 否则
对角元都是0 因为不允许自回路
所有点关于对角线对称(实际上我们把一条边存了两次)
问题:对于无向图的存储,怎样可以省一半空间?
用一个长度N(N+1)/2的一维数组A来存储这些元素Gij在A中的下标是:
(i*(i + 1) / 2 + j)
对于网络,只要把G[i][j]的值定义为边<Vi, Vj>的权重即可
若Vi和Vj之间没有边,则暂用0表示
用邻接矩阵表示一个图的好处:
1.直观、简单、好理解
2.方便检查任意一对顶点间是否存在边
3.方便找任一顶点的“邻接点”(有边直接相连的顶点)
4.方便计算任一顶点的度:(从该顶点发出的边的边数为“出度”, 指向该点的边的边数为“入度”
无向图:对应行(或列)非0元素的个数(也就是所有边的条数)
有向图:对应行非零元素的个数就是“出度”,对应列非零元素的个数就是“入度”
缺点:
1.浪费空间:存稀疏图的时候,点很多,但是边很少,有大量无效元素
对稠密图(特别是完全图)还是很合算的
2.浪费时间: 统计稀疏图中一共有多少条边
用邻接表示一个图:
G[n]为指针数组,对应矩阵每行一个链表,只存非零元素
一定要够稀疏才划算
好处:
1.方便找任一顶点的所有“邻接点”
2.节约稀疏图的空间(需要N个头指针, + 2E个结点(每个结点至少2个域)
3.方便计算任一顶点的度?
对于无向图:是的
对于有向图:只能计算“出度”, 需要构造“逆邻接表”(存指向自己的边,邻接矩阵的一列)
方便计算“入度”
不方便检查任意一对顶点间是否存在边
5.2 图的遍历
1.深度优先搜索(Depth Firsh Search, DFS)
伪代码:(走一步,看一步,走不通后返回到上一个能走通的结点)
类似于树的先序遍历:
void DFS(Vertex V) { visited[V] = true; for(V 的每个邻接点 w) if(!visited[w]) DFS(w); }
若有N个顶点,E条边,时间复杂度是
用邻接表存储图:有O(N+E)
用邻接矩阵存储图:有O(N^2)
2.广度优先搜索(Breadth First Search, BFs)(感觉上应该和邻接表存储搭配更好,但是感觉邻接矩阵也应该合格搭配更好)
(个人感觉用这种遍历方式更好一些)
相当于树的层序遍历(计划性非常强,出队一个,访问一圈,访问的同时入队)
1 void BFS(Vertex V) 2 { 3 visited[V] = true; 4 EnQueue(V, Q); 5 while(!IsEmpty(Q)) 6 { 7 V = DeQueue(Q); 8 for(V 的每个邻接点 w) 9 if(!visited[w]) 10 { 11 vitsit[w] = true; 12 EnQueue(w, Q); 13 } 14 } 15 }
若有N个顶点,E条边,时间复杂度是
用邻接表存储图:有O(N + E)
用邻接矩阵存储图: 有O(N^2)
每调用一次DFS(V)或BFS(V),就把V所在的强连通分量遍历了一遍。
图不连通怎么办(可能有一些孤立的点,他们和其他的结点都不连通)
连通:如果从v 到w存在一条(无向)路径,则称v和w是连通的
路径:v到w的路径是一系列顶点的集合,其中任意一对相邻的顶点间都有图中的边。
路径的长度:是路径中的边数(如果带权,则是所有边的权重和)。如果v和w之间的所有顶点都不同,则称简单路径
回路:起点等于终点的路径
连通图:图中任意两顶点都连通
对于不连通图:
连通分量:无向图的极大连通子图
极大顶点数:再加一个顶点就不连通了
极大边数:包含子图中所有顶点相连的所有边
强连通:有向图中顶点v和w之间存在双向路劲,则称v和w是强连通的
强连通图:有向图中任意两顶点均强连通
弱连通图:不符合强连通图的条件,但是若是将边的方向都去掉,如果是连通的,则称为弱连通图
强连通分量:有向图的极大强连通子图
图不连通时的遍历方法:
1 伪代码: 2 void ListComponents(Graph G) 3 { 4 for(each V in G) 5 if(!visited[V]) 6 DFS(V); //or BFS(V) 7 }
拯救007
void ListComponents(Graph G) { for(each V in G) if(!visited[v]) DFS(V); }
1 void Save007 (Graph G) 2 { 3 for(each V in G) 4 if(!visited[V] && FirstJump(V) 5 { 6 answer = DFS(V); 7 if(answer == Yes) 8 break; 9 } 10 if(answer == Yes) output("Yes"); 11 else output("No"): 12 }
1 DFS 算法 2 int DFS(Vertex V) 3 { 4 visited[V] = true; 5 if(IsSave(v)) answer = Yes; 6 else { 7 for(each w in G) 8 if(!visited[w] && Jump(v, w) 9 { 10 answer = DFS[w]; 11 if(answer == yes) 12 break; 13 } 14 } 15 return answer; 16 }
原文地址:https://www.cnblogs.com/hi3254014978/p/9535276.html