LintCode 590. Connecting Graph II (并查集经典题!!!)

本文介绍了一种使用并查集算法解决图连接和查询问题的方法。通过维护节点的父节点和节点数量,实现高效地连接图中两个节点,并能快速查询包含特定节点的连通组件大小。文章详细解释了算法实现细节,包括路径压缩和组件合并的正确操作。
  1. Connecting Graph II
    Given n nodes in a graph labeled from 1 to n. There is no edges in the graph at beginning.

You need to support the following method:

connect(a, b), an edge to connect node a and node b
query(a), Returns the number of connected component nodes which include node a.
Example
5 // n = 5
query(1) return 1
connect(1, 2)
query(1) return 2
connect(2, 4)
query(1) return 3
connect(1, 4)
query(1) return 3

思路:
经典并查集算法
注意:

  1. 在query()里面必须返回nodeNum[find(a)]而不是nodeNum[father[a]]。原因是当a的component跟另一个component合并时,father[a]可能还没更新。举例如下:
    input case:
    ConnectingGraph2(5)
    query(1)
    connect(1, 2)
    query(1)
    connect(2, 4) – connect(2,4)后,1的father[]还未更新
    query(1) – 如果直接返回nodeSum[father[1]]则出错!!!
    connect(1, 4)
    query(1)

同样,connect()里面也不能用father[a],必须用find(a)。
2) path compression的条件也可以写成while(x2!=father[x2]),但稍慢。
3) connect()里面是father[fatherA]=fatherB,不要写成father[a]=father[b]或father[a]=fatherB之类的。

代码如下:

class ConnectingGraph2 {
public:
    /*
    * @param n: An integer
    */
    ConnectingGraph2(int n) {
        father.resize(n + 1);
        nodeNum.resize(n + 1);
        for (int i = 1; i <= n; ++i) {
            father[i] = i;
            nodeNum[i] = 1;
        }
    }

    /*
     * @param a: An integer
     * @param b: An integer
     * @return: nothing
     */
    void connect(int a, int b) {
        int rootA = find(a);   //不能用father[a]
        int rootB = find(b);   //不能用father[b]
        if (rootA != rootB) {
            father[rootA] = rootB;
            nodeNum[rootB] += nodeNum[rootA];
        }
    }

    /*
     * @param a: An integer
     * @return: An integer
     */
    int query(int a) {
    //    return nodeNum[father[a]];    //wrong!!!
        return nodeNum[find(a)];
    }
    
private:
    vector<int> father;
    vector<int> nodeNum;
    
    //find the root of x
    int find(int x) {
        int x2 = x;
        
        if (father[x] == x) return x;
        
        while(father[x] != x) {
            x = father[x];
        }
        
        //path compression
        while (x2 != x) {
            int temp = father[x2];
            father[x2] = x;
            x2 = temp;
        }
        
        return x;
    }
};
根据原作 https://pan.quark.cn/s/459657bcfd45 的源码改编 Classic-ML-Methods-Algo 引言 建立这个项目,是为了梳理和总结传统机器学习(Machine Learning)方法(methods)或者算法(algo),和各位同仁相互学习交流. 现在的深度学习本质上来自于传统的神经网络模型,很大程度上是传统机器学习的延续,同时也在不少时候需要结合传统方法来实现. 任何机器学习方法基本的流程结构都是通用的;使用的评价方法也基本通用;使用的一些数学知识也是通用的. 本文在梳理传统机器学习方法算法的同时也会顺便补充这些流程,数学上的知识以供参考. 机器学习 机器学习是人工智能(Artificial Intelligence)的一个分支,也是实现人工智能最重要的手段.区别于传统的基于规则(rule-based)的算法,机器学习可以从数据中获取知识,从而实现规定的任务[Ian Goodfellow and Yoshua Bengio and Aaron Courville的Deep Learning].这些知识可以分为四种: 总结(summarization) 预测(prediction) 估计(estimation) 假想验证(hypothesis testing) 机器学习主要关心的是预测[Varian在Big Data : New Tricks for Econometrics],预测的可以是连续性的输出变量,分类,聚类或者物品之间的有趣关联. 机器学习分类 根据数据配置(setting,是否有标签,可以是连续的也可以是离散的)和任务目标,我们可以将机器学习方法分为四种: 无监督(unsupervised) 训练数据没有给定...
### 检查最小生成树是否唯一的方法 要确定一个连通无向图的最小生成树(MST)是否唯一,可以通过 Kruskal 算法或 Prim 算法结合并查集(Union-Find)数据结构实现。以下是具体方法: #### 1. 使用 Kruskal 算法检查 MST 的唯一性 Kruskal 算法通过按权重排序边并选择不形成环的边来构建 MST。为了检查 MST 是否唯一,可以扩展 Kruskal 算法如下: - 首先对所有边按权重升序排序。 - 在构建 MST 的过程中,对于每条权重为 \( w \) 的边,记录有多少条这样的边被加入到 MST 中。 - 如果存在多条权重相同的边可以选择,且这些边的选择会影响最终的 MST,则说明 MST 不唯一。 具体实现中,可以在 Union-Find 数据结构中跟踪每次合并操作,并检查是否有多种方式选择边[^1]。 ```python def is_mst_unique(edges, n): # Sort edges by weight edges.sort(key=lambda x: x[2]) # (u, v, weight) parent = list(range(n)) rank = [0] * n def find(u): while parent[u] != u: parent[u] = parent[parent[u]] u = parent[u] return u def union(u, v): root_u = find(u) root_v = find(v) if root_u == root_v: return False if rank[root_u] > rank[root_v]: parent[root_v] = root_u elif rank[root_u] < rank[root_v]: parent[root_u] = root_v else: parent[root_v] = root_u rank[root_u] += 1 return True mst_weight = 0 count = 0 unique = True i = 0 while count < n - 1 and i < len(edges): u, v, weight = edges[i] j = i # Count how many edges have the same weight while j < len(edges) and edges[j][2] == weight: j += 1 same_weight_edges = edges[i:j] valid_edges = [] for edge in same_weight_edges: u, v, _ = edge if union(u, v): valid_edges.append(edge) if len(valid_edges) > 1: unique = False mst_weight += sum(edge[2] for edge in valid_edges) count += len(valid_edges) i = j return unique if count == n - 1 else False ``` #### 2. 使用 Prim 算法检查 MST 的唯一性 Prim 算法从一个顶点开始逐步扩展 MST。类似地,可以通过以下方法检查 MST 是否唯一: - 在每个步骤中,检查是否存在多条权重相同的边可以加入当前的 MST。 - 如果存在多条这样的边,则说明 MST 不唯一。 在实现中,可以使用优先队列(如堆)来选择最小权重的边,并记录每次选择时的候选边数量[^1]。 ```python import heapq def is_mst_unique_prim(graph, n): visited = [False] * n pq = [(0, 0)] # (weight, vertex) mst_weight = 0 unique = True while pq: weight, u = heapq.heappop(pq) if visited[u]: continue visited[u] = True mst_weight += weight candidates = [] for v, w in graph[u]: if not visited[v]: candidates.append((w, v)) candidates.sort() if len(candidates) > 1 and candidates[0][0] == candidates[1][0]: unique = False for w, v in candidates: heapq.heappush(pq, (w, v)) return unique if all(visited) else False ``` ### 总结 通过扩展 Kruskal 或 Prim 算法,可以在构建 MST 的过程中检查是否存在多条权重相同的边可以被选择。如果存在这样的情况,则说明 MST 不唯一。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值