# 并查集
# 思想
属于一种特殊的数据结构,在解决连通域问题上有着不错的性能。
# 三个组件
维护一个数组
let parents = []
,存放当前节点的父节点,根节点的父节点是其本身查询一个节点的根节点是哪个节点
let parents = [] function find(root){ let temp, son = root; while(root !== parents[root]){ root = parents[root]; } while(son !== root){ //路径压缩,其实就是个扁平化处理的过程 temp = parents[son]; parents[son] = root; son = temp; } return root; } //递归版 function find(root){ if(root !== parents[root]) parents[root] = find(parents[root]); return parents[root]; }
合并两个连通域
function join(x,y){ x = find(x); y = find(y); if(x !== y){ parents[x] = y; } }
# 题目
# 岛屿数量 (opens new window)
# 思路
- 写三大组件,初始化 parents 时将其键值对应
- 判定当前节点四周是否存在陆地,如果有就将它们连接起来,如果没有就将当前节点的父节点置反
- 求出 parents 数组中键和值依然相等的数目(即为连通域的数目)
# 答案
function numIslands(grid: string[][]): number {
const row = grid.length;
if (row === 0) return 0;
const col = grid[0].length;
const parents: number[] = [];
for (let i = 0; i < row; i++) {
for (let j = 0; j < col; j++) {
parents[i * col + j] = i * col + j;
}
}
function find(root: number): number {
if (root !== parents[root]) parents[root] = find(parents[root]);
return parents[root];
}
function union(x: number, y: number): void {
x = find(x);
y = find(y);
if (x !== y) {
parents[x] = y;
}
}
for (let i = 0; i < row; i++) {
for (let j = 0; j < col; j++) {
if (grid[i][j] === "1") {
i < row - 1 &&
grid[i + 1][j] === "1" &&
union((i + 1) * col + j, i * col + j);
j < col - 1 &&
grid[i][j + 1] === "1" &&
union(i * col + j + 1, i * col + j);
} else {
parents[i * col + j] = -parents[i * col + j];
}
}
}
return parents.filter((value, key) => key === value && Object.is(key, value))
.length;
}
DFS 解法
function numIslands(grid: string[][]): number {
let res = 0
const rows = grid.length
if(!rows) return 0
const cols = grid[0].length
function helper(i: number, j: number): void {
if (i < 0 || j < 0 || i > rows - 1 || j > cols - 1 || grid[i][j] === '0') return
grid[i][j] = '0'
helper(i + 1, j)
helper(i, j + 1)
helper(i - 1, j)
helper(i, j - 1)
}
for(let i = 0; i < rows; i++) {
for(let j = 0; j < cols; j++) {
if(grid[i][j] === '1') {
helper(i, j)
res++
}
}
}
return res
}
# 被围绕的区域 (opens new window)
# 思路
- 写三大组件
- 将 0 节点划分为内部节点和边界节点,引入一个虚拟边界 root 节点
- 判定 0 节点是否为内部节点,如果是则替换为 x
# 答案
function solve(board: string[][]): void {
const row = board.length;
if (row === 0) return;
const col = board[0].length;
const parents: number[] = [];
for (let i = 0; i < row; i++) {
for (let j = 0; j < col; j++) {
parents[i * col + j] = i * col + j;
}
}
function find(root: number): number {
if (root !== parents[root]) parents[root] = find(parents[root]);
return parents[root];
}
function union(x: number, y: number): void {
x = find(x);
y = find(y);
if (x !== y) {
parents[x] = y;
}
}
function isConnected(x: number, y: number): boolean {
return find(x) === find(y);
}
let virtualArea = row * col + 1;
for (let i = 0; i < row; i++) {
for (let j = 0; j < col; j++) {
if (board[i][j] === "O") {
if (i === 0 || i === row - 1 || j === 0 || j === col - 1) {
union(i * col + j, virtualArea);
} else {
i > 0 &&
board[i - 1][j] === "O" &&
union(i * col + j, (i - 1) * col + j);
i < row - 1 &&
board[i + 1][j] === "O" &&
union(i * col + j, (i + 1) * col + j);
j > 0 &&
board[i][j - 1] === "O" &&
union(i * col + j, i * col + j - 1);
j < col - 1 &&
board[i][j + 1] === "O" &&
union(i * col + j, i * col + j + 1);
}
}
}
}
for (let i = 0; i < row; i++) {
for (let j = 0; j < col; j++) {
if (board[i][j] === "O" && !isConnected(i * col + j, virtualArea)) {
board[i][j] = "X";
}
}
}
}