题目
解法一 深度优先遍历
本题实际上就是求所有主机与主机间的边所构成的图的连通分量数。任意两个连通分量间只需要一根网线就能相连,最终所求答案就是连通分量数减一。
不熟悉图和连通分量的,可以参考图与连通分量。这里我们首先使用dfs
求图的连通分量数。我们使用visited
数组标记遍历过的主机,防止重复遍历。从主机0
开始进行遍历,若当前主机未遍历过,则连通分量数加一,同时以该主机为起点进行dfs
标记该连通分量中的所有主机。遍历完成后返回连通分量数减一就是最终答案。
代码
class Solution {
public:
int makeConnected(int n, vector<vector<int>>& connections) {
int m = connections.size();
if(m<n-1) return -1;
int connectedGroupCount = 0;//连通分量数
vector<int> visited(n,0);//主机访问标记
vector<vector<int>> map(n,vector<int>(n,0));//图的邻接矩阵
for(const auto& v : connections){//转化为邻接矩阵
int a = v[0], b = v[1];
map[a][b] = 1;
map[b][a] = 1;
}
function<void(int)> dfs = [&](int pos){//dfs
++visited[pos];
for(int i = 0; i < n; ++i){
if(map[pos][i]==1 && visited[i]==0) dfs(i);
}
};
for(int i = 0; i < n; ++i){
if(visited[i]==0){//如果当前节点未遍历过
++connectedGroupCount;//则连通分量数加一
dfs(i);//遍历标记该连通分量的所有节点
}
}
return connectedGroupCount-1;
}
};
复杂度分析
时间复杂度: O(m+n)
。m
为图的边数,n
为主机数。需要遍历主机和边各一次。
空间复杂度: O(n²)
。因为使用了邻接矩阵辅助遍历,需要O(n²)
的空间消耗。
解法二 并查集
除了深度优先遍历,我们还可以使用并查集求连通分量数。对并查集不熟悉的可以参考并查集。
代码
class Solution {
vector<int> unionFind;//并查集
public:
int Find(int x){//查
while(unionFind[x] != x) x = unionFind[x];//一直查找父节点
return x;//返回祖先节点
}
bool Union(int x, int y){//并
int fx = Find(x), fy = Find(y);//查找两节点的祖先节点
if(fx != fy){//若祖先节点不同,合并两集合
unionFind[fx] = fy;
return true;//并返回“已合并”
}
return false;//否则返回“同集合”
}
int makeConnected(int n, vector<vector<int>>& connections) {
int m = connections.size();//计算图的边数
if(m<n-1) return -1;//若边数不到n-1,返回-1
unionFind.resize(n, -1);//初始化并查集
for(int i = 0; i < n; ++i) unionFind[i] = i;
int connectedGroupCount = n;//初始化连通分量数为n
for(const auto& v : connections){
//依次遍历每条边,若合并两个连通分量,则连通分量数减一
//若本就属于同一连通分量,则连通分量数不变
if(Union(v[0],v[1])) --connectedGroupCount;
}
return --connectedGroupCount;//返回连通分量数减一
}
};
复杂度分析
时间复杂度: O(m*O(Union))
。m
为图的边数。
空间复杂度: O(n)
。并查集的空间消耗。