我试图从2D矩阵的左上角开始找出一个相同颜色区域的块。例如:我有以下矩阵:
1 1 1 2 2 3
1 1 2 3 4 5
1 1 1 1 3 4
1 4 3 2 1 5
2 3 4 5 1 2
说,最初的左上角是1,我想找出包含1的相邻区域(我只考虑从左上角开始)。在上面的矩阵中,数字1,2,3,4,5代表不同的颜色。我尝试使用以下代码段来查找相同的颜色块:
colors = ["red", "green", "blue", "purple", "orange"]
# create an array of the colors so we can do work on it
colors_array = [[random.randint(0, len(colors)-1) for x in range(num_wide)] for x in range(num_high)]
// keep track of what colors are touching in a giant array
touching_array = [[[0 for x in range(num_col)] for x in range(num_row)] for x in range(len(colors))]
origin_color = colors_array[0][0]
for x in range(num_row):
for y in range(num_col):
# first row only cares about what's to the left of it
if (x == 0):
# if this is the same color as the origin
if colors_array[x][y] == origin_color:
# store a '1' signifying that this color is touching
touching_array[origin_color][x][y] = 1
else:
# once they don't match, stop looking
break
# other rows need to match the row above it
else:
# if this color is the same color as the origin
if (colors_array[x][y] == origin_color):
# AND the one above it is "touching"
if (colors_array[x][y] == touching_array[origin_color][x-1][y]):
# store a '1' signifying that this color is touching
touching_array[origin_color][x][y] = 1
但是我从左上角开始没有输出相同的彩色块。上面的代码段有什么问题吗?我怎样才能让它正确?如果有人提供上述问题的C / C ++解决方案会更好。
答案 0 :(得分:2)
你忘记了瓷砖可以在所有4个方向沿着两个轴接触。
考虑以下输入:
1 1 1 1 1 1
2 2 2 3 3 1
1 1 1 1 3 1
1 3 3 3 3 1
1 1 1 1 1 1
我在两个版本中编写了一个新算法 - 一个使用递归,另一个使用循环和队列。它类似于他在答案中描述的J.Mac。
算法很简单。我们有一个目标颜色来搜索,位置搜索,输入颜色矩阵,输出矩阵标记以识别匹配的图块。
搜索图块:
我们可以使用递归来搜索相邻的图块,或者我们可以使用队列来跟踪仍然需要搜索的图块,并重复搜索队列前面的图块,直到没有图块为止。
#include <cstdint>
#include <vector>
#include <queue>
#include <string>
#include <iostream>
typedef std::vector<int32_t> vec_1d;
typedef std::vector<vec_1d> vec_2d;
// Print the 2d vector with a label
void dump(std::string const& label, vec_2d const& v)
{
std::cout << label << "\n";
for (std::size_t y(0); y < v.size(); ++y) {
for (std::size_t x(0); x < v[0].size(); ++x) {
std::cout << v[y][x] << " ";
}
std::cout << "\n";
}
std::cout << "\n";
}
// Recursive implementation of the search
void find_connected_r(int32_t target_color
, std::size_t x
, std::size_t y
, vec_2d const& colors
, vec_2d& result)
{
if ((result[y][x] == 1) || (colors[y][x] != target_color)) {
return;
}
result[y][x] = 1;
std::size_t width(colors[0].size());
std::size_t height(colors.size());
if (x > 0) {
find_connected_r(target_color, x - 1, y, colors, result);
}
if (y > 0) {
find_connected_r(target_color, x, y - 1, colors, result);
}
if (x < (width - 1)) {
find_connected_r(target_color, x + 1, y, colors, result);
}
if (y < (height - 1)) {
find_connected_r(target_color, x, y + 1, colors, result);
}
}
// Non-recursive implementation of the search
void find_connected(int32_t target_color
, std::size_t x
, std::size_t y
, vec_2d const& colors
, vec_2d& result)
{
std::size_t width(colors[0].size());
std::size_t height(colors.size());
typedef std::pair<std::size_t, std::size_t> position;
std::queue<position> s;
s.push(position(x, y));
while (!s.empty()) {
position pos(s.front());
s.pop();
if (result[pos.second][pos.first] == 1) {
continue;
}
if (colors[pos.second][pos.first] != target_color) {
continue;
}
result[pos.second][pos.first] = 1;
if (pos.first > 0) {
s.push(position(pos.first - 1, pos.second));
}
if (pos.second > 0) {
s.push(position(pos.first, pos.second - 1));
}
if (pos.first < (width - 1)) {
s.push(position(pos.first + 1, pos.second));
}
if (pos.second < (height - 1)) {
s.push(position(pos.first, pos.second + 1));
}
}
}
// Entry point to the search, select the implementation with last param
vec_2d find_connected(std::size_t x, std::size_t y, vec_2d const& colors, bool recursive)
{
if (colors.empty() || colors[0].empty()) {
throw std::runtime_error("Invalid input array size");
}
int32_t target_color(colors[y][x]);
vec_2d result(colors.size(), vec_1d(colors[0].size(), 0));
if (recursive) {
find_connected_r(target_color, x, y, colors, result);
} else {
find_connected(target_color, x, y, colors, result);
}
return result;
}
int main()
{
vec_2d colors{
{ 1, 1, 1, 1, 1, 1 }
, { 2, 2, 2, 3, 3, 1 }
, { 1, 1, 1, 1, 3, 1 }
, { 1, 3, 3, 3, 3, 1 }
, { 1, 1, 1, 1, 1, 1 }
};
dump("Input", colors);
dump("Search from (0,0) Recursive", find_connected(0, 0, colors, true));
dump("Search from (0,0) Loop", find_connected(0, 0, colors, false));
dump("Search from (1,1) Recursive", find_connected(1, 1, colors, true));
dump("Search from (1,1) Loop", find_connected(1, 1, colors, false));
dump("Search from (1,3) Recursive", find_connected(1, 3, colors, true));
dump("Search from (1,3) Loop", find_connected(1, 3, colors, false));
}
输出:
Input
1 1 1 1 1 1
2 2 2 3 3 1
1 1 1 1 3 1
1 3 3 3 3 1
1 1 1 1 1 1
Search from (0,0) Recursive
1 1 1 1 1 1
0 0 0 0 0 1
1 1 1 1 0 1
1 0 0 0 0 1
1 1 1 1 1 1
Search from (0,0) Loop
1 1 1 1 1 1
0 0 0 0 0 1
1 1 1 1 0 1
1 0 0 0 0 1
1 1 1 1 1 1
Search from (1,1) Recursive
0 0 0 0 0 0
1 1 1 0 0 0
0 0 0 0 0 0
0 0 0 0 0 0
0 0 0 0 0 0
Search from (1,1) Loop
0 0 0 0 0 0
1 1 1 0 0 0
0 0 0 0 0 0
0 0 0 0 0 0
0 0 0 0 0 0
Search from (1,3) Recursive
0 0 0 0 0 0
0 0 0 1 1 0
0 0 0 0 1 0
0 1 1 1 1 0
0 0 0 0 0 0
Search from (1,3) Loop
0 0 0 0 0 0
0 0 0 1 1 0
0 0 0 0 1 0
0 1 1 1 1 0
0 0 0 0 0 0
这非常简单,例如dump(...)
。
我们遍历所有元素,但不是打印值,而是打印坐标,并且仅当元素值不为0时。
void dump_coordinates(std::string const& label, vec_2d const& v)
{
std::cout << label << "\n";
for (std::size_t y(0); y < v.size(); ++y) {
for (std::size_t x(0); x < v[0].size(); ++x) {
if (v[y][x]) {
std::cout << "(" << x << ", " << y << ") ";
}
}
}
std::cout << "\n";
}
称之为:
dump_coordinates("Coordinates searching from (1,3)", find_connected(1, 3, colors, true));
你得到:
Input
1 1 1 1 1 1
2 2 2 3 3 1
1 1 1 1 3 1
1 3 3 3 3 1
1 1 1 1 1 1
...
Search from (1,3) Loop
0 0 0 0 0 0
0 0 0 1 1 0
0 0 0 0 1 0
0 1 1 1 1 0
0 0 0 0 0 0
Coordinates searching from (1,3)
(3, 1) (4, 1) (4, 2) (1, 3) (2, 3) (3, 3) (4, 3)
注意:坐标为(行,列),两者均为0索引。坐标不按搜索顺序排序。
由于我们已经有了获取所有连接元素的掩码的方法,我们只需要使用这个掩码来改变所有适当的值。
这与我们在dump_coordinates(...)
中所做的类似。
同样,我们遍历所有元素,但这次不是打印,而是在掩码值不为0时更改给定位置的颜色值。
代码:
vec_2d& change_masked(int32_t new_color
, vec_2d& colors
, vec_2d const& mask)
{
for (std::size_t y(0); y < mask.size(); ++y) {
for (std::size_t x(0); x < mask[0].size(); ++x) {
if (mask[y][x]) {
colors[y][x] = new_color;
}
}
}
return colors;
}
称之为:
dump("Search from (0,0), replace all found with color from (1,1)"
, change_masked(colors[1][1], colors, find_connected(0, 0, colors, true)));
输出:
Input
1 1 1 1 1 1
2 2 2 3 3 1
1 1 1 1 3 1
1 3 3 3 3 1
1 1 1 1 1 1
Search from (0,0) Recursive
1 1 1 1 1 1
0 0 0 0 0 1
1 1 1 1 0 1
1 0 0 0 0 1
1 1 1 1 1 1
...
Search from (0,0), replace all found with color from (1,1)
2 2 2 2 2 2
2 2 2 3 3 2
2 2 2 2 3 2
2 3 3 3 3 2
2 2 2 2 2 2
答案 1 :(得分:1)
我认为在以下情况下您的代码可能有问题:
1 1 2
1 1 A
1 1 1
想象一下A的颜色是1.它应该存储为触摸,但由于上面的块不是相同的颜色,A将被视为不接触。
我对这种算法的方法类似于下面的伪代码(如果上面的代码是固定的,你的方法似乎很好)
/** Method to find all touching blocks of the same color */
void findColoredNeighbors(Block block){
// Add current block to 'touching'
touching_array[block.x][block.y] = 1;
// Check all adyacent blocks to see if any of them matches the color
for (Position position: getAdyacentBlocks(block)){
// If a block matches the color, we have to make sure it isn't stored
// as touching yet to avoid infite recursion
if((colors_array[position.x][position.y] == block.color) && (touching_array[position.x][position.y] != 1))
findColoredNeighbors(getBlock(position.x, position.y));
}
}
此方法假定您在调用之前清除了touching_array,并且您有一个getAdyacentBlocks(Block b)
,它返回作为参数传递的块旁边的块列表。
希望它有所帮助!