文章简要概述
基本概念
- 并查集是一种数据结构,并查集这三个字,一个字代表一个意思。
- 并(Union),代表合并
- 查(Find),代表查找
- 集(Set),代表这是一个以字典为基础的数据结构,它的基本功能是合并集合中的元素,查找集合中的元素 并查集的典型应用是有关连通分量的问题 并查集解决单个问题(添加,合并,查找)的时间复杂度都是O(1)O(1) 因此,并查集可以应用到在线算法中
内容简介
- 本文主要进行并查集相关的算法题刷题题解记录,记录并查集相关算法以及如何解。
- 这文一共有3道题,主要介绍leetcode中
547. 省份数量、200. 岛屿数量、和990. 等式方程的可满足性的解题思路。
与并查集相关算法
547. 省份数量
题目大意:
有 n 个城市,其中一些彼此相连,另一些没有相连。如果城市 a 与城市 b 直接相连,且城市 b 与城市 c 直接相连,那么城市 a 与城市 c 间接相连。
省份 是一组直接或间接相连的城市,组内不含其他没有相连的城市。
给你一个 n x n 的矩阵 isConnected ,其中 isConnected[i][j] = 1 表示第 i 个城市和第 j 个城市直接相连,而 isConnected[i][j] = 0 表示二者不直接相连。
返回矩阵中 省份 的数量。
示例
输入: 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. 岛屿数量
题目大意:
给你一个由 '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. 等式方程的可满足性
题目大意:
给定一个由表示变量之间关系的字符串方程组成的数组,每个字符串方程 equations[i] 的长度为 4,并采用两种不同的形式之一:"a==b" 或 "a!=b"。在这里,a 和 b 是小写字母(不一定不同),表示单字母变量名。
只有当可以将整数分配给变量名,以便满足所有给定的方程时才返回 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。往期文章:
树形结构-对应算法详解一 树形结构-对应算法详解二 链表相关算法复习一
链表相关算法复习二 链表相关算法复习三 数据结构与算法-栈一
数据结构与算法-栈二 数据结构与算法-队列一 数据结构与算法-队列二
数据结构与算法-链表一 数据结构与算法-链表二 数据结构与算法-链表三
有兴趣的可以一起来刷题,求点赞👍 关注!