leetcode 1319.连通网络的操作次数

PS:算法并非原创,仅作个人学习记录使用,侵删

题目描述
leetcode 1319.连通网络的操作次数_第1张图片
题目完整描述

算法分析
乍看题目,应该又是图论方面的知识,可能又需要用到并查集。
这里如果已经连接的那个连通分量中有环的话,则可以拆掉用来连接这个连通分量和一个电脑,所以应该就是统计除了这个连通分量之外的电脑数量A,和连通分量中的环个数B相比较,如果A>B,说明可拆卸的电缆数目不足,如果A<=B,说明可以连接所有电脑,且结果为A。
看了大佬们的题解之后,我也发现了自己思维方式里面的局限性,那就是没有考虑多个独立的连通分量(且成员数大于1)的可能。
实际上,随着电脑规模的扩大,可能会有多个独立的连通分量(且成员数大于1)。这是,我们需要做的就是对于每个连通分量都查找环,取出多余的边进行多个连通分量之间的连接。最终的结果要么是-1,要么最小连接数是连通分量数目-1。

所以题目的逻辑就变成了:
1、遍历图,记录所有的连通分量及可移动边数。连通分量个数记作K,一个连通分量的可移动边的总数是边数-(顶点数+1)(因为连接顶点数为D的边的最小值是D-1,超过的部分都是成环边,可以拆下来而不影响连通性)。
2、所有连通分量的可移动边总数就是我们可以用来连接电脑的电缆总数A。
3、如果电缆总数A<连通分量总数K-1,那么说明电缆无法满足最小需求,返回-1;
如果电缆总数A>=连通分量总数K-1,那么说明电缆可以满足最小需求,返回K-1。

因此,问题主要就在于图的连通分量查找统计,这一步正是我们可以用并查集、DFS、BFS实现的算法。
C\Java解法使用了并查集;C++使用的是DFS,python使用的是BFS。

代码实现
【C】

/*
并查集
*/
void InitGroupMaster(int *group, int n){
     //初始化并查集的小组,每位成员所属的小组长就是自己
    for (int i = 0; i < n; i++) {
     
        group[i] = i;
    }
    return;
}

int FindMaster(int *group, int pos){
     //找到pos成员的小组长
    if (pos != group[pos]) {
     
        group[pos] = FindMaster(group, group[pos]);
    }
    return group[pos];
}

int UnionGroup(int *group, int** connections, int connectionsSize){
     //连接图中的顶点形成连通分量,这里不同于一般的并查集写法,而是将遍历图的过程放在函数中。
    int res = 0;
    for (int i = 0; i < connectionsSize; i++) {
     
        int x = FindMaster(group, connections[i][0]);
        int y = FindMaster(group, connections[i][1]);
        if (x != y) {
     
            group[x] = y;
            res++;//连通分量的个数
        }
    }
    return res;
}

int makeConnected(int n, int** connections, int connectionsSize, int* connectionsColSize){
     
    if (connectionsSize < (n - 1)) {
     //如果边的个数<顶点总数-1,必然无法连接所有点,因为n-1是最小的保障
        return -1;
    }

    int *group = (int *)malloc(n * sizeof(int));//并查集,存储组员和组长的对应信息
    InitGroupMaster(group, n);//初始化并查集

    int cnt = UnionGroup(group, connections, connectionsSize);//统计连通分量个数
    return (n - 1 - cnt);//用线的数目 - 集合的个数 = 需要连接的线路
 }

C语言参考网址

【C++】

/*DFS:深度优先遍历*/
class Solution {
     
private:
    vector<vector<int>> edges;
    vector<int> used;

public:
    void dfs(int u) {
     
        used[u] = true;
        for (int v: edges[u]) {
     
            if (!used[v]) {
     
                dfs(v);
            }
        }
    }
    
    int makeConnected(int n, vector<vector<int>>& connections) {
     
        if (connections.size() < n - 1) {
     //宏观判断
            return -1;
        }
        edges.resize(n);
        for (const auto& conn: connections) {
     //遍历图,并初始化顶点之间的邻接关系(为了进行深度优先遍历做的准备
            edges[conn[0]].push_back(conn[1]);
            edges[conn[1]].push_back(conn[0]);
        }
        
        used.resize(n);
        int ans = 0;
        for (int i = 0; i < n; ++i) {
     //得出连通分量的个数
            if (!used[i]) {
     
                dfs(i);
                ++ans;
            }
        }
        
        return ans - 1;//这里感觉比较欠妥,似乎没有考虑可移动的边无法满足连接的情况
    }
};

C++参考网址

【Java】

/*
并查集,和C解法类似,不做赘述
*/
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;
    }
}

Java参考网址

【python】

#BFS:广度优先遍历
from collections import deque
class Solution:
    def makeConnected(self, n, connections):
        num_component = 0  #G中连通分量的数目
        num_remains = 0    #G中冗余边的数目
        
        #构建图G的邻接表表示:
        G = []
        for i in range(0, n):
            G.append([])
        for pair in connections:
            G[pair[0]].append(pair[1])
            G[pair[1]].append(pair[0])
        
        #以BFS统计G中连通分量和冗余边的数目:
        color = [0] * n
        for computer in range(0, n):
            if(color[computer] != 0):
                continue
            #对新发现的包含computer的连通分量进行遍历
            num_component += 1
            num_node = 0    #该连通分量中的结点数
            num_degree = 0  #该连通分量中所有结点度的和
            Q = deque([])
            Q.append(computer)
            color[computer] = 1
            while(len(Q) != 0):
                u = Q.popleft()
                num_node += 1
                for v in G[u]:
                    if not color[v]:
                        color[v] = 1
                        Q.append(v)
                    num_degree += 1
            num_remains += (num_degree / 2) - (num_node - 1)
        
        #判断是否可行:
        if num_component - 1 <= num_remains:
            return num_component - 1
        else:
            return -1

python参考网址

你可能感兴趣的:(Leetcode,算法,图论,dfs,bfs,leetcode)