携手创作,共同成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第9天,点击查看活动详情
最近很少有并查集的题目,偶尔翻到之前使用DFS解决的一道题目,这道题目或许可以尝试使用并查集来解
被围绕的区域
给你一个
m x n
的矩阵board
,由若干字符'X'
和'O'
,找到所有被'X'
围绕的区域,并将这些区域里所有的'O'
用'X'
填充。
分析
DFS或BFS
- 如果使用DFS或者BFS来分析这道题目,思路是通过不断地搜索,搜索到O的时候,分情况,如果是在边界,把O改为S,如果不是边界,把O改为X,最后把S改为X,此时的矩阵就是答案
并查集
- 首先简单介绍一下并查集:并查集是一种很优雅的树型结构,一般用来描述不相交集合的合并和查询,合并指的是把不相交的集合合并成一个,查询是查询两个元素是否在一个集合中
- 首先要实现一个并查集,包括矩阵为参数的初始化、合并、查询
- 设置一个傀儡节点,遍历整个矩阵,遍历到字符O,当字符O在边界时,这个字符和傀儡节点相连,如果不在边界就和相邻的节点相连
- 再次遍历矩阵,如果O和傀儡节点相连,就不变,如果不是就改为X,就是的矩阵就是答案
- 优化:因为矩阵中的元素和其坐标有相关性,可以把二维化为一位,id用x_y来表示,数据结构更优美
代码
/**
* @param {character[][]} board
* @return {void} Do not return anything, modify board in-place instead.
*/
var solve = function(board) {
const DIRS = [[0, 1], [0, -1], [1, 0], [-1, 0]];
if (board === null || board.length === 0) return;
let rows = board.length;
let cols = board[0].length;
const UnionFind = function(board){
let parent = {};
for (let x=0; x<rows; x++){
for (let y=0; y<cols; y++){
if (board[x][y] === 'O') {
let id = `${x}_${y}`;
parent[id] = id;
}
}
}
parent['dummy'] = 'dummy';
this.find = (x) => {
if (x === parent[x]) return x;
parent[x] = this.find(parent[x]);
return parent[x];
}
this.union = (x, y) => {
let rootX = this.find(x);
let rootY = this.find(y);
if (rootX !== rootY){
parent[rootX] = rootY;
}
}
}
let uf = new UnionFind(board);
let dummyBorder = 'dummy';
for (let x=0; x<rows; x++){
for (let y=0; y<cols; y++){
if (board[x][y] === 'O'){
let id = `${x}_${y}`;
if (x === 0 || x === rows-1 || y === 0 || y === cols-1){
uf.union(dummyBorder, id);
}
for (let dir of DIRS){
let nx = x + dir[0];
let ny = y + dir[1];
if (nx >= 0 && ny >=0 && nx < rows && ny < cols && board[nx][ny] === 'O'){
let nId = `${nx}_${ny}`;
uf.union(id, nId);
}
}
}
}
}
for (let x = 0; x < rows; x++) {
for (let y = 0; y < cols; y++) {
if (board[x][y] === 'O' && uf.find(`${x}_${y}`) !== uf.find(dummyBorder)) {
board[x][y] = 'X';
}
}
}
};
总结
- 并查集作为一种特殊的数据结构在算法的宇宙中不算突出,但是却能给我们解决问题带来新的灵感,当题目中如果有连同、查找同时存在的操作时,不妨试试并查集
- 今天也是有收获的一天