数据结构课设有向图强连通分量求解

合集下载

图连通性算法及应用

图连通性算法及应用

图连通性算法及应用图是计算机科学领域中常见的数据结构,用于表示对象之间的关系。

在图论中,图的连通性是一个重要的概念,指的是在图中任意两个顶点之间是否存在路径。

图连通性算法是为了判断图中的连通性而设计的算法,并且在实际应用中有着广泛的应用。

一、连通性的定义与分类在图论中,连通性有两种常见的定义方式:强连通性和弱连通性。

强连通性是指在有向图中,任意两个顶点之间存在互相可达的路径;弱连通性是指在有向图中,将其所有有向边的方向忽略后,剩下的无向图是连通的。

本文将重点介绍无向图的连通性算法及其应用。

二、连通性算法的原理1. 深度优先搜索(DFS)深度优先搜索是最常用的连通性算法之一。

它从图中的一个顶点开始,沿着一条未访问过的边深入图中的下一个顶点,直到无法深入为止,然后回溯至上一个顶点,继续深入其他未访问过的顶点。

通过深度优先搜索算法,我们可以得到一个图的连通分量,从而判断图是否连通。

2. 广度优先搜索(BFS)广度优先搜索同样是常用的连通性算法之一。

它从图中的一个顶点开始,沿着一条未访问过的边遍历与该顶点直接相邻的所有顶点,然后再以这些相邻顶点为起点,继续遍历它们的相邻顶点,直到遍历完所有连通的顶点。

通过广度优先搜索算法,我们可以得到一个图的层次遍历树,从而判断图是否连通。

三、连通性算法的应用1. 社交网络分析在社交网络分析中,连通性算法可以用来判断一个社交网络中是否存在分割成多个互不相连的社群。

通过判断社交网络的连通性,我们可以发现隐藏在社交网络背后的关系网络,从而更好地理解和分析社会关系。

2. 网络路由优化在计算机网络中,连通性算法可以用来判断网络节点之间的连通性。

通过分析网络的拓扑结构,我们可以选择合适的路由算法,从而实现快速且可靠的数据传输。

3. 图像分割在计算机视觉和图像处理中,连通性算法可以用来判断图像中的连通区域。

通过判断图像的连通性,我们可以对图像进行分割和提取,从而实现目标检测和图像识别等应用。

求强连通分量的几种算法的实现与分析

求强连通分量的几种算法的实现与分析

求强连通分量的几种算法的实现与分析作者:陈燕,江克勤来源:《电脑知识与技术》2011年第09期摘要:有向图的强连通性是图论中的经典问题,有着很多重要的应用。

该文给出了求强连通分量的Kosaraju、Tarjan和Gabow三个算法的具体实现,并对算法的效率进行了分析。

关键词:强连通分量;深度优先搜索;Kosaraju算法;Tarjan算法;Gabow算法中图分类号:TP312文献标识码:A文章编号:1009-3044(2011)09-2140-03The Implementation and Analysis of Several Algorithms About Strongly Connected Components CHEN Yan1, JIANG Ke-qin2(1.Nanjing Health Inspection Bureau, Nanjing 210003, China; 2.School of Computer and Information, Anqing Teachers College, Anqing 246011, China)Abstract: Digraph of strong connectivity is the classic problems in graph theory, which arises in many important applications. In this paper, the detailed implementation of Kosaraju, Tarjan and Gabow algorithms is discussed for solving strongly connected components, and the efficiency of three algorithms is analyzed.Key words: strongly connected components; depth first search; Kosaraju; Tarjan; Gabow图的连通性是图论中的经典问题,所谓连通性,直观地讲,就是“连成一片”。

Kosaraju算法

Kosaraju算法

求有向图的强连通分量的经典算法——Kosaraju算法一、Kosaraju算法步骤:Step1、对有向图G做dfs(深度优先遍历),记录每个结点结束访问的时间Step2、将图G逆置,即将G中所有弧反向。

Step3、按Step1中记录的结点结束访问时间从大到小对逆置后的图做dfs Step4、得到的遍历森林中每棵树对应一个强连通分量。

相关概念:在dfs(bfs)算法中,一个结点的开始访问时间指的是遍历时首次遇到该结点的时间,而该结点的结束访问时间则指的是将其所有邻接结点均访问完的时间。

二、Kosaraju算法求解过程实例下面结合实例说明Kosaraju算法的基本策略。

图1给出了一个有向图G。

图1 有向图GStep1:假设从DFS在遍历时按照字母顺序进行,根据Kosaraju算法,在步骤1中我们得到的遍历顺序可以表达为[A,[C,[B,[D,D],B],C],A][E,[F,[G,[H,H],G],F],E]在遍历序列中每一个结点出现了两次,其中第一次出现的时间为该结点的开始访问时间,第二次出现的时间为该结点的结束访问时间。

Step2:根据算法第2步,将图G逆置,得到对应的反向图G’如图2所示。

Step3:根据步骤1得到的遍历序列,按照结点结束访问时间递减排序后的结果为EFGHACBD下面,按照该结点序列顺序对逆置图G ’所深度优先遍历,得到的深度优先遍历森林如图3所示。

森林中共有4棵树,其中(a)和(d)只有一个结点,这里认为单结点也是一个强联通分量(在实际应用中可以根据实际需要将这种情况过滤掉)。

三、算法讨论问题1:以上图为例,第一遍搜索得到以A 为根的子序列(设为S1)和以E 为根的子树序列(设为S2),图反向后,再从E 开始搜索,能搜到的元素肯定不会包含S1的元素,为什么?答:因为S1中的点都不能到达E ,而第二遍搜索就是看哪些点能到达E ,所以搜不到S1中的点。

问题2:图反向后对A 进行深搜,尽管E 能到达A ,为什么搜不到E ?因为第一遍深搜时,A 不能达到E ,所以E 肯定位于A 的右边,而第二遍深搜是按照结束时间进行搜索的,在搜索A 之前,已经搜完E ,对E 设置了已经遍历标志,所以不会把E 并入A 的强联通分量。

有向图的强连通分量

有向图的强连通分量

实验报告课程名称数据结构实验项目名称有向图的强连通分量班级与班级代码14计算机实验班实验室名称(或课室)实验楼803 专业计算机科学与技术任课教师学号:姓名:实验日期:2015年12 月03 日广东财经大学教务处制姓名实验报告成绩评语:指导教师(签名)年月日说明:指导教师评分后,实验报告交院(系)办公室保存。

一、实验目的与要求采用邻接表存储的有向图。

二、实验内容(1)创建N个节点的空图DiGraph CreateGraph(int NumVertex)//创建一个N个节点的空图{DiGraph G;G = malloc( sizeof( struct Graph ) );if( G == NULL ) FatalError( "Out of space!!!" );G->Table = malloc( sizeof( struct TableEntry ) * NumVertex );if( G->Table == NULL )FatalError( "Out of space!!!" );G->NumVertex = NumVertex;G->NumEdge = 0;int i;for (i=0;i<NumVertex;i++){G->Table[i].Header=MakeEmpty(NULL);G->Table[i].V=i;}return G;}(2)在图G上执行DFS,通过对DFS生成森林的后序遍历对G的顶点编号。

//后序DFS遍历图G,并将节点按后序遍历的顺序编号int *PostDFS(DiGraph G){int NumVertex=G->NumVertex;int visited[NumVertex];int i;for (i=0;i<NumVertex;i++){visited[i]=0;//初始化,所有节点都未被访问过}int *post = malloc( sizeof( int ) * NumVertex );//用于存放后序DFS遍历时,节点的编号if( post == NULL ) FatalError( "Out of space!!!" );int postCounter=0;//定义一个内嵌的DFS函数void DFS (Vertex v)//从节点v出发执行DFS{visited[v]=1;//标记该节点被访问Position P = Header( G->Table[v].Header );if( !IsEmpty( G->Table[v].Header ) ){do//对节点v的所有邻接点递归调用DFS{P = Advance( P );Vertex w=Retrieve( P );if (visited[w]==0)//v的邻接点w未被访问过{DFS(w);}} while( !IsLast( P, G->Table[v].Header ) );}post[v]=postCounter;postCounter++;}Vertex j;for (j=0;j<NumVertex;j++)//从每个节点出发进行DFS,因为图G有可能不是连通的{if (visited[j]==0) DFS(j);}return post;}(3)求图G的强连通分量int *StrongConnectedComponent(DiGraph G)//求图G的强连通分量{//DFS后序遍历图G,并将节点按后序遍历的顺序编号存入post数组int *post=PostDFS(G);//求图G的逆置GRDiGraph GR=ReverseGraph(G);//按post编号从大到小顺序在GR上执行DFS,所得连通分量就是原图G的强连通分量int NumVertex=GR->NumVertex;int visited[NumVertex];int i;for (i=0;i<NumVertex;i++){visited[i]=0;//初始化,所有节点都未被访问过}int *sccID = malloc( sizeof( int ) * NumVertex );//用于标记强连通分量的数组if( sccID == NULL ) FatalError( "Out of space!!!" );int sccCounter=0;//定义一个内嵌的DFS函数void DFS (Vertex v)//从节点v出发执行DFS{visited[v]=1;//标记该节点被访问sccID[v]=sccCounter;Position P = Header( GR->Table[v].Header );if( !IsEmpty( GR->Table[v].Header ) ){do//对节点v的所有邻接点递归调用DFS{P = Advance( P );Vertex w=Retrieve( P );if (visited[w]==0)//v的邻接点w未被访问过{DFS(w);}} while( !IsLast( P, GR->Table[v].Header ) );}}int m;for (m=NumVertex-1;m>=0;m--)//按post编号从大到小顺序在GR上执行DFS,因为图G有可能不是连通的{Vertex n;for (n=0;n<NumVertex;n++){if (post[n]==m)//节点n的编号为当前最大值{if (visited[n]==0){DFS(n);sccCounter++;}}}}return sccID;}(4)测试函数main(){DiGraph G=CreateGraph(10);//插入15条边,参见教科书P248,图9-74InsertEdge(G,0,1);InsertEdge(G,0,3);InsertEdge(G,1,2);InsertEdge(G,1,5);InsertEdge(G,2,0);InsertEdge(G,2,3);InsertEdge(G,2,4);InsertEdge(G,3,4);InsertEdge(G,5,2);InsertEdge(G,6,5);InsertEdge(G,6,7);InsertEdge(G,7,5);InsertEdge(G,7,9);InsertEdge(G,8,7);InsertEdge(G,9,8);//插入15条边结束printf("输出图...\n");OutputGraph(G);printf("\n输出图的逆置...\n");DiGraph GR=ReverseGraph(G);OutputGraph(GR);printf("\n输出后序DFS编号...\n");int *pp=PostDFS(G);int i;for (i=0;i<G->NumVertex;i++)printf("%d---",pp[i]);printf("\n\n输出强连通分量编号...\n");int *scc=StrongConnectedComponent(G);for (i=0;i<G->NumVertex;i++)printf("%d~~~",scc[i]);printf("\n\n");}(5)测试截图四、实验中的问题及解决办法,疑问和猜想。

强连通算法--Tarjan个人理解+详解

强连通算法--Tarjan个人理解+详解

强连通算法--Tarjan个⼈理解+详解⾸先我们引⼊定义:1、有向图G中,以顶点v为起点的弧的数⽬称为v的出度,记做deg+(v);以顶点v为终点的弧的数⽬称为v的⼊度,记做deg-(v)。

2、如果在有向图G中,有⼀条<u,v>有向道路,则v称为u可达的,或者说,从u可达v。

3、如果有向图G的任意两个顶点都互相可达,则称图 G是强连通图,如果有向图G存在两顶点u和v使得u不能到v,或者v不能到u,则称图G 是强⾮连通图。

4、如果有向图G不是强连通图,他的⼦图G2是强连通图,点v属于G2,任意包含v的强连通⼦图也是G2的⼦图,则乘G2是有向图G的极⼤强连通⼦图,也称强连通分量。

5、什么是强连通?强连通其实就是指图中有两点u,v。

使得能够找到有向路径从u到v并且也能够找到有向路径从v到u,则称u,v是强连通的。

然后我们理解定义:既然我们现在已经了解了什么是强连通,和什么是强连通分量,可能⼤家对于定义还是理解的不透彻,我们不妨引⼊⼀个图加强⼤家对强连通分量和强连通的理解:标注棕⾊线条框框的三个部分就分别是⼀个强连通分量,也就是说,这个图中的强连通分量有3个。

其中我们分析最左边三个点的这部分:其中1能够到达0,0也能够通过经过2的路径到达1.1和0就是强连通的。

其中1能够通过0到达2,2也能够到达1,那么1和2就是强连通的。

.........同理,我们能够看得出来这⼀部分确实是强连通分量,也就是说,强连通分量⾥边的任意两个点,都是互相可达的。

那么如何求强连通分量的个数呢?另外强连通算法能够实现什么⼀些基本操作呢?我们继续详解、接着我们开始接触算法,讨论如何⽤Tarjan算法求强连通分量个数:Tarjan算法,是⼀个基于Dfs的算法(如果⼤家还不知道什么是Dfs,⾃⾏百度学习),假设我们要先从0号节点开始Dfs,我们发现⼀次Dfs 我萌就能遍历整个图(树),⽽且我们发现,在Dfs的过程中,我们深搜到了其他强连通分量中,那么俺们Dfs之后如何判断他喵的哪个和那些节点属于⼀个强连通分量呢?我们⾸先引⼊两个数组:①dfn【】②low【】第⼀个数组dfn我们⽤来标记当前节点在深搜过程中是第⼏个遍历到的点。

求有向图的强连通分量个数(kosaraju算法)

求有向图的强连通分量个数(kosaraju算法)

求有向图的强连通分量个数(kosaraju算法)求有向图的强连通分量个数(kosaraju算法)1. 定义连通分量:在⽆向图中,即为连通⼦图。

上图中,总共有四个连通分量。

顶点A、B、C、D构成了⼀个连通分量,顶点E构成了⼀个连通分量,顶点F,G和H,I分别构成了两个连通分量。

强连通分量:有向图中,尽可能多的若⼲顶点组成的⼦图中,这些顶点都是相互可到达的,则这些顶点成为⼀个强连通分量。

上图中有三个强连通分量,分别是a、b、e以及f、g和c、d、h。

2. 连通分量的求解⽅法对于⼀个⽆向图的连通分量,从连通分量的任意⼀个顶点开始,进⾏⼀次DFS,⼀定能遍历这个连通分量的所有顶点。

所以,整个图的连通分量数应该等价于遍历整个图进⾏了⼏次(最外层的)DFS。

⼀次DFS中遍历的所有顶点属于同⼀个连通分量。

下⾯我们将介绍有向图的强连通分量的求解⽅法。

3. Kosaraju算法的基本原理我们⽤⼀个最简单的例⼦讲解Kosaraju算法显然上图中有两个强连通分量,即强连通分量A和强连通分量B,分别由顶点A0-A1-A2和顶点B3-B4-B5构成。

每个连通分量中有若⼲个可以相互访问的顶点(这⾥都是3个),强连通分量与强连通分量之间不会形成环,否则应该将这些连通分量看成⼀个整体,即看成同⼀个强连通分量。

我们现在试想能否按照⽆向图中求连通分量的思路求解有向图的强连通分量。

我们假设,DFS从强连通分量B的任意⼀个顶点开始,那么恰好遍历整个图需要2次DFS,和连通分量的数量相等,⽽且每次DFS遍历的顶点恰好属于同⼀个连通分量。

但是,我们若从连通分量A中任意⼀个顶点开始DFS,就不能得到正确的结果,因为此时我们只需要⼀次DFS就访问了所有的顶点。

所以,我们不应该按照顶点编号的⾃然顺序(0,1,2,……)或者任意其它顺序进⾏DFS,⽽是应该按照被指向的强连通分量的顶点排在前⾯的顺序进⾏DFS。

上图中由强连通分量A指向了强连通分量B。

7.4.2有向图的强连通分量

7.4.2有向图的强连通分量
p->=head_int;
p->tailvex=tail_int;
p->hlink=G.xlist[head_int].firstin;
p->tlink=G.xlist[tail_int].firstout;
p->info=NULL;
//{tailvex,headvex,hlink,tlink,info}。
//
head_int=0;
tail_int=0;
head_char=head[k];
tail_char=tail[k];
while (head_char != G.xlist[head_int].data)
head_int++;
while (tail_char != G.xlist[tail_int].data)
p = G.xlist[i].firstin;
while(p != NULL)
{
printf("<---");
printf("%c",G.xlist[p->tailvex].data);
p = p->hlink;
}//while(p != NULL)
}// CreateDG
//(crosslist,正向)深度遍历G。
void DFSTraverse_crosslist_zheng(OLGraph G, Status (*Visit)(int v))
{
count=0; //为求连通分量。
int j;
VisitFunc = Visit;
for( j=0; j<G.vexnum; j++)

数据结构课设有向图强连通分量求解

数据结构课设有向图强连通分量求解

数据结构课设有向图强连通分量求解数据结构课设:有向图强连通分量求解一、引言有向图是图论中的一种重要概念,强连通分量是有向图中的一个子图,其中任意两个顶点之间都存在一条有向路径。

在数据结构课设中,我们需要实现一个算法,能够求解给定有向图的强连通分量。

二、问题描述给定一个有向图G=(V,E),其中V表示顶点的集合,E表示边的集合。

我们需要设计一个算法,能够找出图G中的所有强连通分量,并将其输出。

三、算法设计为了解决这个问题,我们可以使用Tarjan算法,该算法是一种经典的强连通分量求解算法。

下面是Tarjan算法的详细步骤:1. 初始化步骤:- 创建一个空栈S,用于存储访问过的顶点。

- 创建一个整数数组dfn和low,用于记录每个顶点的访问次序和最早访问到的祖先顶点的次序。

- 创建一个布尔数组inStack,用于标记顶点是否在栈中。

- 创建一个整数变量index,用于记录当前访问的次序。

2. 递归搜索步骤:- 遍历图中的每个顶点v,若v未被访问,则执行递归搜索函数Tarjan(v)。

- 在Tarjan(v)函数中,首先将v入栈,并标记v在栈中。

- 然后将dfn[v]和low[v]均设为index的值,并将index加1。

- 对于v的每个邻接顶点u,若u未被访问,则执行递归搜索函数Tarjan(u)。

- 在递归返回后,更新low[v]的值为v的所有邻接顶点u的low值的最小值。

- 若dfn[v]等于low[v],则说明v是一个强连通分量的根节点,将栈中的顶点依次出栈,直到v为止,并将这些顶点构成一个强连通分量。

3. 输出结果:- 将找到的所有强连通分量输出。

四、算法分析Tarjan算法的时间复杂度为O(V+E),其中V为顶点的数量,E为边的数量。

该算法通过深度优先搜索的方式遍历图中的每个顶点,同时使用dfn和low数组记录顶点的访问次序和最早访问到的祖先顶点的次序。

通过比较dfn和low数组的值,可以确定强连通分量的根节点,并将其输出。

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

数据结构课设有向图强连通分量求解
强连通分量(Strongly Connected Components)是有向图中的一种特殊的连通分量,指的是图中任意两个顶点之间都存在有向路径。

求解有向图的强连通分量可以使用Tarjan算法,该算法基于深度优先搜索(DFS)。

具体步骤如下:
1. 初始化一个栈,用于存储已访问的顶点;
2. 对于图中的每一个顶点v,如果v未访问,则进行DFS遍历;
3. 在DFS遍历中,对于当前访问的顶点v,设置v的索引号和低链接号为当前索引值,并将v入栈;
4. 遍历v的所有邻接顶点w,如果w未访问,则进行DFS遍历,并将w的低链接号更新为min(当前顶点的低链接号, w的低链接号);
5. 如果当前顶点v的低链接号等于索引号,则将v出栈,并将v及其出栈的顶点组成一个强连通分量。

示例代码如下:
```python
class Graph:
def __init__(self, vertices):
self.V = vertices
self.adj = [[] for _ in range(vertices)]
self.index = 0
self.lowlink = [float("inf")] * vertices
self.onStack = [False] * vertices
self.stack = []
self.scc = []
def addEdge(self, u, v):
self.adj[u].append(v)
def tarjanSCC(self, v):
self.index += 1
self.lowlink[v] = self.index
self.stack.append(v)
self.onStack[v] = True
for w in self.adj[v]:
if self.lowlink[w] == float("inf"):
self.tarjanSCC(w)
self.lowlink[v] = min(self.lowlink[v], self.lowlink[w]) elif self.onStack[w]:
self.lowlink[v] = min(self.lowlink[v], self.lowlink[w]) if self.lowlink[v] == self.index:
scc = []
w = -1
while w != v:
w = self.stack.pop()
self.onStack[w] = False
scc.append(w)
self.scc.append(scc)
def findSCC(self):
for v in range(self.V):
if self.lowlink[v] == float("inf"): self.tarjanSCC(v)
return self.scc
```
使用示例:
```python
g = Graph(5)
g.addEdge(1, 0)
g.addEdge(0, 2)
g.addEdge(2, 1)
g.addEdge(0, 3)
g.addEdge(3, 4)
print("Strongly Connected Components:") scc = g.findSCC()
for component in scc:
print(component)
```
输出结果:
```
Strongly Connected Components:
[4]
[3]
[0, 2, 1]
```
以上代码实现了有向图的强连通分量的求解,通过Tarjan算法进行DFS遍历和低链接号的更新,最终得到所有的强连通分量。

相关文档
最新文档