如何使用并查集解算法题

162 阅读5分钟

文章简要概述

基本概念

  • 并查集是一种数据结构,并查集这三个字,一个字代表一个意思。
  • 并(Union),代表合并
  • 查(Find),代表查找
  • 集(Set),代表这是一个以字典为基础的数据结构,它的基本功能是合并集合中的元素,查找集合中的元素 并查集的典型应用是有关连通分量的问题 并查集解决单个问题(添加,合并,查找)的时间复杂度都是O(1)O(1) 因此,并查集可以应用到在线算法中

内容简介

  • 本文主要进行并查集相关的算法题刷题题解记录,记录并查集相关算法以及如何解。
  • 这文一共有3道题,主要介绍leetcode中547. 省份数量200. 岛屿数量、和990. 等式方程的可满足性的解题思路。

与并查集相关算法

547. 省份数量

547. 省份数量--leetcode

题目大意:

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

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

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

返回矩阵中 省份 的数量。

示例

graph1.jpg

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

输出: 2

解题思路:

  • 初始时,每个城市都属于不同的连通分量
  • 遍历矩阵isConnected,如果两个城市之间有相连关系,则它们属于同一个连通分量,对它们进行合并
  • 计算连通分量的总数,即为省份的总数。

代码:

/**
 * @param {number[][]} isConnected
 * @return {number}
 */
var findCircleNum = function(isConnected) {
   const len = isConnected.length;
   const parent = new Array(len).fill(0).map((el,index) => index);
   for(let i = 0; i < len; i++) {
       for(let j = 1; j < len; j++) {
           if (isConnected[i][j] === 1) {
             union(parent, i, j);
           }  
       }
   }
   return parent.filter((el, index) => el === index).length
   function union(parent, i, j) {
       parent[find(parent, i)] = find(parent, j)
   }
   function find(parent, index) {
       if (parent[index] !== index) {
           parent[index] = find(parent, parent[index])
       }
       return parent[index];
   }
};

200. 岛屿数量

200. 岛屿数量--leetcode

题目大意:

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

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

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

示例:

输入:

grid = [  ["1","1","1","1","0"],
  ["1","1","0","1","0"],
  ["1","1","0","0","0"],
  ["0","0","0","0","0"]
]

输出:1

解题思路:

  • 实现一个并查集的方法
  • 将数据拍平到一个数组中,遍历数据,判断当前节点的上一个节点,和左边的节点是否可以联通
  • 通过并查集,统计符合条件的数量

代码:

// 构建一个并查集
class QuickUnion {
    constructor(len) {
        this.arr = new Array(len).fill(0).map((el, index) => index)
    }
    find(index) {
        return this.arr[index] = this.arr[index] === index ? index : this.find(this.arr[index])
    }
    union (i, j) {
        this.arr[this.find(i)] = this.find(j)
    }
}
/**
 * @param {character[][]} grid
 * @return {number}
 */
var numIslands = function(grid) {
    const m = grid[0].length;
    const arr = grid.flat();
    const len = arr.length;
    const u = new QuickUnion(len);
    for(let i = 0; i < len; i++) {
        if (arr[i] === '0') continue;
        // 判断连通上边
        if (i >= m && arr[i-m] === '1') u.union(i, i-m);
        // 判断连通左边
        if (i % m && arr[i-1] === '1') u.union(i, i-1);
    }
    let res = 0;
    for(let i = 0; i < len; i++) {
        if (arr[i] === '1' && u.find(i) === i) res++;
    }
    return res
};

990. 等式方程的可满足性

990. 等式方程的可满足性

题目大意:

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

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

示例:

输入:["a==b","b!=a"]

输出:false

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

解题思路:

  • 通过构建的并查集方法,实例一个并查集u
  • 遍历传入的数据,判断第二位是否是 =, 如果是就将等式的两边的字符联通
  • 判断第二位是否是 ,如果是则判断两边的字符对应的标记是否相同,如果相同则不成立,返回false
  • 最后遍历完了,则返回true

代码:

class QuickUnion {
    constructor(len) {
        this.arr = new Array(len).fill(0).map((el, index) => index)
    }
    find(index) {
        return this.arr[index] = this.arr[index] === index ? index : this.find(this.arr[index])
    }
    union (i, j) {
        this.arr[this.find(i)] = this.find(j)
    }
}
/**
 * @param {string[]} equations
 * @return {boolean}
 */
var equationsPossible = function(equations) {
   const u = new QuickUnion(26);
   for (let i = 0; i < equations.length; i++) {
        if (equations[i][1] == '=') {
            u.union(equations[i].charCodeAt(0) - 97, equations[i].charCodeAt(3) - 97)
        }
    }
   for (let i = 0; i < equations.length; i++) {
        if (equations[i][1] == '!') {
            if (u.find(equations[i].charCodeAt(0) - 97) == u.find(equations[i].charCodeAt(3) - 97)) {
                return false
            }
        }
    }
    return true
};

结束语

数据结构与算法相关的练习题会持续输出,一起来学习,持续关注。当前是并查集部分。后期还会有其他类型的数据结构,题目来源于leetcode。

往期文章:

树形结构-对应算法详解一                             树形结构-对应算法详解二                             链表相关算法复习一

链表相关算法复习二                                      链表相关算法复习三                                      数据结构与算法-栈一

数据结构与算法-栈二                                    数据结构与算法-队列一                                数据结构与算法-队列二

数据结构与算法-链表一                                 数据结构与算法-链表二                                 数据结构与算法-链表三

有兴趣的可以一起来刷题,求点赞👍 关注!