目录
无向连通图的相关定义
主要算法流程
DFS判断:
BFS判断:
Warshell判断:
Union-Find判断:
Tarjan判断:
算法步骤及相关分析:
a)算法步骤:
从顶点v出发,访问顶点v,并令visited[v] = 1;
依次查找v的所有邻接点w,若visited[w] 为0,则从w出发,深度优先遍历图。
进行判断,遍历visited数组,若visited数组中有一个值不为1,则说明该点未被访问,图不连通。
b)时间复杂度::算法运行时间主要是耗费在寻找邻接w处,寻找一个符合条件的w的时间复杂度是O(V),V个节点就是O(V^2),尽管可能不会寻找V次。
c)空间复杂度:空间复杂度仅耗费在辅助数组visited[]上,空间复杂度为O(V)。
d)改进:
设置全局静态变量count,记录访问结点的数量,所以判断时不必遍历visited数组,只要判断count值是 否等于结点数量V即可;
visited数组设置为全局变量,与BFS函数共享。
//DFS递归
public void DFS(int[] visited, int v) {
visited[v] = 1;
judgeDFSCount ++;
for(int i=0; i
从一个结点开始,访问与其关联的所有结点,将这些结点入队,重复此过程,直至队列为空。访问的结点构成一个连通分量,若该连通分量未包含所有结点,则无向图不连通。
算法步骤及相关分析:
a)算法步骤:
从顶点v开始,访问v并置visited[v] = 1,将v入队;
只要队列不空,就重复一下操作:
队首顶点v出队;
依次查找v所有邻接点w,如果visited[w]值为0,则访问w并置visited[w] = 1,将w放入队列中;
进行判断,遍历visited数组,若有一个值不为1,则图就不连通。
b)时间复杂度:BFS的时间复杂度主要是耗费在搜索邻接点上,与DFS类型,复杂度也是O(V^2)。
c)空间复杂度:使用一个队列以及辅助数组visited[],空间复杂度也是O(V)。
代码:
此处Queue类为自写的,可简单实现。
//BFS判断
public boolean BFS() {
int count = 0 ; //由于BFS不用递归,所以定义局部变量
boolean flag = false;
Queue Q = new Queue(); //使用队列进行BFS
visited = new int[this.vertexNum]; //记录结点被访问情况
Q.add(0); //首先访问0号结点
while(!Q.isEmpty()) { //队列未空进行如下操作
int s = Q.front(); //首先队首出列,并获取队首元素
Q.remove();
visited[s] = 1; //队首被访问
count ++; //更新count值
for(int j=0; j
图中点与点之间构成边,也可以理解为存在边的两个结点之间存在关系,那么就可以使用Warshell算法求这些关系的传递闭包。Warshell算法更改矩阵的依据实质上是关系的继承,若结点i与结点j之间有边,那么结点 i 就继承结点 j 的连通关系。
主要算法步骤及相关分析:
a)算法步骤:
复制邻接矩阵给矩阵A,即令A = M ;
置变量i = 1;
对所有j如果A[j,i] = 1,则k = 1,2,3,…,n
A[j,k] = A[j,k] + A[i,k];
i加1;
如果i <= n,则转步骤(3),否则进入下一步判断;
判断矩阵A,A中所有值都为1则无向图连通,否则不连通。
b)时间复杂度:代码的主要时间开销在矩阵的计算上,复杂度为O(V^3)。
c)空间复杂度:空间开销在于需要一个邻接矩阵的复制矩阵,复杂度为O(V^2)。
代码:
由于Java不支持整型的逻辑运算,所以针对上述算法步骤中的步骤3,需要构造一个与邻接矩阵(0-1)对应的Boolean型数组。
或者步骤三的逻辑或直接化成整型的“+”,最后仍是判断矩阵是否存在0。
//Warshell算法判断可达性矩阵
public boolean Warshell() {
int count = 0; //计算关键步运行次数以判断时间复杂度
boolean flag = true;
boolean[][] tmp = new boolean[this.vertexNum][this.vertexNum];
for(int i=0; i
改进尝试:
其实这个改进效果并不好,而且其本事仍存在问题。解决简单问题没问题,但是图稍微复杂就会出现问题,若有大神有解决方案,请务必指教。
改进效果,通过count变量的大小来体现,与上面的Warshell的count相比,有一定改进。
由于无向图邻接矩阵的对称性,所以邻接矩阵的一个半角矩阵就包含了结点之间的关系。所以针对Warshell算法,我进行了相关修改,使其只求解一个半角矩阵。
修改关系的继承规则:若结点i与结点j右边,那么i继承j的连通关系,并将i的连通关系同赋给j。
改进关键步骤:
对所有j,j∈[1,i],i∈[2,n],如果A[i,j] = 1,则对k∈[1,i]
// A[i,k] = A[i,k] + A[j,k]; //承载j列关系
// A[i,k] = A[i,k] + A[k,j]; //承载j行关系
A[i,k] = A[i,k] + A[j,k] + A[k.j]; //合并以上两行
A[j,k] = A[j,k] + A[i,k]; //j与i的关系保持一样
改进后:
时间复杂度上,核心的的计算代码从n^3改进为n(n+1)(2n+1)/3,有一定改进;
其次,空间复杂度上,图的存储只使用半角矩阵,空间复杂度降低一半,但还是O(V^2)。但就目前来说,仅仅是一次尝试,仍需改进。
根据读入的边对进行集合的划分,读入的边对证明是相连的,那么我们就划分到一个集合中,然后读入新的边对就划分到新的集合中,一旦读入的边对(两个顶点)都在同一个集合中出现过,那么证明是相互连通的,如果读入的边对,两个顶点是出现在两个集合中,那么说明这两个集合至少通过该边是可以相连的,那么集合进行合并。重复以上过程,即可得到求解结果。最后只要判断所以结点是否在一个集合就可得到图是否连通,若在一个集合,则图连通,反之不连通。
算法步骤及相关分析:
a)算法:
unionFindJudge():使用并查集进行集合判断
关键代码:
for(int j = 1; j
代码:
Union-Find,并查集类的实现请参考以下博文:http://www.cnblogs.com/noKing/p/8018609.html
public boolean UnionFindJudge() {
boolean flag = true;
for(int j = 1; j
主要算法步骤及相关分析:
dfn[]:记录一个结点的搜索次序;
low[]:记录结点或结点所在的子树能够追溯到的最早的栈中结点的次序号
其实就是结点i,被访问的时间戳就是dfn[i],low[i]是其所在强连通分量的根。如果这两个值相等,说明i就是树根,那么栈中i上面的元素都是这个强连通分量的元素。
中间的具体操作,实现原理请自己查阅。
代码:
此处判断依据是:进行一次判断,得到一个强连通分量,如果这个强连通分量的个数与图的点个数想法,就是连通图。
本次代码使用数组模拟栈,读者可自由发挥,知道需要用到栈即可。
public boolean tarjan() {
judgeTarjanCount = 0; //记录一个连通分量的结点数
top = -1; t =0; //top为栈顶,t为访问结点的时间,全局变量
tarjan(0); //从0号结点开始,只访问一个连通分量
if(judgeTarjanCount == this.vertexNum) //如果该连通分量的结点数与图总结点数
return true; //相同,那么图就是连通的
else //否则不连通
return false;
}
private void tarjan(int current) {
dfn[current] = low[current] = ++ t; //赋初值,为当前时间戳
z[++top]=current; //入栈,用数组模拟栈
dist[current] = 1; //标记current已在栈中
for(int i=0;ilow[i]) //low[current]=min{low[current],low[i]}
low[current] = low[i];
}else if(dist[i] == 1 && low[current] > dfn[i]) { //若在栈中
low[current] = dfn[i];
//low[current]=min{low[current],dfn[i]}
}
}
}
if(low[current] == dfn[current]) { //如果相等,说明是连通分量
do { //将连通分量出栈
judgeTarjanCount ++; //记录连通分量的结点个数
}while(z[top--] != current);
}
}