持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第30天,点击查看活动详情
给你一个二维字符网格数组 grid ,大小为 m x n ,你需要检查 grid 中是否存在 相同值 形成的环。
一个环是一条开始和结束于同一个格子的长度 大于等于 4 的路径。对于一个给定的格子,你可以移动到它上、下、左、右四个方向相邻的格子之一,可以移动的前提是这两个格子有 相同的值 。
同时,你也不能回到上一次移动时所在的格子。比方说,环 (1, 1) -> (1, 2) -> (1, 1) 是不合法的,因为从 (1, 2) 移动到 (1, 1) 回到了上一次移动时的格子。
如果 grid 中有相同值形成的环,请你返回 true ,否则返回 false 。
示例 1:
输入:grid = [["a","a","a","a"],["a","b","b","a"],["a","b","b","a"],["a","a","a","a"]]
输出:true
示例 2:
输入:grid = [["c","c","c","a"],["c","d","c","c"],["c","c","e","c"],["f","c","c","c"]]
输出:true
示例 3:
输入:grid = [["a","b","b"],["b","z","b"],["b","b","a"]]
输出:false
并查集
使用并查集判断无向图中是否有环的方法非常简洁且直观:
- 对于图中的任意一条边 ,我们将 和 对应的集合合并。如果 和 已经属于同一集合,那么说明 和 已经连通,在边 的帮助下,图中会形成一个环。
这样一来,我们只要遍历图中的每一条边并进行上述的操作即可。具体的方法是,我们遍历数组 中的每一个位置,如果该位置与其上方或左侧的值相同,那么就有了一条边,并将这两个位置进行合并。这样的方法可以保证每一条边的两个节点只会被合并一次。
由于并查集是一维的数据结构,而数组 是二维的。因此对于数组中的每个位置 ,我们可以用 将其映射至一维空间中:
- 上方的位置对应着 ;
- 左侧的位置对应着 。
/**
* @param {character[][]} grid
* @return {boolean}
*/
var containsCycle = function(grid) {
let searched = new Array(grid.length).fill(0).map(i => new Array(grid[0].length).fill(false)); // 记录搜索过的元素,防止重复
function search(i, j, pre = null, path = new Set()) {
// 进行搜索,探寻到路径中重复的元素,说明成环,返回true.
if (path.has(i+'-'+j)) {
return true;
}
let pos = [[-1,0], [1,0], [0,1], [0,-1]];
let can = false;
path.add(i+'-'+j);
for (let [x,y] of pos) {
if (i+x >= 0 && i+x < grid.length && j+y >= 0 && j+y < grid[0].length && grid[i+x][j+y] === grid[i][j]) {
if (`${i+x}-${j+y}` === pre) continue; // 不能重复走上一次走过的格子
can |= search(i+x, j+y, i+'-'+j, path);
}
}
searched[i][j] = true;
path.delete(i+'-'+j);
return can;
}
for (let i=0; i<grid.length; i++) {
for (let j=0; j<grid[0].length; j++) {
if (!searched[i][j] && search(i,j)) return true; // 遍历,找到了立即返回结果
}
}
return false;
};