问题
I am trying to find out a block of same colored region starting from the top left corner in a 2D matrix. For example: I have the following Matrix:
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
say, The initial top left corner is 1 and I want to find out the adjacent area containing 1's (I will only consider starting from the top left corner). In the above matrix the digits 1,2,3,4,5 represent different colors. I have tried using the following code segment for finding out the same colored block:
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
But I haven't got the same colored block starting from top left corner as output. Is there anything wrong in the above code segment? How will I make it correct? It would be better If someone provide a C/C++ solution of the above problem.
回答1:
You forgot that the tiles can touch along both axes, in all 4 directions.
Consider the following 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
I wrote a new algorithm in two versions -- one using recursion, the other a loop and queue. It is similar to what J.Mac described in his answer.
The algorithm is simple. We are given a target color to search for, the position to search at, an input matrix of colors, and output matrix of flags to identify matched tiles.
To search a tile:
- If the tile has the correct color AND it is not marked
- Mark the tile
- Search all neighboring tiles (2-4 depending on whether we're in the middle, at the edge or in the corner).
We can either use recursion to search the neighboring tiles, or we can use a queue to keep track of the tiles that still need to be searched and repeatedly searching tiles from the front of the queue until none remain.
#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));
}
Output:
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
Printing coordinates
This is very simple, like dump(...)
.
We iterate through all the elements, but instead of printing values we print coordinates, and only when the element value is not 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";
}
Call it:
dump_coordinates("Coordinates searching from (1,3)", find_connected(1, 3, colors, true));
And you get:
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)
NB: Coordinates are (row, column), and both are 0-indexed. The coordinates are not sorted in order of search.
Replacing connected elements with another color
Since we already have a method to obtain a mask of all the connected elements, we just need to use this mask to change all the appropriate values.
This is similar to what we did in dump_coordinates(...)
.
Again, we iterate over all the elements, but this time instead of printing, we change the color value at given position when the mask value is not 0.
Code:
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;
}
Call it:
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)));
Output:
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
回答2:
I think you might have a problem with your code in the following case:
1 1 2
1 1 A
1 1 1
Imagine A's color was 1. It should be stored as touching, but as the block above isn't of the same color, A will be considered as not touching.
My approach for this kind of algorithm would be something like the following pseudo-code (although yours seems good if the above is fixed)
/** 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));
}
}
This method assumes that you clear touching_array before calling it, and you have a getAdyacentBlocks(Block b)
which returns a list of the blocks next to the one passed as parameter.
Hope it helps!
来源:https://stackoverflow.com/questions/36349593/finding-out-same-colored-block-in-a-2d-matrix