Поиск блоков в массивах

Я просматривал некоторые вопросы интервью, и я наткнулся на это:

существует массив 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 shape и объект в последнем ряд.

у меня возникли проблемы с алгоритмом, который поймает форму " u " (как показано ниже). Как мне подойти к этому?

0 1 0 1
0 1 0 1
0 1 1 1
0 0 0 0
0 1 1 0

7 ответов


это работает в 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;
    }

один подход будет использовать Заливка. Алгоритм будет примерно таким:

for row in block_array:
    for block in row:
        if BLOCK IS A ONE and BLOCK NOT VISITED: 
            FLOOD_FILL starting from BLOCK

вы бы отметить элементы, посещенные во время процесса заливки потока, и отслеживать фигуры оттуда, а также.


Я бы использовал непересекающиеся наборы (связанные компоненты).

в начале каждый (i,j) матричный элемент со значением 1-это сам набор элементов.

затем вы можете перебирать каждый элемент матрицы и для каждого элемента (i,j) вы должны присоединиться к каждому смежному набору позиций {(i+1,j),(i-1,j),(i,j+1),(i,j-1)} к (i,j), если его значение равно 1.

вы можете найти реализацию непересекающихся множеств в непересекающиеся наборы в Python

В конце, количество различных множеств - это количество объектов.


мои два цента (Слэш) алгоритм:

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)

Почему бы просто не посмотреть на все соседние ячейки данного блока? Начните с какой-то ячейки, в которой есть 1, Следите за ячейками, которые вы посещали раньше, и продолжайте просматривать соседние ячейки, пока не сможете найти 1 больше. Затем перейдите на ячейки, которые вы еще не посмотрели, и повторите процесс.


что-то вроде этого должно работать:

  1. в то время как массив имеет 1, который не отмечен:
    1. создать новый объект
    2. создать очередь
    3. добавить 1 в очередь
    4. пока очередь не пуста:
      1. получить 1 в верхней части очереди
      2. пометить его
      3. добавить его в текущий объект
      4. ищите своих 4 соседей
      5. если какой-либо из них является 1 и еще не отмечен, добавьте его в очередь

Я бы использовал непересекающиеся-set datastructure (иначе известный как union-find).

кратко: для каждого подключенного компонента создайте "обратное дерево", используя одну ссылку на элемент в качестве" родительского " указателя. После родительских указателей в конечном итоге будет найден корень дерева, который используется для идентификации компонента (так как он одинаков для каждого элемента подключенного компонента). Чтобы объединить соседние компоненты, сделайте корень одного компонента родительским другой (который больше не будет корнем, так как теперь у него есть родитель).

две простые оптимизации делают эту структуру данных очень эффективной. Во-первых, сделайте все корневые запросы "свернуть" их пути, чтобы указать непосредственно на корень-таким образом, следующий запрос будет нужен только один шаг. Другой, всегда используйте "более глубокий" из двух деревьев в качестве нового корня; это требует поддержания "рангового" балла для каждого корня.

кроме того, чтобы сделать оценку соседей больше эффективно, вы можете рассмотреть возможность предварительной обработки входных данных по строкам. Таким образом, непрерывный сегмент 1 ' s на той же строке может начать жизнь как один подключенный компонент, и вы можете эффективно сканировать сегменты предыдущей строки на основе критерия соседа.