Open azl397985856 opened 2 years ago
仔细分析一下, 会发现本题实际上就是要找连通分量的数量。
N个节点只需要n-1条边就可以组成一个connected component (CC)(1棵生成树)。
对于每根电缆,将两侧合并,如果它们已经具有相同的根,例如 在同一个 cc 中,则该电缆是多余的。
确定额外的电缆(可以拔下并使用它们连接其他电缆)以确保 extra cables >= connected groups -1。
实际上, 根本不需要知道需要拆下哪几条边, 只要电缆的数量够就行了!
只要我们有 n-1 根电缆,我们总能找到一种方法来连接所有计算机。
此时问题可以转换为: 找连通分量的数量
。
实现语言: C++
class Solution {
vector<int> p;
public:
int makeConnected(int n, vector<vector<int>>& connections) {
if (connections.size() < n - 1)
return -1;
p.resize(n);
iota(p.begin(), p.end(), 0); // 依次填充0 ~ n-1
for (const auto& c : connections)
p[find(c[0])] = find(c[1]);
unordered_set<int> st;
for (int i = 0; i < n; i++)
st.insert(find(i));
return st.size() - 1;
}
int find(int x)
{
if (p[x] == x) return x;
p[x] = find(p[x]);
return p[x];
};
};
class Solution {
List
public int makeConnected(int n, int[][] connections) {
if (connections.length < n - 1) {
return -1;
}
edges = new List[n];
for (int i = 0; i < n; ++i) {
edges[i] = new ArrayList<Integer>();
}
for (int[] conn : connections) {
edges[conn[0]].add(conn[1]);
edges[conn[1]].add(conn[0]);
}
used = new boolean[n];
int ans = 0;
for (int i = 0; i < n; ++i) {
if (!used[i]) {
dfs(i);
++ans;
}
}
return ans - 1;
}
public void dfs(int u) {
used[u] = true;
for (int v : edges[u]) {
if (!used[v]) {
dfs(v);
}
}
}
}
时间复杂度:O(n+m),其中 m 是数组 connections 的长度。
空间复杂度:O(n+m),其中 O(m) 为存储所有边需要的空间,O(n) 为深度优先搜索中使用的栈空间。
C++ Code:
struct UF
{
private:
vector<int> parent;
int count;
public:
UF(int n)
{
for(int i=0; i< n; i++)
parent.push_back(i);
count = n;
}
void uninTwoNode(int i, int j)
{
int parentI = findParent(i);
int parentJ = findParent(j);
if(parentI != parentJ)
{
parent[parentI] = parentJ;
count--;
}
}
int findParent(int i)
{
while(parent[i] != i)
{
parent[i] = parent[parent[i]];
i = parent[i];
}
return i;
}
int connectedNum()
{
return count;
}
};
class Solution {
public:
int makeConnected(int n, vector<vector<int>>& connections) {
// generate tree. and tree
// connections number is n-1;
if(connections.size()<n-1) // is impossible.
return -1;
UF uf(n);
for(int i=0; i< connections.size(); i++)
{
uf.uninTwoNode(connections[i][0], connections[i][1]);
}
return uf.connectedNum()-1;
}
};
C++ Code:
class Solution {
public:
vector<int> parent;
int find(int x)
{
if(x!=parent[x])
{
parent[x] = find(parent[x]);
return parent[x];
}
return x;
}
int makeConnected(int n, vector<vector<int>>& connections) {
if(n>connections.size()+1)
return -1;
for(int i=0;i<n;i++)
parent.push_back(i);
for(int i=0;i<connections.size();i++)
if(find(connections[i][0])!=find(connections[i][1]))
parent[find(connections[i][0])] = find(connections[i][1]);
int res = -1;
unordered_map<int,bool> mp;
for(int i=0;i<n;i++)
{
find(i);
if(mp.count(parent[i])!=1)
{
res++;
mp[parent[i]] = true;
}
}
return res;
}
};
class Solution:
def makeConnected(self, n: int, connections: List[List[int]]) -> int:
def find(x):
while x != root[x]:
root[x] = root[root[x]]
x = root[x]
return x
def union(x, y):
nonlocal count
root_x, root_y = find(x), find(y)
if root_x != root_y:
root[root_x] = root_y
count -= 1
# To connect all nodes need at least n-1 edges
if len(connections) < n - 1: return -1
root = [i for i in range(n)]
count = n
for c in connections:
union(c[0], c[1])
return count - 1
Time complexity: O(N)
Space complexity: O(N)
https://leetcode.com/problems/number-of-operations-to-make-network-connected/
const makeConnected = function(n, connections) {
let edges = connections.length;
if(edges < n-1) return -1;
let g = [];
for(let i=0;i<n;i++) g[i] = []
for(let i=0;i<edges;i++){
g[connections[i][0]].push(connections[i][1]);
g[connections[i][1]].push(connections[i][0]);
}
let v = Array(n).fill(0),c=0;
for(let i=0;i<n;i++){
if(!v[i]){
c++;
dfs(i,g,v)
}
}
return c-1;
};
function dfs(i,con,v){
v[i] = 1;
for(let x of con[i]){
if(!v[x]) dfs(x,con,v)
}
}
UnionFind. For n nodes, we need at least n-1 edges to connect them all. If the amount of the edges is fewer than that, we return -1. For each edge, we union its two nodes. After we scan through all edges, we can know how many unions we have now. The difference between the union amount and 1 is the edges we need to extract.
class UnionFind:
def __init__(self, n):
self.parent = {}
self.size = {}
self.cnt = n
for i in range(n):
self.parent[i] = i
self.size[i] = 1
def find(self, p):
if p!=self.parent[p]:
self.parent[p] = self.find(self.parent[self.parent[p]])
return self.parent[p]
return p
def union(self, p, q):
root_p, root_q = self.find(p), self.find(q)
if root_p != root_q:
self.parent[root_p] = root_q
self.size[root_q] += self.size[root_p]
self.cnt -= 1
class Solution:
def makeConnected(self, n: int, connections: List[List[int]]) -> int:
if len(connections)< n - 1:
return -1
UF = UnionFind(n)
for i in connections:
UF.union(i[0], i[1])
return UF.cnt - 1
Time: O(N +E). N is the is number of the machines. E is the amount of edges. Initializing the union find needs O(N). Scanning through the edges needs O(E). Space: O(N)
使用并查集的方法
先判断 len(connections) 是否 < n-1, 因为如果小于那么代表无法连接 n 个节点, 返回 -1
否则一定能连接 n 个节点
那么用并查集将现在有的 connection 进行连接
连接后要改动的 minimum number of times 其实就是 现在并查集中联通分量的个数 -1, 因为网线一定够, 所以不需要找到从哪儿拔网线, 只需要计算最少需要添加多少根网线
最少需要添加的网线数目就是现在有的联通分量的个数 -1, 所以遍历 i, 计数不同的 parents ( find(i) 的结果 ) 的个数, 最后返回 不同 parents 的个数 -1
class UF:
def __init__(self, num):
self.parents = [i for i in range(num)]
def find(self, x):
while self.parents[x] != x:
self.parents[x] = self.parents[self.parents[x]]
x = self.parents[x]
return x
def isconnected(self, x, y):
return self.find(x) == self.find(y)
def union(self, x, y):
if self.find(x) != self.find(y):
px, py = self.find(x), self.find(y)
self.parents[px] = py
class Solution:
def makeConnected(self, n: int, connections: List[List[int]]) -> int:
num = len(connections)
if num < n-1:
return -1
uf = UF(n)
for i, j in connections:
uf.union(i, j)
parents = set()
for i in range(n):
pi = uf.find(i)
parents.add(pi)
return len(parents)-1
n 为 节点个数, m 为 connections 长度
时间复杂度: O((m+n)logn) 进行了 m 次 union, 所以复杂度是 O(mlogn), 进行了 n 次查找 parents, 所以复杂度是 O(nlogn), 综合时间复杂度为 O((m+n)logn)
空间复杂度: O(n) 并查集的空间复杂度是 O(n), parents set 的空间复杂度小于 O(n) 所以总得空间复杂度是 O(n)
Idea:union find Time: O(N +E). N is the is number of the machines. E is the amount of edges. Initializing the union find needs O(N). Scanning through the edges needs O(E). Space: O(N)
class Solution:
def makeConnected(self, n: int, connections: List[List[int]]) -> int:
if len(connections)< n - 1:
return -1
uf = UF(n)
for i in connections:
uf.union(i[0], i[1])
return uf.count - 1
class UF:
def __init__(self,n):
self.parent = {}
self.size = {}
self.count = n
for i in range(n):
self.parent[i] = i
self.size[i] = 1
def find(self,x):
while x != self.parent[x]:
x = self.parent[x]
return x
def union(self,x,y):
if self.connected(x,y): return
self.parent[self.find(x)] = self.find(y)
self.size[self.find(x)] += self.size[self.find(y)]
self.count -= 1
def connected(self,x,y):
return self.find(x) == self.find(y)
connections 就相当于连接线。
可能性判断:当计算机的数量为 n 时,我们至少需要 n-1 根线才能将它们进行连接。如果线的数量少于 n-1,那么我们无论如何都无法将这 nn 台计算机进行连接。因此如果数组 \textit{connections}connections 的长度小于 n-1,我们可以直接返回 -1 作为答案,否则我们一定可以找到一种操作方式。
先按照连接线连接:连接前判断他们有没有连在一起(find==find),如果没连就连在一起;如果连了这就是多余的线,不做操作。 连接线遍历完之后出现了几个联通分量,就需要联通分量-1条线把他们连起来。 【不用考虑多余的线够不够,直接返回联通分量-1 就好了,因为一开始就判断过了可能性,如果通过的话是一定可能的】
class UF(object):
def __init__(self, n):
self.n = n
self.parent = list(range(n))
self.size = [1] * n # ?
# 连通分量的数量。
self.count = n
def find(self, x):
if x != self.parent[x]:
# self.parent[x] = self.find( self.parent[x] )
self.parent[x] = self.find( self.parent[x] )
return self.parent[x]
# return self.parent # 降低树的高度
return x
def union(self, x, y):
x, y = self.find(x), self.find(y)
if x == y:
return False
if self.size[y] < self.size[x]:
x, y = y, x
self.parent[x] = y # 小的x挂大的上面,大的y是parent。
self.size[y] += self.size[x]
self.count -= 1
return True
def connected(self, x, y):
return self.find(x) == self.find(y)
class Solution(object):
def makeConnected(self, n, connections):
"""
:type n: int
:type connections: List[List[int]]
:rtype: int
"""
uf = UF(n)
if len(connections) < n -1:
return -1
# lines = 0
for node1, node2 in connections:
if uf.find(node1) != uf.find(node2):
uf.union(node1, node2)
else:
continue
return uf.count - 1
时间复杂度:O(m + n)? m:connections 的长度, n:节点数量 官解:时间复杂度:O(m⋅α(n)) ?,其中 m 是数组connections 的长度,α 是阿克曼函数的反函数???。 空间复杂度:O(n)O(n),即为并查集需要使用的空间。
class Solution:
def makeConnected(self, n: int, connections: List[List[int]]) -> int:
if len(connections) < n - 1:
return -1
parent = {}
def find(x):
parent.setdefault(x, x)
if x != parent[x]:
parent[x] = find(parent[x]) # path compression
return parent[x]
def union(x, y):
rootX = find(x)
rootY = find(y)
if rootX == rootY:
return
parent[rootX] = rootY
for a, b in connections:
union(a, b)
k = len(set(map(find, range(n)))) - 1
return k
Connected computers form a connected components. We need to find out the number of connected components CC. If there is only one CC, all computers are connecetd. We don't need any operation. If there are n CC, we need at least n-1 more edges to connected all CCs. If we have greater than or equal to n-1 extra edges in the original network, then we can do n-1 operations to connect. Otherwise, we can't. So use unionfind to count the number of CC, meanwhile also count the extra edges in the orignial network. The extra edges can be obtained by checking if the two computer in connections already have the same root. If yes, this edge is an extra edge.
Time: O(m) m is the length of connections. Space: O(n) n is the number of computer
class Solution {
public int makeConnected(int n, int[][] connections) {
UnionFind uf = new UnionFind(n);
int extra=0;
for(int i=0;i<connections.length;i++) {
int a = connections[i][0];
int b = connections[i][1];
if(uf.find(a)==uf.find(b))
extra++;
else
uf.union(a,b);
}
int cc=uf.count();
if(cc==1)
return 0;
if(extra>=cc-1)
return cc-1;
return -1;
}
class UnionFind {
int[] parent;
int count;
public UnionFind(int n) {
parent = new int[n];
for(int i=0;i<n;i++) {
parent[i]=i;
}
count=n;
}
public int find(int x) {
while(x!=parent[x]) {
parent[x]=parent[parent[x]];
x=parent[x];
}
return parent[x];
}
public void union(int a, int b) {
int aroot = find(a);
int broot = find(b);
if(aroot!=broot) {
parent[broot]=aroot;
}
count--;
}
public int count() {
return count;
}
}
}
和昨天前天一样也是连通数问题,今天11分半 ide free bug free完成了,但是效果差速度慢,优化发现忘了做路径优化更没有实现降秩操作,而且可以用ans来维护当前连通域数量(每次合并操作自减1即可),优化后速度从584ms到了84ms
class Solution:
def makeConnected(self, n: int, connections: List[List[int]]) -> int:
if len(connections)<n-1:
return -1
uf={}
size = {}
ans = n
# for i in range(n):
# uf[i]=i
for i,j in connections:
p,q=i,j
while p!=uf.setdefault(p,p):
p=uf[p]
uf[i]=p
while q!=uf.setdefault(q,q):
q=uf[q]
uf[j]=q
if p!=q:
if size.setdefault(p,1)>=size.setdefault(q,1):
uf[q]=p
size[p]+=size[q]
else:
uf[p]=q
size[q]+=size[p]
ans-=1
return ans-1
var makeConnected = function(n, connections) {
let edges = connections.length;
if(edges < n-1) return -1;
let g = [];
for(let i=0;i<n;i++) g[i] = []
for(let i=0;i<edges;i++){
g[connections[i][0]].push(connections[i][1]);
g[connections[i][1]].push(connections[i][0]);
}
let v = Array(n).fill(0),c=0;
for(let i=0;i<n;i++){
if(!v[i]){
c++;
dfs(i,g,v)
}
}
return c-1;
};
function dfs(i,con,v){
v[i] = 1;
for(let x of con[i]){
if(!v[x]) dfs(x,con,v)
}
}
class Solution {
public int makeConnected(int n, int[][] connections) {
if (connections.length < n - 1) return -1; // To connect all nodes need at least n-1 edges
UF uf = new UF(n);
int count = 0;
for (int[] conn : connections) {
int u = conn[0];
int v = conn[1];
if (uf.find(u) == uf.find(v)) {
count++;
} else {
uf.union(u, v);
}
}
return uf.size - 1 <= count ? uf.size - 1 : -1;
}
private static class UF {
int[] parent;
int size;
public UF (int n) {
parent = new int[n];
size = n;
for (int i = 0; i < n; i++) {
parent[i] = i;
}
}
public int find (int x) {
if (parent[x] != x) {
parent[x] = find(parent[x]);
}
return parent[x];
}
public void union (int x, int y) {
int pX = find(x);
int pY = find(y);
if (pX != pY) {
parent[pY] = pX;
size--;
}
}
}
}
Java Code:
class Solution {
public int makeConnected(int n, int[][] connections) {
if(connections.length < n-1) return -1;
int count = n;
int[] checkAndSet = new int[n];
for(int i = 0;i < n;i++) checkAndSet[i] = i;
for(int[] con : connections){
int index1 = con[0];
int index2 = con[1];
if(find(checkAndSet,index1) != find(checkAndSet,index2)){
union(checkAndSet,index1,index2);
--count;
}
}
return count-1;
}
public void union(int[] checkAndSet,int index1,int index2){
checkAndSet[find(checkAndSet,index1)] = find(checkAndSet,index2);
}
public int find(int[] checkAndSet,int index){
if(checkAndSet[index] != index) checkAndSet[index] = find(checkAndSet,checkAndSet[checkAndSet[index]]);
return checkAndSet[index];
}
}
class Solution
{
class DisjointSet
{
private:
std::vector<int> id;
std::vector<int> sz;
public:
DisjointSet(int n)
{
id.resize(n);
sz.resize(n);
std::iota(id.begin(), id.end(), 0);
std::fill(sz.begin(), sz.end(), 1);
}
void unite(int x, int y)
{
int xId = find(x);
int yId = find(y);
if (xId == yId) return;
if (sz[xId] < sz[yId]) {
id[xId] = id[yId];
sz[yId] += sz[xId];
} else {
id[yId] = id[xId];
sz[xId] += sz[yId];
}
return;
}
int find(int x)
{
if (x != id[x]) {
id[x] = find(id[x]);
}
return id[x];
}
};
public:
int makeConnected(int n, vector<vector<int>> &connections)
{
// n computers at least need n-1 cables
if (connections.size() < n - 1) {
return -1;
}
// build the Disjoint set
DisjointSet ds(n);
for (auto const &c : connections) {
ds.unite(c[0], c[1]);
}
// count the numbers of disjoint sets
int res{ 0 };
for (int ii = 0; ii < n; ii++) {
if (ds.find(ii) == ii) {
res++;
}
}
// res is at most n
// connecting res sets needs res-1 cables
return res - 1;
}
};
并查集检查连通分量
class Solution {
public int makeConnected(int n, int[][] connections) {
int m = connections.length;
if (m < n-1) {
return -1;
}
Map<Integer, Integer> parents = new HashMap<>();
for (int i=0; i<n; i++) {
parents.put(i, null);
}
int ans = n;
for (int[] con: connections) {
// find
int root1 = find(parents, con[0]);
int root2 = find(parents, con[1]);
// union
if (root1 != root2) {
parents.put(root1, root2);
ans--;
}
}
return ans - 1;
}
private int find(Map<Integer, Integer> parents, int node) {
int root = node;
while (parents.get(root) != null) {
root = parents.get(root);
}
while (node != root) {
int oldParent = parents.get(node);
parents.put(node, root);
node = oldParent;
}
return root;
}
}
TC: O(E * Log(V)) SC: O(V)
思路
并查集
遍历connections, 对于当前conn
如果conn[0] 和 conn[1]已经相连, 可用连接数count++
不相连 则union
最后检查UF的size和count做比较,并返回答案
特判:如果n个点相连 则最少需要n-1个边,所以如果connections.length < n - 1,则必然不能联通所有点
Java Code
class Solution {
public int makeConnected(int n, int[][] connections) {
if (connections.length < n - 1) return -1; // To connect all nodes need at least n-1 edges
UF uf = new UF(n);
int count = 0;
for (int[] conn : connections) {
int u = conn[0];
int v = conn[1];
if (uf.find(u) == uf.find(v)) {
count++;
} else {
uf.union(u, v);
}
}
return uf.size - 1 <= count ? uf.size - 1 : -1;
}
private static class UF {
int[] parent;
int size;
public UF (int n) {
parent = new int[n];
size = n;
for (int i = 0; i < n; i++) {
parent[i] = i;
}
}
public int find (int x) {
if (parent[x] != x) {
parent[x] = find(parent[x]);
}
return parent[x];
}
public void union (int x, int y) {
int pX = find(x);
int pY = find(y);
if (pX != pY) {
parent[pY] = pX;
size--;
}
}
}
} 时间&空间 时间 O(n) n = connections.length 并查集find/union 可以认为是O(1)操作 空间 O(n)
class UF:
def __init__(self, n):
self.p = [i for i in range(n)]
self.rank = [1] * n
self.comp = n
def union(self, x, y):
px = self.find(x)
py = self.find(y)
if px != py:
self.comp -= 1
if self.rank[px] < self.rank[py]:
self.p[px] = py
self.rank[py] += self.rank[px]
else:
self.p[py] = px
self.rank[px] += self.rank[py]
def find(self, x):
tx = x
while x != self.p[x]:
x = self.p[x]
while tx != x:
t = self.p[tx]
tx = self.p[tx]
tx = t
return x
class Solution:
def makeConnected(self, n: int, cs: List[List[int]]) -> int:
conn = len(cs)
if conn < n - 1:
return -1
uf = UF(n)
for x, y in cs:
uf.union(x, y)
return uf.comp - 1
class Solution:
availableWires = 0
numComponents = 0
def makeConnected(self, n: int, connections: List[List[int]]) -> int:
self.numComponents = n
parent = [i for i in range(n)]
def find(p):
root = p
while root != parent[root]:
root = parent[root]
return root
def union(p, q):
root1 = find(p)
root2 = find(q)
if root1 == root2:
self.availableWires += 1
return
parent[root2] = root1
self.numComponents -= 1
for node in connections:
union(node[0], node[1])
if self.numComponents - self.availableWires == 1 or self.availableWires >= self.numComponents :
return self.numComponents - 1
return -1
Time: O(E * Log(V)) Space: O(V)
class Solution:
def makeConnected(self, n: int, connections: List[List[int]]) -> int:
root = [i for i in range(n)]
def find(p):
while p != root[p]:
root[p] = root[root[p]]
p = root[p]
return p
def union(p, q):
root[find(p)] = find(q)
have = 0
for connec in connections:
a, b = connec
if find(a) != find(b):
union(a, b)
else:
have += 1
diff_root = set()
for i in range(n):
diff_root.add(find(i))
return len(diff_root) - 1 if have >= len(diff_root) - 1 else -1
冰茶几
class UF:
def __init__(self):
self.parent = {}
self.size = defaultdict(lambda: 1)
def find(self, x):
#Don't foget this one, this is mandatory as lazy init.
ox = x
self.parent.setdefault(x, x)
while x != self.parent[x]:
x = self.parent[x]
self.parent[ox] = x
return x
def merge(self, x, y):
px, py = self.find(x), self.find(y)
if px == py: return
if self.size[px] < self.size[py]:
self.parent[px] = py
self.size[py] += self.size[px]
else:
self.parent[py] = px
self.size[px] += self.size[py]
def is_connected(self, x, y):
return self.find(x) == self.find(y)
class Solution:
def makeConnected(self, n: int, connections: List[List[int]]) -> int:
uf = UF()
extra = []
for u, v in connections:
if uf.is_connected(u, v):
extra.append((u, v))
uf.merge(u,v)
components = set()
for i in range(n):
if uf.size[uf.find(i)] != n:
components.add((uf.find(i), uf.size[uf.find(i)]))
ret = max(len(components) - 1, 0)
return ret if ret <= len(extra) else -1
并查集
JavaScript Code
/**
* @param {number} n
* @param {number[][]} connections
* @return {number}
*/
var makeConnected = function (n, connections) {
// 连接 n 台电脑至少需要 n - 1 根线缆
if (connections.length < n - 1) {
return -1;
}
// 计算联通分量,最小操作次数就是将联通分量链接的次数
let father = Array.from({ length: n }, (v, i) => i);
let count = n;
for (connection of connections) {
union(...connection);
}
return count - 1;
function find(v) {
if (father[v] !== v) {
father[v] = find(father[v]);
}
return father[v];
}
function union(x, y) {
if (find(x) !== find(y)) {
count--;
father[find(x)] = find(y);
// 联通分量数减一
}
}
};
复杂度
思路: 关键是如果边少于顶点数减一,直接返回-1 其他情况就是对整体进行联合,每联合一次,groupNum-- 最后由groupNum-1可以返回答案
func makeConnected(n int, connections [][]int) int {
if len(connections) < n-1{
return -1
}
groupNum := n
parent := make([]int,n)
var findRoot func(x int) int
findRoot = func(x int)int{
if x == parent[x]{
return x
}
parent[x] = findRoot(parent[x])
return parent[x]
}
for i:=0;i<len(parent);i++{
parent[i] = i
}
for i:=0;i<len(connections);i++{
aRoot := findRoot(connections[i][0])
bRoot := findRoot(connections[i][1])
if aRoot != bRoot{
groupNum--
parent[aRoot] = bRoot
}
}
return groupNum-1
}
时间复杂度:O(V+E), 其中 V是顶点数, E是边数 空间复杂度:O(V)
var makeConnected = function(n, connections) {
// 连接 n 台电脑至少需要 n - 1 根线缆
if (connections.length < n - 1) {
return -1;
}
let arr = Array.from({length:n},(v,i) => i);
let count = n;
for(const connection of connections){
union(...connection);
}
function find(x) {
if(arr[x] !== x){
arr[x] = find(arr[x]);
}
return arr[x];
}
function union(x,y){
if(find(x) !== find(y)){
count--;
arr[find(x)] = find(y);
}
}
return count - 1;
};
class Solution:
def makeConnected(self, n: int, connections: List[List[int]]) -> int:
if len(connections) < n - 1:
return -1
self.visited = set()
self.graph = [[] for _ in range(n)]
count = 0
for connection in connections:
self.graph[connection[0]].append(connection[1])
self.graph[connection[1]].append(connection[0])
for i in range(n):
if i not in self.visited:
self.dfs(i)
count += 1
return count - 1
def dfs(self, i):
self.visited.add(i)
for next_node in self.graph[i]:
if next_node not in self.visited:
self.dfs(next_node)
class union_find(object):
def __init__(self,n):
self.root = list(range(n))
self.size = n
def find(self,x):
if self.root[x] ==x:
return x
self.root[x] = self.find(self.root[x])
return self.root[x]
def union(self,x,y):
rootx = self.find(x)
rooty = self.find(y)
if rootx != rooty:
self.root[rooty] = rootx
self.size -=1
def isconnected(self,x,y):
return self.find(x) == self.find(y)
class Solution:
def makeConnected(self, n: int, connections: List[List[int]]) -> int:
num_lines = len(connections)
if num_lines< n -1:
return -1
uf = union_find(n)
for i in range(num_lines):
if uf.isconnected(connections[i][0],connections[i][1]):
continue
uf.union(connections[i][0],connections[i][1])
return uf.size -1
var makeConnected = function (n, connections) {
if (connections.length < n - 1) {
return -1;
}
let groupNum = n;
let parent = new Array(n);
const findRoot = (x) => {
if (x == parent[x]) {
return x;
}
parent[x] = findRoot(parent[x]);
return parent[x];
};
for (let i = 0; i < parent.length; i++) {
parent[i] = i;
}
for (let i = 0; i < connections.length; i++) {
const aRoot = findRoot(connections[i][0]);
const bRoot = findRoot(connections[i][1]);
if (aRoot != bRoot) {
groupNum--;
parent[aRoot] = bRoot;
}
}
return groupNum - 1;
};
var makeConnected = function (n, connections) {
if (connections.length < n - 1) return -1;
let uf = new UnionFind(n);
for (let [i, j] of connections) {
uf.union(i, j);
}
return uf.count - 1;
};
class UnionFind {
constructor(n) {
this.count = n;
this.parent = Array.from({ length: n }, (i, idx) => idx);
}
find(x) {
if (this.parent[x] !== x) {
this.parent[x] = this.find(this.parent[x]);
}
return this.parent[x];
}
union(x, y) {
if (this.find(x) === this.find(y)) return;
this.parent[this.parent[x]] = this.parent[y];
this.count--;
}
https://leetcode-cn.com/problems/number-of-operations-to-make-network-connected/
思考:
class Solution:
def makeConnected(self, n: int, connections: List[List[int]]) -> int:
if len(connections) < n - 1:
return -1
root = [i for i in range(n)]
def find(p):
while p != root[p]:
root[p] = root[root[p]]
p = root[p]
return p
def union(p, q):
root[find(p)] = find(q)
have = 0
for a, b in connections:
if find(a) != find(b):
union(a, b)
else:
have += 1 #联通的点
diff_root = set()
for i in range(n):
diff_root.add(find(i)) #独立集合的个数
return len(diff_root) - 1 if have >= len(diff_root) - 1 else -1
时间复杂度:O(N a(N) 合并与查找时间复杂度为 O(logx)和 O(logy)
空间复杂度:O(n)
/**
* @param {number} n
* @param {number[][]} connections
* @return {number}
*/
const makeConnected = function(n, connections) {
const dsu = new DSU(n);
let extraEdges = 0;
for (const [a, b] of connections) {
const currCount = dsu.count;
dsu.union(a, b);
extraEdges += dsu.count === currCount ? 1 : 0; // extra connection if a and b are already linked
}
const unlinkedComputers = dsu.count - 1; // substract one from linked group
return extraEdges >= unlinkedComputers ? unlinkedComputers : -1;
};
class DSU {
constructor(n) {
this.parent = Array.from(Array(n).keys());
this.count = n;
}
find(x) {
if (this.parent[x] !== x) {
this.parent[x] = this.find(this.parent[x]);
}
return this.parent[x];
}
union(x, y) {
const rootX = this.find(x);
const rootY = this.find(y);
if (rootX !== rootY) {
this.parent[Math.min(rootX, rootY)] = Math.max(rootX, rootY);
this.count -= 1;
}
}
}
就简单的运用交叉集,但主要返回-1的情况,线段小于点的个数减一则必不能,还有就是进行合并集合的时候,进行一下判断可以提高运行效率(力扣上35ms->2ms)
class Solution { int[]parent; public int makeConnected(int n, int[][] connections) { if(connections.length<n-1)return -1; //使用交查集 connection是边 边的长度 必须大于等于n-1 int size = n; parent = new int[size]; //初始化 int res = size; for(int i=0;i<size;i++){ parent[i] = i; } for(int i=0;i<connections.length;i++){ if(unite(connections[i][0],connections[i][1])){ res--; } } return res-1; } private int findset(int x){ return parent[x]==x?x:(findset(parent[x])); } private boolean unite(int a,int b){ int x = findset(a); int y = findset(b); if(x==y)return false; //这个判断可以起到优化的作用 if(x<y){ parent[y]=x; }else{ parent[x]=y; } return true; } }
时间复杂度:O(elogv)
空间复杂度:O(v)
https://leetcode.com/problems/number-of-operations-to-make-network-connected/
-DFS
DFS
class Solution:
def makeConnected(self, n: int, connections: List[List[int]]) -> int:
# 排除边不够的情况
if len(connections) < n - 1:
return -1
self.visited = set()
#建图
self.graph = [[] for _ in range(n)]
count = 0
for connection in connections:
self.graph[connection[0]].append(connection[1])
self.graph[connection[1]].append(connection[0])
#逐个node排查,找出联通子图个数
for i in range(n):
if i not in self.visited:
self.dfs(i)
count += 1
return count - 1
#用dfs来找联通子图
def dfs(self, i):
self.visited.add(i)
for next_node in self.graph[i]:
if next_node not in self.visited:
self.dfs(next_node)
时间复杂度: O(N) 空间复杂度:O(N)
连接n个网络,需要n-1条线,如果线少于n-1肯定是无法连通的。如果已经有k块连通分量了,那只需要k-1根线把他们连起来
class UnionFind:
def __init__(self, n):
self.parent = {}
self.size = {}
self.cnt = 0
for i in range(n):
self.parent[i] = i
self.size[i] = 1
self.cnt += 1
def find(self, x):
while x != self.parent[x]:
self.parent[x] = self.find(self.parent[x])
return self.parent[x]
return x
def union(self, p, q):
parent_p, parent_q = self.find(p), self.find(q)
if parent_p == parent_q:
return
if self.size[parent_p] > self.size[parent_q]:
self.parent[parent_q] = self.parent[parent_p]
self.size[parent_p] += self.size[parent_q]
else:
self.parent[parent_p] = self.parent[parent_q]
self.size[parent_q] += self.size[parent_p]
self.cnt -= 1
class Solution:
def makeConnected(self, n: int, connections: List[List[int]]) -> int:
# n个节点起码要n-1根线
if len(connections) < n-1:
return -1
uf = UnionFind(n)
for i, j in connections:
uf.union(i, j)
return uf.cnt - 1
并查集
class Solution {
public int makeConnected(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;
}
}
// 并查集模板
class UnionFind {
int[] parent;
int[] size;
int n;
// 当前连通分量数目
int setCount;
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 : (parent[x] = findset(parent[x]));
}
public boolean unite(int x, int y) {
x = findset(x);
y = findset(y);
if (x == y) {
return false;
}
if (size[x] < size[y]) {
int temp = x;
x = y;
y = temp;
}
parent[y] = x;
size[x] += size[y];
--setCount;
return true;
}
public boolean connected(int x, int y) {
x = findset(x);
y = findset(y);
return x == y;
}
}
查并集复制题解
class Solution {
public int makeConnected(int n, int[][] connections) {
int m = connections.length;
UnionFind uf = new UnionFind(n);
// 非必要连接
int noNeedNum = 0;
for(int i=0;i<m;i++){
int from = connections[i][0];
int to = connections[i][1];
if(uf.find(from)==uf.find(to)) noNeedNum++; // 不是必要的连接
else uf.union(from,to); // 是必要的连接
}
if(uf.size==1) return 0; // 这些连接已经可以完成整体连接
if(noNeedNum >= uf.size - 1) return uf.size - 1; // 非必要连接足够支持剩余连通分量
return -1;
}
}
class UnionFind{
int size;
int[] parent;
public UnionFind(int n){
size = n;
parent = new int[n];
for(int i=0;i<n;i++){
parent[i]=i;
}
}
public int find(int node){
if(parent[node]==node) return node;
else return parent[node]=find(parent[node]);
}
public void union(int x,int y){
int rootX = find(x);
int rootY = find(y);
if(rootX==rootY) return ;
parent[rootX] = rootY;
size--;
}
}
时间复杂度:O(N) 空间复杂度:O(N)
/**
@return {number} */ var makeConnected = function(n, connections) { if (connections.length < n - 1) { return -1; }
const edges = new Map(); for (const [x, y] of connections) { edges.get(x) ? edges.get(x).push(y) : edges.set(x, [y]); edges.get(y) ? edges.get(y).push(x) : edges.set(y, [x]); }
const used = new Array(n).fill(0);
let ans = 0; for (let i = 0; i < n; i++) { if (!used[i]) { dfs(i, used, edges); ans++; } } return ans - 1; };
const dfs = (u, used, edges) => { used[u] = 1; if (edges.get(u)) { for (const v of edges.get(u)) { if (!used[v]) { dfs(v, used, edges); } } } }
class Solution:
def makeConnected(self, n: int, connections: List[List[int]]) -> int:
root = [i for i in range(n)]
def find(p):
while p != root[p]:
root[p] = root[root[p]]
p = root[p]
return p
def union(p, q):
root[find(p)] = find(q)
have = 0
for connec in connections:
a, b = connec
if find(a) != find(b):
union(a, b)
else:
have += 1
diff_root = set()
for i in range(n):
diff_root.add(find(i))
return len(diff_root) - 1 if have >= len(diff_root) - 1 else -1
DFS
class Solution:
def makeConnected(self, n: int, connections: List[List[int]]) -> int:
if len(connections) < n-1: return -1
isolated = 0
G = [set() for _ in range(n)]
visited = [False] * n
for i,j in connections:
G[i].add(j)
G[j].add(i)
def dfs(i):
if visited[i] : return 0
visited[i] = True
for j in G[i] :
#visited[j] = True
dfs(j)
return 1
for i in range(n):
if not visited[i] : isolated += dfs(i)
return isolated - 1
Space: O(n) Time: O(max(n, connections))
···python3 class Solution: def makeConnected(self, n: int, connections: List[List[int]]) -> int: root = [i for i in range(n)]
def find(p):
while p != root[p]:
root[p] = root[root[p]]
p = root[p]
return p
def union(p, q):
root[find(p)] = find(q)
have = 0
for connec in connections:
a, b = connec
if find(a) != find(b):
union(a, b)
else:
have += 1
diff_root = set()
for i in range(n):
diff_root.add(find(i))
return len(diff_root) - 1 if have >= len(diff_root) - 1 else -1
用union find 找有多少个连通图假设为k, 那么k-1就是所求 前提connections 里面要有至少n-1个边
class UF:
def __init__(self, n) -> None:
self.parent = {i: i for i in range(n)}
def find(self, i):
if self.parent[i] != i:
self.parent[i] = self.find(self.parent[i])
return self.parent[i]
def connect(self, i, j):
root_i, root_j = self.find(i), self.find(j)
if root_i != root_j:
self.parent[root_i] = root_j
class Solution:
def makeConnected(self, n: int, connections: List[List[int]]) -> int:
if len(connections) < n - 1:
return -1
uf = UF(n)
for node1, node2 in connections:
uf.connect(node1, node2)
root_set = set()
for node in range(n):
root_set.add(uf.find(node))
return len(root_set) - 1
m为connection的个数 Time O(m+n) Space O(n)
1319. 连通网络的操作次数
https://leetcode-cn.com/problems/number-of-operations-to-make-network-connected/
思路: 1.n台计算机连接成一个网络至少要n-1根线缆; 2.n台计算机连接成一个网络,等用于整个网络只有一个连通分量
代码: 1.特判: 已知线缆数目是否小于n-1 2.根据已知线缆connections构建图,求连通分量个数N 3.最少操作次数等同于N-1,即将N个连通分量相连需要N-1根线缆。
class Solution:
def makeConnected(self, n: int, connections: List[List[int]]) -> int:
if len(connections) < n-1: return -1
parents = list(range(n))
def find(x):
if parents[x] != x:
parents[x] = find(parents[x])
return parents[x]
def union(x, y):
parents[find(y)] = parents[x]
for x, y in connections:
root_x, root_y = find(x), find(y)
if root_x != root_y:
parents[root_y] = root_x
return sum([find(x) == x for x in range(n)]) - 1
var makeConnected = function (n, connections) {
// 连接 n 台电脑至少需要 n - 1 根线缆
if (connections.length < n - 1) {
return -1;
}
// 计算联通分量,最小操作次数就是将联通分量链接的次数
let father = Array.from({ length: n }, (v, i) => i);
let count = n;
for (connection of connections) {
union(...connection);
}
return count - 1;
function find(v) {
if (father[v] !== v) {
father[v] = find(father[v]);
}
return father[v];
}
function union(x, y) {
if (find(x) !== find(y)) {
count--;
father[find(x)] = find(y);
// 联通分量数减一
}
}
};
并查集
class Solution{
public int makeConnected(int n, int[][] connections) {
if (connections.length < n - 1) {
return -1;
}
UnionFind uf = new UnionFind(n);
for (int[] connection : connections) {
uf.union(connection[0], connection[1]);
}
return uf.rest>=uf.size - 1?uf.size - 1:-1;
}
class UnionFind {
private int rest = 0;
private int size;
private int[] parent;
public UnionFind(int size) {
this.size = size;
this.parent = new int[size];
for (int i = 0; i < size; i++) {
parent[i] = i;
}
}
public int find(int i) {
if (parent[i] != i) {
parent[i] = find(parent[i]);
}
return parent[i];
}
public void union(int i, int j) {
int pi = find(i);
int pj = find(j);
if (pi != pj) {
size--;
if (pi > pj) {
parent[pi] = pj;
} else {
parent[pj] = pi;
}
}else{
rest++;
}
}
}
}
In order to find out if we could rewire the cables, we need to know how many extra cables do we have. During iteration connections, if two computers have been indirectely connected, we know that the cable between them is an extra cable that we could use to connect other disconnected computers. After found out the number of extra cables, we just need to find out how many set of networks we have, if we have more cables than the seperate networks, we could reroute the cables, thus we return the number of disconnected networks, otherwise, return - 1;
class Solution {
public:
vector<int> parent;
int find(int i) {
int p = parent[i];
while (p != parent[p])
p = parent[p];
return p;
}
void union_node(int i, int j) {
int pi = find(i);
int pj = find(j);
if (pi == pj)
return;
if (pi < pj)
parent[pj] = pi;
else
parent[pi] = pj;
}
int makeConnected(int n, vector<vector<int>>& connections) {
parent = vector<int>(n);
// initially, each node's parent is itself
for (int i = 0; i < n; i++)
parent[i] = i;
int n_dup = 0;
for(vector<int>& conn : connections) {
// (x, y) already connected, we can use its cable to connect others.
if (find(conn[0]) == find(conn[1])) {
n_dup++;
} else {
union_node(conn[0], conn[1]);
}
}
int n_tree = 0;
set<int> counter;
for (int i = 0; i < n; i++) {
int p = find(i);
if (!counter.count(p)) {
n_tree++;
counter.insert(p);
}
}
return n_dup >= n_tree - 1? n_tree - 1 : -1;
}
};
class Solution:
def makeConnected(self, n: int, connections: List[List[int]]) -> int:
if len(connections) < n-1: return -1
parents = list(range(n))
def find(x):
if parents[x] != x:
parents[x] = find(parents[x])
return parents[x]
def union(x, y):
parents[find(y)] = parents[x]
for x, y in connections:
root_x, root_y = find(x), find(y)
if root_x != root_y:
parents[root_y] = root_x
return sum([find(x) == x for x in range(n)]) - 1
思路: 不同的电脑连接成连通分量(网络),计算连通分量数M, 连接M个网络,需要M-1条线,如果线少于M-1肯定是无法连通的
复杂度分析:
代码(C++):
class DSU {
public:
vector<int> parent;
int network;
DSU(int n) {
parent.resize(n);
for (int i = 0; i < n; ++i)
parent[i] = i;
network = n;
}
int Find(int x) {
if (x != parent[x]) {
parent[x] = Find(parent[x]);
}
return parent[x];
}
void Union(int x, int y) {
int parentX = Find(x);
int parentY = Find(y);
if (parentX != parentY) {
parent[parentX] = parentY;
network--;
}
}
};
class Solution {
public:
int makeConnected(int n, vector<vector<int>>& connections) {
if (n > connections.size() + 1) return -1;
DSU dsu(n);
for (auto& c : connections) {
dsu.Union(c[0], c[1]);
}
return dsu.network - 1;
}
};
class Solution:
def makeConnected(self, n: int, connections: List[List[int]]) -> int:
if len(connections) < n - 1:
return -1
parent = {}
def find(x):
parent.setdefault(x, x)
if x != parent[x]:
parent[x] = find(parent[x]) # path compression
return parent[x]
def union(x, y):
rootX = find(x)
rootY = find(y)
if rootX == rootY:
return
parent[rootX] = rootY
for a, b in connections:
union(a, b)
k = len(set(map(find, range(n)))) - 1
return k
并查集。根据结论形成一个连通分量所需的最小边数等于顶点数 - 1。先判断能否形成一个连通分量。如果不行返回-1。如果可以,则求现有的连通分量总个数,总个数 - 1即为形成连通分量还需要的边数,即为最后结果。
class Solution {
class UnionFind {
int[] parent;
public UnionFind(int n) {
parent = new int[n];
for (int i = 0; i < n; i++) {
parent[i] = i;
}
}
public int find(int i) {
if (parent[i] != i) {
parent[i] = find(parent[i]);
}
return parent[i];
}
public void union(int i1, int i2) {
int x = find(i1);
int y = find(i2);
parent[x] = y;
}
}
public int makeConnected(int n, int[][] connections) {
int edges = connections.length; // number of edges
// the minimum number of edges needed to form a connected component is number of nodes - 1
if (edges < n - 1) return -1;
UnionFind uf = new UnionFind(n);
// connect based on edges
for (int[] connection: connections) {
uf.union(connection[0], connection[1]);
}
// count the number of different connected components
Set<Integer> roots = new HashSet<>();
for (int i = 0; i < n; i++) {
int root = uf.find(i);
roots.add(root);
}
// calculate the number of connected components to union in order to form only one connected component
return roots.size() - 1;
}
}
复杂度分析
1319. 连通网络的操作次数
入选理由
暂无
题目地址
https://leetcode-cn.com/problems/number-of-operations-to-make-network-connected/
前置知识
暂无
题目描述