我有一个这样的整数矩阵:
1 1 1 0 3 3 3 0 2 2
1 1 1 0 3 3 3 0 2 2
0 0 0 0 3 3 3 0 0 0
2 2 0 0 3 3 3 0 4 4
0 0 0 0 0 0 0 0 0 0
1 1 1 1 1 0 4 4 4 0
我有相同值的不同区域,由“0”分隔,我需要计算矩阵中有多少区域。我的算法基于“0”,每次发现“0”时都会出现一个新区域,所以我增加计数器。问题是我逐行搜索并多次输入同一区域。我只需要计算矩形区域。
一种简单、快速的算法是遍历所有条目并在遇到每个区域时将其设置为零。这需要 O(N*M) 运行时间(每个条目最多访问两次)和 O(1) 额外内存。
要将区域设置为零,只需注意它开始的列,然后迭代到最右边的列。然后从左到右列遍历下面的行,将每个条目设置为零。
工作代码:
int count_regions( int *arr, int rows, int cols ) {
int region_count = 0;
for ( int first_index = 0; first_index != rows * cols; ++ first_index ) {
if ( arr[ first_index ] == 0 ) continue;
++ region_count;
int first_row = first_index / cols, first_col = first_index % cols;
int last_col;
for ( last_col = first_col;
last_col != cols && arr[ first_row * cols + last_col ] != 0;
++ last_col ) ;
for ( int last_row = first_row;
last_row != rows && arr[ last_row * cols + first_col ] != 0;
++ last_row ) {
for ( int col = first_col; col != last_col; ++ col ) {
arr[ last_row * cols + col ] = 0;
}
}
}
return region_count;
}
绘画技术很好。这个想法是你投下一个完全覆盖一个区域的油漆炸弹。伪代码:
for (each space) {
if (space has been painted or is a border) continue;
else {
numAreas++;
drop paint bomb
}
}
油漆炸弹的工作原理是这样的:
paint space;
for (each adjacent space) {
if (space has been painted or space is a border) continue;
else {
paint space;
drop another bomb;
}
}
一种方法是使用Flood Fill算法来检测连续区域,如下所示:
假设初始矩阵只有正数,
counter
,设置为-1
counter
当您到达矩阵的末尾时,counter
负数将等于连续编号区域的数量减一,即您需要的结果是-(counter+1)
。
我一直很喜欢用于隔离组的 Union/Find 算法。但那是因为我有一个多年前写的旧实现。实现起来没什么大不了的,但使用 Flood Fill 可能更合适。