连通网络的最少操作次数

已知有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;
	}
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值