我正在使用一个二维数组,该数组在每个点处都包含值,我需要找到一种方法来计算和返回由与该值相邻的值组成的组数。例如:
+ - -
+ - -
+ + +
在3x3数组的ASCII版本中,将有两个值“ +”和“-”的组。我将基于邻接关系将此数组定义为具有两个不同的组。如果两个正方形不通过相邻路径连接,则它们将不在同一组中。
3组:
+ - -
- - -
+ - -
5组:
+ - +
- - -
+ - +
如何编写一种算法,可以找到组的数量,其值和位置?
感谢您的帮助!
答案 0 :(得分:4)
您要寻找的算法是Flood Fill。要查找数组中从[0,0]开始的组,然后填充以查找具有相同值的所有连接的单元格,并随即将它们标记为“完成”。洪水填充终止后,它访问的单元格列表就是您的第一组。然后扫描整个阵列,直到找到未标记为已完成的单元,然后从那里开始另一个洪水填充以找到下一个组。重复直到所有单元格都标记为完成。
答案 1 :(得分:2)
我们可以通过将其建模为graph来解决此问题,如下所示:
例如,对于此数组:
+ - -
+ - -
+ + +
我们图的adjacency list表示将是(这里,顶点被标记为0,0
之类的坐标,它对应于array[0][0]
处的像元):
0,0 -> 1,0
0,1 -> 0,2 1,1
0,2 -> 0,1 1,2
1,0 -> 0,0 2,0
1,1 -> 0,1 1,2
1,2 -> 1,1 0,2
2,0 -> 1,0 2,1
2,1 -> 2,0 2,2
2,2 -> 2,1
在外观上看起来像这样(水平边缘表示为...
):
+ -...-
| | |
+ -...-
|
+...+...+
请注意,我们的图形构造在较高层次上符合我们的要求(即,连接类似的“区域”)。现在我们需要计算这些区域。为此,我们可以使用depth first search。
我们的策略是在任何顶点启动DFS并完成它。这将完全覆盖初始顶点所属的区域。我们将继续对尚未被任何DFS触及的所有顶点执行此操作,并计算我们需要执行这些DFS的次数。
这通常称为connect components问题。用于查找特定情况下组件数量的算法将花费 nxn 数组花费 O(n ^ 2)时间,因为我们将拥有 n ^ 2 个顶点且小于 n ^ 2 个边(DFS的时间复杂度为 O(V + E))。
这是基于上述算法的javascript工作解决方案(示例的测试在底部):
function getGroups(array) {
const graph = buildGraph(array);
const components = getConnectedComponents(graph);
return components;
}
function getConnectedComponents(graph) {
let componentId = 0,
vertexComponent = {},
marked = new Set();
let dfs = function(source) {
marked.add(source);
vertexComponent[source] = componentId;
for (let u of graph[source]) {
if (!marked.has(u)) dfs(u);
}
}
for (let v in graph) {
if (!marked.has(v)) {
dfs(v); // start dfs from an unmarked vertex
componentId++; // dfs must have "touched" every vertex in that component, so change componentId for the next dfs
}
}
return vertexComponent;
}
function buildGraph(grid) {
// builds an adjacency list (set) graph representation from a 2d grid
let g = {};
grid.forEach((row, i) => {
row.forEach((cell, j) => {
let v = i + ',' + j; // vertex label ( e.g 0,1 )
if (!(v in g)) g[v] = new Set();
getNeighbors(grid, i, j).forEach(n => {
if (!(n in g)) g[n] = new Set();
g[v].add(n);
g[n].add(v);
});
});
});
return g;
}
function getNeighbors(grid, i, j) {
// returns neighboring cells of the same type as grid[i][j]
let n = [];
let color = grid[i][j];
if (i - 1 >= 0 && grid[i - 1][j] === color) n.push([i - 1, j].join());
if (j - 1 >= 0 && grid[i][j - 1] === color) n.push([i, j - 1].join());
if (i + 1 < grid.length && grid[i + 1][j] === color) n.push([i + 1, j].join());
if (j + 1 < grid[0].length && grid[i][j + 1] === color) n.push([i, j + 1].join());
return n;
}
// Let's do some testing
let array = [
[0, 1, 1],
[0, 1, 1],
[0, 0, 0]
];
let arrayGroups = getGroups(array);
console.log(arrayGroups);
console.log('Total groups: ', new Set(Object.values(arrayGroups)).size);
array = [
[0, 1, 1],
[1, 1, 1],
[0, 1, 1]
];
arrayGroups = getGroups(array);
console.log(arrayGroups);
console.log('Total groups: ', new Set(Object.values(arrayGroups)).size);
array = [
[0, 1, 0],
[1, 1, 1],
[0, 1, 0]
];
arrayGroups = getGroups(array);
console.log(arrayGroups);
console.log('Total groups: ', new Set(Object.values(arrayGroups)).size);