这可以通过并查集来解决(虽然正如其他答案所示,DFS可能会更简单一些)。
这个数据结构背后的基本思想是不断地合并同一组中的元素。通过将每个组表示为一棵树(其中节点跟踪其自己的父节点,而不是反过来),您可以通过遍历到根节点来检查两个元素是否在同一组中,并且您可以通过将一个根节点作为另一个根节点的父节点来合并节点。
下面是一个简短的代码示例:
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)
{
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);
}
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";
}
}
演示页面可显示使用不同字母的每组“1”。
p i
pp ii
p i
pppp
p
可以很容易地修改此代码以分别获取组件或获取与每个组件对应的元素列表。一种想法是将上面的 cout << (char)('a'+c);
替换为 componentMap[c].add(Point(x,y))
,其中 componentMap
是一个 map<int, list<Point>>
,该映射中的每个条目都对应一个组件并给出一个点列表。
有各种优化方法可提高并查集的效率,上述仅是基本实现。