【路飞】547. 省份数量、200. 岛屿数量、990. 等式方程的可满足性、684. 冗余连接

129 阅读5分钟

什么是并查集:并查集(Union-find Sets)是一种树型的数据结构,用于处理一些不相交集合的合并及查询问题。

并查集的思想是用一个数组表示了整片森林(parent),树的根节点唯一标识了一个集合,我们只要找到了某个元素的的树根,就能确定它在哪个集合里。

并查集代码封装如下

//定义个保存父节点索引的数组,
let fa = [];
//给所有节点赋默认值
let unionSet = function(n){
    for(let i = 0; i < n; i ++){
        fa[i] = i;
    }
}
//查询当前根元素,当根元素等于自己时就是根节点l
let get = function(x){
    return fa[x] = (fa[x] == x ? x : get(fa[x]));
}
//将a根节点挂在b根节点下,这样a和b就有共同根节点
let merge = function(a,b){
    fa[get(a)] = get(b);
}

547. 省份数量

有 n 个城市,其中一些彼此相连,另一些没有相连。如果城市 a 与城市 b 直接相连,且城市 b 与城市 c 直接相连,那么城市 a 与城市 c 间接相连。

省份 是一组直接或间接相连的城市,组内不含其他没有相连的城市。

给你一个 n x n 的矩阵 isConnected ,其中 isConnected[i][j] = 1 表示第 i 个城市和第 j 个城市直接相连,而 isConnected[i][j] = 0 表示二者不直接相连。

返回矩阵中 省份 的数量。

示例 1:

输入: isConnected = [[1,1,0],[1,1,0],[0,0,1]]
输出: 2

示例 2:

输入: isConnected = [[1,0,0],[0,1,0],[0,0,1]]
输出: 3

解题思路:本体可以利用并查集将所有相连的城市放到一个根节点下,然后循环isConnected有几个根节点,有几个就代表有几个省份:代码如下:

var findCircleNum = function(isConnected) {
  //因为是全局变量需要重置
   fa = [];
   //重置当前树父节点索引
    unionSet(isConnected.length);
    for(let i = 0; i < isConnected.length; i ++){
        for(let j = 0; j < isConnected[i].length; j ++){
            //如果isConnected[i][j] == 1就带边 i 和 j相连
            if(isConnected[i][j]) merge(i,j);
        }
    }
    ans = 0;
    //循环父节点,判断几个有子节点,有的话就是一颗单独的数,也就是一个单独的省份
    for(let i = 0; i < isConnected.length; i ++){
        //如果get(i) == i 就代表i下面有子字节,i就是根节点,所以加一
        if(i == get(i)) ans ++;
    }
    return ans
};
//以下代码上面已经介绍过
let fa = [];
let unionSet = function(n){
    for(let i = 0; i < n; i ++){
        fa[i] = i;
    }
    console.log(fa)
}
let get = function(x){
    return fa[x] = (fa[x] == x ? x : get(fa[x]));
}

let merge = function(a,b){
    console.log(get(a),get(b))
    fa[get(a)] = get(b);
}

200. 岛屿数量

给你一个由 '1'(陆地)和 '0'(水)组成的的二维网格,请你计算网格中岛屿的数量。

岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。

此外,你可以假设该网格的四条边均被水包围。

 

示例 1:

输入:grid = [
  ["1","1","1","1","0"],
  ["1","1","0","1","0"],
  ["1","1","0","0","0"],
  ["0","0","0","0","0"]
]
输出:1

示例 2:

输入:grid = [
  ["1","1","0","0","0"],
  ["1","1","0","0","0"],
  ["0","0","1","0","0"],
  ["0","0","0","1","1"]
]
输出:3

解题思路:本题可以将二维数组转换成一维数组,一维数组组成是每个二维数组的索引,长度为,二维数组的长乘以宽,这题也可以并查集来解决,这题本意是想看将1连接起来,有几个独立的连线模块,如果当前节点是1,那么如果上面节点,和左边节点节点也是1的话就可以分别将这两个索引合并,为什么不判断右,因为当前节点结束,就会操作右节点,所有没必要,下面也是一样,代码如下:

var numIslands = function(grid) {
    //重置全局变量
    fa = [];
    //获得数组长度
    let n = grid.length;
    //获得每行个数
    let m = grid[0].length;
    //给二维数组每个索引赋默认值
    unionSet(n*m);
    for(let i = 0; i < n; i ++){
        for(let j = 0; j < m; j ++){
            //当前节点为0不需要合并
            if(grid[i][j] == '0') continue;
            //判断当前节点是否存在上一个节点,如果存在并且等于1,就将当前节点索引个上面一个索引合并,find()是用来查找索引的
            if(i > 0 && grid[i - 1][j] == '1') merge(find(i -1,j,m),find(i,j,m));
            //判断左边是否可以合并
            if(j > 0 && grid[i][j - 1] == '1') merge(find(i,j - 1,m),find(i,j,m));
        }
    }
    let cnt = 0;
    //循环遍历以索引组织的以为数组有几个根节点存在,存在的和就是本题所求
    for(let i = 0; i < n; i ++){
        for(let j = 0; j < m; j ++){
            if(grid[i][j] == '0') continue ;
            if(find(i,j,m) == get(find(i,j,m))) cnt ++;
        }
    }
    return cnt;
};
// 计算索引
let find = function(i,j,m){
    return i * m + j;
}

//定义个保存父节点索引的数组,
let fa = [];
//给所有节点赋默认值
let unionSet = function(n){
    for(let i = 0; i < n; i ++){
        fa[i] = i;
    }
}
//查询当前根元素,当根元素等于自己时就是根节点l
let get = function(x){
    return fa[x] = (fa[x] == x ? x : get(fa[x]));
}
//将a根节点挂在b根节点下,这样a和b就有共同根节点
let merge = function(a,b){
    fa[get(a)] = get(b);
}
}

990. 等式方程的可满足性

给定一个由表示变量之间关系的字符串方程组成的数组,每个字符串方程 equations[i] 的长度为 4,并采用两种不同的形式之一:"a==b" 或 "a!=b"。在这里,a 和 b 是小写字母(不一定不同),表示单字母变量名。

只有当可以将整数分配给变量名,以便满足所有给定的方程时才返回 true,否则返回 false。 

示例 1:

输入:["a==b","b!=a"]
输出:false
解释:如果我们指定,a = 1 且 b = 1,那么可以满足第一个方程,但无法满足第二个方程。没有办法分配变量同时满足这两个方程。

示例 2:

输入: ["b==a","a==b"]
输出: true
解释: 我们可以指定 a = 1 且 b = 1 以满足满足这两个方程。

示例 3:

输入: ["a==b","b==c","a==c"]
输出: true

示例 4:

输入: ["a==b","b!=c","c==a"]
输出: false

示例 5:

输入: ["c==c","b==d","x!=z"]
输出: true

解题思路:这题我们可以先将相等的两个字母合并,然后再判断不相等的两个数组是否都存在相等的数组中,如果存在那就返回false,不相等就是成立的,具体实现需要将字母转换成从0开始的数字,因为我们封装的并查集默认的父节点是从0开始的数组,题目中说都是小写字母,所有默认的保存父节点数组的长度小于26代码如下:

var equationsPossible = function(equations) {
    fa = [];
    let n = equations;
    //默认父节点数组索引
    unionSet(26);
    //将字母a转换成数字,a - a = 0; b - a = 1; c- 1 = 2;...
    let aIndex = 'a'.charCodeAt(0);
    for(let i = 0; i < n.length; i ++){
        //如果是不等号不需要合并
        if(n[i][1] == '!') continue;
        //将相等的两个字母转换成数字,这里就表示两个是相等的,所有合并成一个数组
        let a = n[i][0].charCodeAt(0) - aIndex;
        let b = n[i][3].charCodeAt(0) - aIndex;
        merge(a,b);
    }
    for(let i = 0; i < n.length; i ++){
        if(n[i][1] == '=') continue;
        let a = n[i][0].charCodeAt(0) - aIndex;
        let b = n[i][3].charCodeAt(0) - aIndex;
        //如果a b 同时存在相等的数组里,就不符合
        if(get(a) == get(b)) return false;
    }
    return true;
};

//定义个保存父节点索引的数组,
let fa = [];
//给所有节点赋默认值
let unionSet = function(n){
    for(let i = 0; i < n; i ++){
        fa[i] = i;
    }
}
//查询当前根元素,当根元素等于自己时就是根节点l
let get = function(x){
    return fa[x] = (fa[x] == x ? x : get(fa[x]));
}
//将a根节点挂在b根节点下,这样a和b就有共同根节点
let merge = function(a,b){
    fa[get(a)] = get(b);
}

684. 冗余连接

树可以看成是一个连通且 无环 的 无向 图。

给定往一棵 n 个节点 (节点值 1~n) 的树中添加一条边后的图。添加的边的两个顶点包含在 1 到 n 中间,且这条附加的边不属于树中已存在的边。图的信息记录于长度为 n 的二维数组 edges ,edges[i] = [ai, bi] 表示图中在 ai 和 bi 之间存在一条边。

请找出一条可以删去的边,删除后可使得剩余部分是一个有着 n 个节点的树。如果有多个答案,则返回数组 edges 中最后出现的边。

示例 1:

输入: edges = [[1,2], [1,3], [2,3]]
输出: [2,3]

示例 2:

输入: edges = [[1,2], [2,3], [3,4], [1,4], [1,5]]
输出: [1,4]

解题思路:这题的解题思路是,先判断当前的两个值是否存在同一个父节点,如果存在就是多余的操作,代码如下:

var findRedundantConnection = function(edges) {
    fa = [];
    unionSet(edges.length);
    for(let i = 0; i < edges.length; i ++){
        let a = edges[i][0];
        let b = edges[i][1];
        //判断当前两个值是否存在同一个根节点,如果存在就是多余才做,不存在就合并,这样大家就会在一个数组里,
        if(get(a) == get(b)) return edges[i];
        merge(a,b);
    }
    return [];
};

//定义个保存父节点索引的数组,
let fa = [];
//给所有节点赋默认值
let unionSet = function(n){
    for(let i = 0; i < n; i ++){
        fa[i] = i;
    }
}
//查询当前根元素,当根元素等于自己时就是根节点l
let get = function(x){
    return fa[x] = (fa[x] == x ? x : get(fa[x]));
}
//将a根节点挂在b根节点下,这样a和b就有共同根节点
let merge = function(a,b){
    fa[get(a)] = get(b);
}