已知有n个节点,和m条连接两个节点的线条。需要移动几条线使得所有的节点通过线条可以达到其他的节点?
若给出的图是一个极大连通图,则不需要进行任何操作。反之,表示当前的图存在不止一个连通子图。如果有多余的线条,则可以将几个子图进行连接。需要移动的线条数则为连通子图 的个数-1。
什么时候有多余的线条?
- 对于一个带有n个节点的图,一个最简单的极大连通子图需要n-1条线即可完成,所以给的已知线条数<n-1,则不可能成为一个极大连通图。
所以可以转化为求连通子图的个数。有两种方法:图的深度优先搜索遍历和并查集
- 图的深度优先搜索遍历
一次完整的递归深度优先遍历,就是遍历了一个通过图。所以要计算对于n个节点进行了几次完整的遍历。
图的深度优先搜索遍历(DFS)类似于二叉树的先序遍历。
1.首先访问出发点v,并将其标记为已访问过;
2.然后选取与v连接的未被访问的任意一个顶点w,并访问它;
3.再选取与w邻接的未被访问过的顶点,并访问,以此重复。
4.当一个顶点的所有的邻接点都被访问过时,则依次退回到最近被访问过的顶点。若该顶点还有其他邻接点未被访问,则从这些为被访问的顶点中取一个并重复上述访问过程,直至图中所有顶点都被访问过为止。
- 并查集
并查集本身就是用来维护连通性的数据结构。 - 可以快速的将两个含有多个元素的集合合并成一个
- 可以方便地判断两个元素是否属于同一个集合。(通过这两个元素所在的节点找到它们的根节点,如果根节点相同,则说明它们属于同一个集合,否则属于不同的集合,可以用一个一维数组来记录当前节点的父节点)
并查集也用于克鲁斯卡尔算法(kruskal),用于判断,对于当前的图,加入一条边会不会产生回路。
深度优先:
class solussion{
List<Integer>[] edges;
boolean[] used;
public int makeConnection(int n,int [][] connections){
if(connections.length<n-1){
return -1;
}
edges=new List[n];
used= new boolean[n];
for(int i =0 ;i<n;++i){
edges[i]= new ArrayList<>();
}
for(int[] conn:connections){
edges[conn[0]].add(conn[1]);
edges[conn[1]].add(conn[0]);
}
int result=0;
for(int i=0;i<n;++i){
if(!used){
dfs(i);
result++;
}
}
return result-1;
}
public void dfs(int v){
used[v]=true;
for(int u:edges[v]){
if(!used[u]){
dfs(u);
}
}
}
}
并查集:
//定义一个并查集
class UnionFind{
int n;//节点个数
int setCount;//当前连通图的个数
int[] parent;//用于存储节点的父节点
int [] size;//当前节点与其所有下级几点的个数和
public UnionFind(int n){
this.n=n;
this.setCount=n;//初始化时,把每一个节点都当作一个连通图
this.parent= new int[n];
this.size=new int[n];
Arrays.fill(size,1);
for(int i=0;i<n ;++i){
parent[i]=i;//初始化时,每个节点的父节点默认为自己本身
}
}
public int findSet(int x){
return parent[x]==x?x:findSet(parent[x]);//通过一次一次的找父节点,直至找到根节点
}
public boolean unite(int x,int y){
x = findSet(x);//找到x的根节点
y = findSet(y);//找到y的根节点,并且后面都是对根节点进行操作
if(x==y){//表面已经在一个连通图中
return false;
}
if(size[y]>size[x]){
int temp=x;
x=y;
y=temp;
}
parent[y]=x;
size[x]+=size[y];
--setCount;
return true;
}
}
//根据给的节点数,与边的信息,来判断
class Solussion{
public int makeConnection(int n,int [][] connections){
if(connections.length<n-1){
return -1;
}
UnionFind uf= new UnionFind(n);
for(int[] conn:connections){
uf.unite(conn[0],conn[1]);
}
return uf.setCount-1;
}
}