【路飞】~并查集入门题

158 阅读4分钟

这两天刷算法,又被一个新名词“并查集“所充斥
后来经过了解,现在我能明白大概的意思就是同时进行查询与合并操作,下面结合两道比较好理解的题目来看一下

990. 等式方程的可满足性

这道题题意大致就是说,会给一个数组,每个元素啥有固定长度为4的字符串组成的,例如:['a==b','a!==b'],其中只含有等号于不等号,并且元素下标位置啥固定的,但a,b是变量数量不定
初步一分析,通过暴力双循环记录所有的变量映射关系啥可以解的,但这里我用并查集的解法解
大致思路就是,将双等号左右两端的标记为同一个标识,反之即不是相等关系

var equationsPossible = function(equations) {
    let map = {} // 定义一个map存放每个变量的映射
    
    //查的操作
    let find = (x) => {
        if(!map[x]){
            return map[x] = x
        }
        // 这里是一种优化的写法:路径压缩
        return map[x] === x ?  map[x] : map[x] = find(map[x]) 
    }
    // 并的操作
    let union = (x,y)=>{
        x = find(x)
        y = find(y)
        map[x] = y
    }
    // [c1,c2,c3,c4] 是解构的写法由于每个元素是由固定长度为4的字符串 所以可以这么写
    for(let [c1,c2,c3,c4] of equations){
        //判断是否是等于号,如果是等于号,则合并统一一个标识
        if(c2 === '='){
            union(c1,c4)
        }
    }

    for(let [c1,c2,c3,c4] of equations){
        // 这里通过find函数 查找出是否属于同一个标识
        let x = find(c1)
        let y = find(c4)
        // 如果是非等号,那两个变量不的标识是不一样的,若一样则代表不满足等式方程
        if(c2 === '!'){
            if(x === y){
                return false
            }
        }
    }
    return true
};

684. 冗余连接

给定往一棵 n 个节点 (节点值 1~n) 的树中添加一条边后的图。添加的边的两个顶点包含在 1 到 n 中间,且这条附加的边不属于树中已存在的边。图的信息记录于长度为 n 的二维数组 edges ,edges[i] = [ai, bi] 表示图中在 ai 和 bi 之间存在一条边。 请找出一条可以删去的边,删除后可使得剩余部分是一个有着 n 个节点的树
大致就是说,点根点之间通过线连接后,是否能去除掉其中的两点之间的一根线,还不影响所有点的连通关系
实现逻辑上 与上一题大致是一样的 只不过场景不同,最后需要处理的步骤不一样

var findRedundantConnection = function(edges) {
    // 由于题目给定的范围是3-1002 所以这里数组设置为1005的长度
    let parent  = new Array(1005).fill(0)
    // 初始化每个点的标识
    for(let i = 0; i < parent.length; i++){
        parent[i] = i
    }
    // 查的操作 一样采取路径压缩
    let find = (idx)=>{
        return parent[idx] === idx ? idx : parent[idx] = find(parent[idx])
    }
    // 合并操作
    let union = (x,y)=>{
        x = find(x)
        y = find(y)
        if(x === y )return;
        parent[x] = y
    }
    //  判断两点是否属于同一个集合 
    // 例如 [[1,2],[1,3],[2,3]] 
    // 1与2 链接了属于一个集合 1与3又链接了也是同一个集合 
    // 那说明,1,2,3都是一个集合,那最后[2,3]满足题目要求
    let same = (x,y)=>{
        x = find(x)
        y = find(y)
        return x == y
    }
    
    for(let i = 0; i<edges.length; i++){
        if(same(edges[i][0],edges[i][1])){
            return edges[i]
        }else{
            union(edges[i][0],edges[i][1])
        }
    }
    return
};

1319. 连通网络的操作次数

题目:用以太网线缆将 n 台计算机连接成一个网络,计算机的编号从 0 到 n-1。线缆用 connections 表示,其中 connections[i] = [a, b] 连接了计算机 a 和 b。 网络中的任何一台计算机都可以通过网络直接或者间接访问同一个网络中其他任意一台计算机。 给你这个计算机网络的初始布线 connections,你可以拔开任意两台直连计算机之间的线缆,并用它连接一对未直连的计算机。请你计算并返回使所有计算机都连通所需的最少操作次数。如果不可能,则返回 -1 。


这题的大致意思就是 提供n台计算机与一个connections二维数组,每个元素中是两台已经相连的同一个网络的主机, n台计算机中不一定都存在connections数组里,所以有可能有主机没有被连接上网络,所以需要去计算把所有计算机都连通网络的最少操作次数

var makeConnected = function(n, connections) {
    //  如果connections的长度小于主机的总数 -1,那说明网线不够没办法连接所有的计算机,
    if(connections.length < n-1){
        return -1
    }
    // 初始化所有主机的标识
    let parent = new Array(n).fill(0)
    for(let i = 0; i < n; i++){
        parent[i] = i
    }
    // 查的操作
    let find = (x)=>{
        return x === parent[x] ? x : parent[x] = find(parent[x])
    }
    // 合并集合操作
    let union = (x,y) => {
        x = find(x)
        y = find(y)
        parent[x] = y
    }
    //遍历connections 统一标识
    for(let [x,y] of connections){
        union(x,y)
    }
    let count = 0
    // 如果当前下标与合并过后的parent元素还是一致的话 说明该主机需要进行连接网络
    for(let i = 0; i<n; i++){
        if(parent[i] === i){
            count++
        }
    }
    // -1 是因为两台主机只需一个连接,3台主机 需要两个 以此类推
    return count -1
};