这可以通过union find解决(尽管如另一个答案所示,DFS 可能更简单一些)。
这种数据结构背后的基本思想是重复合并同一组件中的元素。这是通过将每个组件表示为一棵树来完成的(节点跟踪它们自己的父节点,而不是相反),您可以通过遍历到根节点来检查 2 个元素是否在同一个组件中,您可以合并节点通过简单地使一个根成为另一个根的父级。
一个简短的代码示例演示了这一点:
const int w = 5, h = 5;
int input[w][h] = {{1,0,0,0,1},
{1,1,0,1,1},
{0,1,0,0,1},
{1,1,1,1,0},
{0,0,0,1,0}};
int component[w*h];
void doUnion(int a, int b)
{
// get the root component of a and b, and set the one's parent to the other
while (component[a] != a)
a = component[a];
while (component[b] != b)
b = component[b];
component[b] = a;
}
void unionCoords(int x, int y, int x2, int y2)
{
if (y2 < h && x2 < w && input[x][y] && input[x2][y2])
doUnion(x*h + y, x2*h + y2);
}
int main()
{
for (int i = 0; i < w*h; i++)
component[i] = i;
for (int x = 0; x < w; x++)
for (int y = 0; y < h; y++)
{
unionCoords(x, y, x+1, y);
unionCoords(x, y, x, y+1);
}
// print the array
for (int x = 0; x < w; x++)
{
for (int y = 0; y < h; y++)
{
if (input[x][y] == 0)
{
cout << ' ';
continue;
}
int c = x*h + y;
while (component[c] != c) c = component[c];
cout << (char)('a'+c);
}
cout << "\n";
}
}
现场演示。
上面将使用字母表中的不同字母显示每组。
p i
pp ii
p i
pppp
p
应该很容易修改它以单独获取组件或获取与每个组件对应的元素列表。一个想法是将cout << (char)('a'+c);
上面的componentMap[c].add(Point(x,y))
替换componentMap
为 a map<int, list<Point>>
- 然后此地图中的每个条目将对应于一个组件并给出一个点列表。
有各种优化可以提高 union find 的效率,以上只是一个基本的实现。