我正在查看一些面试问题,我偶然发现了这个问题:
有一个m x n数组。数组中的块用1表示,0表示无块。您应该找到数组中的对象数。对象只是一组水平和/或垂直连接的块。
例如
0 1 0 0
0 1 0 0
0 1 1 0
0 0 0 0
0 1 1 0
答案:此阵列中有2个对象。 L形对象和最后一行中的对象。
我无法想出能够捕获“你好”的算法。 (如下)形状。我该怎么做呢?
0 1 0 1
0 1 0 1
0 1 1 1
0 0 0 0
0 1 1 0
答案 0 :(得分:3)
一种方法是使用Flood Fill。算法将是这样的:
for row in block_array:
for block in row:
if BLOCK IS A ONE and BLOCK NOT VISITED:
FLOOD_FILL starting from BLOCK
您可以标记在洪水填充过程中访问过的物品,并从那里跟踪形状。
答案 1 :(得分:2)
这适用于C#
static void Main()
{
int[][] array = { new int[] { 0, 1, 0, 1 }, new int[] { 0, 1, 0, 1 }, new int[] { 0, 1, 1, 1 }, new int[] { 0, 0, 0, 0 }, new int[] { 0, 1, 1, 0 } };
Console.WriteLine(GetNumber(array));
Console.ReadKey();
}
static int GetNumber(int[][] array)
{
int objects = 0;
for (int i = 0; i < array.Length; i++)
for (int j = 0; j < array[i].Length; j++)
if (ClearObjects(array, i, j))
objects++;
return objects;
}
static bool ClearObjects(int[][] array, int x, int y)
{
if (x < 0 || y < 0 || x >= array.Length || y >= array[x].Length) return false;
if (array[x][y] == 1)
{
array[x][y] = 0;
ClearObjects(array, x - 1, y);
ClearObjects(array, x + 1, y);
ClearObjects(array, x, y - 1);
ClearObjects(array, x, y + 1);
return true;
}
return false;
}
答案 2 :(得分:1)
我会使用Disjoint sets(连接组件)。
在开始时,值为1的每个(i,j)矩阵元素本身就是一个元素集。
然后你可以迭代每个矩阵元素,对于每个元素(i,j),你应该连接每个相邻的位置集{(i + 1,j),(i-1,j),(i,j + 1) ),(i,j-1)}到(i,j)设置它的值是否为1。
找到不相交集的实现最后,不同组的数量是对象的数量。
答案 3 :(得分:1)
我的两分钱(斜线)算法:
1. List only the 1's.
2. Group (collect connected ones).
在Haskell:
import Data.List (elemIndices, delete)
example1 =
[[0,1,0,0]
,[0,1,0,0]
,[0,1,1,0]
,[0,0,0,0]
,[0,1,1,0]]
example2 =
[[0,1,0,1]
,[0,1,0,1]
,[0,1,1,1]
,[0,0,0,0]
,[0,1,1,0]]
objects a ws = solve (mapIndexes a) [] where
mapIndexes s =
concatMap (\(y,xs)-> map (\x->(y,x)) xs) $ zip [0..] (map (elemIndices s) ws)
areConnected (y,x) (y',x') =
(y == y' && abs (x-x') == 1) || (x == x' && abs (y-y') == 1)
solve [] r = r
solve (x:xs) r =
let r' = solve' xs [x]
in solve (foldr delete xs r') (r':r)
solve' vs r =
let ys = filter (\y -> any (areConnected y) r) vs
in if null ys then r else solve' (foldr delete vs ys) (ys ++ r)
输出:
*Main> objects 1 example1
[[(4,2),(4,1)],[(2,2),(2,1),(1,1),(0,1)]]
(0.01 secs, 1085360 bytes)
*Main> objects 1 example2
[[(4,2),(4,1)],[(0,3),(1,3),(2,3),(2,2),(2,1),(1,1),(0,1)]]
(0.01 secs, 1613356 bytes)
答案 4 :(得分:0)
为什么不查看给定块的所有相邻单元格?从一个包含1的单元格开始,跟踪之前访问过的单元格,并继续查看相邻的单元格,直到找不到1为止。然后移动到尚未查看的单元格并重复此过程。
答案 5 :(得分:0)
这样的事情应该有效:
答案 6 :(得分:0)
我会使用disjoint-set datastructure(也称为union-find)。
简要说明:对于每个连接的组件,使用每个元素的单个链接构建“反向树”作为“父”指针。在父指针之后最终将找到树的根,该树用于组件标识(因为它对于连接组件的每个成员都是相同的)。要合并相邻组件,请将一个组件的根目录作为另一个组件的父组件(它将不再是根目录,因为它现在具有父组件)。
两个简单的优化使这个数据结构非常有效。一个是,让所有根查询“折叠”它们的路径直接指向根 - 这样,下一个查询只需要一步。另一种是,总是使用两棵树的“更深”作为新根;这需要维持每个根的“等级”分数。
此外,为了使评估邻居更有效,您可以考虑逐行预处理输入。这样,同一行上的1
的连续段可以作为单个连接组件开始生命,并且您可以根据邻居标准有效地扫描前一行的段。