2021-01-23 1319.连通网络的操作次数 [并查集,DFS](连通分量)

1319.连通网络的操作次数

思路一:并查集

好吧,我不说了,这个月看到题目第一个思路就是并查集。
主要分两类情况讨论:

  1. 鉴于连接n个电脑至少要n-1条线,所以如果线的数量 connections.length < 电脑数量 n-1 ,则我们输出(-1)表示一定不可能。
  2. 否则,我们直到现在的线的条数一定大于等于n-1条。

我们注意到,每个含有 n i n_i ni台电脑的Cluster(即相连通的电脑群)只需要 n i − 1 n_i-1 ni1条线,记 C C C为所有Cluster的集合,则包含 ∣ C ∣ |C| C个Cluster至少需要
∑ i ∈ ∣ C ∣ ( n i − 1 ) = ∑ i ∈ ∣ C ∣ n i − ∣ C ∣ = n − ∣ C ∣ \sum_{i \in |C|} (n_i -1) = \sum_{i\in|C|} n_i -|C| = n-|C| iC(ni1)=iCniC=nC
条线。
又因为,我们这种情况下一定能连接,所以至少有 n − 1 n-1 n1条线,所以,一定有 ( n − 1 ) − ( n − ∣ C ∣ ) = ∣ C ∣ − 1 (n-1)-(n-|C|)=|C|-1 (n1)(nC)=C1条线多出来,我们用每一条线就可以减少一个Cluster,因此,只要用 ∣ C ∣ − 1 |C|-1 C1条线就可以将原先 ∣ C ∣ |C| C个类别变成1个类别。
因此,我们这种情况下返回的是 ∣ C ∣ − 1 |C|-1 C1

class Solution {
    
    
    public int makeConnected(int n, int[][] connections) {
    
    
        // 如果线的数量 connections.length  < 电脑数量 n-1 
        if(connections.length < n-1)
            return -1;
       
        // 我感觉需要转接的线就等于 总类别(联通算一类)数量-1。
        UnionFind uf = new UnionFind(n);
        for(int[] connection : connections)
            uf.union(connection[0],connection[1]);
        
        return uf.countClasses()-1;
    }
}

class UnionFind{
    
    
    int[] elements;
    int[] rank;
    
    public UnionFind(int N){
    
    
        this.elements = new int[N];
        this.rank = new int[N];
        for(int i=0;i<N;i++)
            this.elements[i]=i;
    }

    public int find(int i){
    
    
        if(elements[i]==i)   
            return i;
        else{
    
    
            elements[i]=find(elements[i]);
            return elements[i];
        }
    }

    public void union(int i, int j){
    
    
        int parenti = find(i);
        int parentj = find(j);
        if(rank[parenti]<rank[parentj])
            elements[parenti]=parentj;
        else if(rank[parenti]>rank[parentj])
            elements[parentj]=parenti;
        else{
    
    
            elements[parentj]=parenti;
            rank[parenti]++;
        }
    }

    public int countClasses(){
    
    
        int res=0;
        for(int i=0;i<elements.length;i++)
            if(elements[i]==i)
                res++;
        return res;
    }
}

思路二:DFS

  • 第一步:构建邻接表
  • 第二步:用DFS找连通分量
class Solution {
    
    
    List<Integer>[] edges; // 用来记录和当前节点相邻的节点
    boolean[] used; //用来记录每个顶点是否被经过了

    public int makeConnected(int n, int[][] connections) {
    
    
        // 如果线的数量 connections.length  < 电脑数量 n-1 
        if(connections.length < n-1)
            return -1;

        used = new boolean[n];
        edges = new List[n];
        for(int i=0;i<n;i++)
            edges[i] = new ArrayList<Integer>();
        for(int[] connection : connections){
    
    
            edges[connection[0]].add(connection[1]);
            edges[connection[1]].add(connection[0]);
        }
        
        int classes = 0; //用来记录类别数量
        for(int i=0;i<n;i++){
    
    
            // 用DFS将没有被经过的节点的邻居全遍历一遍
            if(!used[i]){
    
    
                dfs(i);
                classes++;
            }
        }

        return classes-1;
    }

    public void dfs(int i){
    
    
        used[i] = true;
        for(Integer neighbor : edges[i]){
    
    
            if(!used[neighbor])
                dfs(neighbor);
        }
    }
}

收获:DFS的模版

List<Integer>[] edges; //edges[i]存储和顶点i相连的顶点的编号
boolean[] visited; // 记录点i是否遍历过

// 初始化edges
edges = new List[n]; // n为所有顶点数量
for(int i=0;i<n;i++)
	edges = new ArrayList<Integer>();
// 初始化权重
for(...)
	edges[i].add(j);
	edges[j].add(i);

public void dfs(int i){
    
    
	visited[i] = true;
	for(Integer neighbor: edges[i]){
    
    
		if(!visited[neighbor]){
    
    
			dfs(neighbor);
		}
	}
}

猜你喜欢

转载自blog.csdn.net/weixin_44495738/article/details/113064709