Существует 2d массив элементов (в моем случае они называются Intersections
).
Определенный предмет дается в качестве начала. Задача состоит в том, чтобы найти все предметы, прямо или косвенно связанные с этим предметом, которые удовлетворяют определенной функции.
Итак, основной алгоритм такой:
Добавить начало в список результатов.
Повторяйте до тех пор, пока не будет внесено никаких изменений: добавьте каждый элемент в массиве, который удовлетворяет функции, и коснитесь любого элемента в списке результатов в списке результатов.
Моя текущая реализация выглядит так:
private IList<Intersection> SelectGroup (
Intersection start,
Func<Intersection, Intersection, bool> select)
{
List<Intersection> result = new List<Intersection> ();
Queue<Intersection> source = new Queue<Intersection> ();
source.Enqueue (start);
while (source.Any ()) {
var s = source.Dequeue ();
result.Add (s);
foreach (var neighbour in Neighbours (s)) {
if (select (start, neighbour)
&& !result.Contains (neighbour)
&& !source.Contains (neighbour)) {
source.Enqueue (neighbour);
}
}
}
Debug.Assert (result.Distinct ().Count () == result.Count ());
Debug.Assert (result.All (x => select (x, result.First ())));
return result;
}
private List<Intersection> Neighbours (IIntersection intersection)
{
int x = intersection.X;
int y = intersection.Y;
List<Intersection> list = new List<Intersection> ();
if (x > 1) {
list.Add (GetIntersection (x - 1, y));
}
if (y > 1) {
list.Add (GetIntersection (x, y - 1));
}
if (x < Size) {
list.Add (GetIntersection (x + 1, y));
}
if (y < Size) {
list.Add (GetIntersection (x, y + 1));
}
return list;
}
(Функция select
принимает начальный элемент и возвращает истину, если второй элемент удовлетворяет.)
Это делает свою работу и оказалось достаточно быстрым для обычных размеров массива (около 20 * 20). Тем не менее, я заинтересован в дальнейших улучшениях. Есть идеи?
Пример (X
удовлетворяет по отношению к другим X
s, .
никогда не удовлетворяет):
....
XX..
.XX.
X...
В этом случае есть 2 группы: центральная группа из 4 предметов и группа из одного предмета в левом нижнем углу. Выбор группы (например, с помощью начального элемента [3, 3]) возвращает первый, в то время как последний может быть выбран с использованием исходного элемента и единственного возвращаемого значения [1, 4].
Пример 2:
.A..
..BB
A.AA
На этот раз 4 группы. Группы 3 A не связаны, поэтому они возвращаются как отдельные группы. Большие группы A и B связаны, но A не относится к B, поэтому они возвращаются как отдельные группы.