目次
1. 画像のレンダリング
この質問では、指定された 2 次元配列内の指定された「カラー ブロック」を別の色に染める必要があります。「カラーブロック」の定義は、直接的または間接的に隣接する同じ色の正方形で構成される全体です。
「カラーブロック」は、異なる色の正方形で囲まれた同じ色の島であることがわかります。カラー ブロック内の任意の場所から開始し、幅優先検索または深さ優先検索を使用して島全体を横断します。
注: ターゲットの色が初期の色と同じ場合、元の配列を変更する必要はありません。
1. 幅優先検索
アイデアとアルゴリズム
指定された開始点から開始して、幅優先検索を実行します。マス目が検索されるたびに、最初の位置のマス目と同じ色であれば、そのマス目がキューに追加され、繰り返しキューに入るのを防ぐためにマス目の色が更新されます。
注: 初期位置の色が変更されるため、その後の更新操作を容易にするために初期位置の色を保存する必要があります。
class Solution {
public:
const int dx[4] = {1, 0, 0, -1};
const int dy[4] = {0, 1, -1, 0};
vector<vector<int>> floodFill(vector<vector<int>>& image, int sr, int sc, int color) {
int currColor = image[sr][sc];
if (currColor == color) {
return image;
}
int n = image.size(), m = image[0].size();
queue<pair<int, int>> que;
que.emplace(sr, sc);
image[sr][sc] = color;
while (!que.empty()) {
int x = que.front().first, y = que.front().second;
que.pop();
for (int i = 0; i < 4; i++) {
int mx = x + dx[i], my = y + dy[i];
if (mx >= 0 && mx < n && my >= 0 && my < m && image[mx][my] == currColor) {
que.emplace(mx, my);
image[mx][my] = color;
}
}
}
return image;
}
};
複雑さの分析
時間計算量: O(n×m)。n と m はそれぞれ 2 次元配列の行数と列数です。最悪の場合、すべての正方形を 1 回通過する必要があります。
空間複雑さ: O(n×m)。n と m はそれぞれ 2 次元配列の行数と列数です。主にキューのオーバーヘッドが原因です。
2. 深さ優先検索
アイデアとアルゴリズム
指定された開始点から開始し、深さ優先検索を実行します。マス目を探索するたびに、最初の位置のマス目と同じ色の場合はマス目の色を更新して重複探索を防ぎ、同じでない場合はバックトラッキングを行います。
注: 初期位置の色が変更されるため、その後の更新操作を容易にするために初期位置の色を保存する必要があります。
class Solution {
public:
const int dx[4] = {1, 0, 0, -1};
const int dy[4] = {0, 1, -1, 0};
void dfs(vector<vector<int>>& image, int x, int y, int currColor, int color) {
if (image[x][y] == currColor) {
image[x][y] = color;
for (int i = 0; i < 4; i++) {
int mx = x + dx[i], my = y + dy[i];
if (mx >= 0 && mx < image.size() && my >= 0 && my < image[0].size()) {
dfs(image, mx, my, currColor, color);
}
}
}
}
vector<vector<int>> floodFill(vector<vector<int>>& image, int sr, int sc, int color) {
int currColor = image[sr][sc];
if (currColor != color) {
dfs(image, sr, sc, currColor, color);
}
return image;
}
};
複雑さの分析
時間計算量: O(n×m)。n と m はそれぞれ 2 次元配列の行数と列数です。最悪の場合、すべての正方形を 1 回通過する必要があります。
空間複雑さ: O(n×m)。n と m はそれぞれ 2 次元配列の行数と列数です。主にスタックスペースのオーバーヘッドが原因です。
2.島の最大面積
1. 深さ優先検索
グリッド内の接続された各形状の面積を知り、最大値を取得したいと考えています。
私たちが 1 つの土地にいて、それに接続されているすべての土地 (およびそれらの土地に接続されている土地) を 4 方向に探索した場合、探索された土地の総数は、その接続された形状の面積になります。
各土地を複数回訪問しないようにするために、この土地を通過するたびにこの土地の価値を 0 に設定します。こうすることで、同じ土地を何度も訪れることがなくなります。
class Solution {
int dfs(vector<vector<int>>& grid, int cur_i, int cur_j) {
if (cur_i < 0 || cur_j < 0 || cur_i == grid.size() || cur_j == grid[0].size() || grid[cur_i][cur_j] != 1) {
return 0;
}
grid[cur_i][cur_j] = 0;
int di[4] = {0, 0, 1, -1};
int dj[4] = {1, -1, 0, 0};
int ans = 1;
for (int index = 0; index != 4; ++index) {
int next_i = cur_i + di[index], next_j = cur_j + dj[index];
ans += dfs(grid, next_i, next_j);
}
return ans;
}
public:
int maxAreaOfIsland(vector<vector<int>>& grid) {
int ans = 0;
for (int i = 0; i != grid.size(); ++i) {
for (int j = 0; j != grid[0].size(); ++j) {
ans = max(ans, dfs(grid, i, j));
}
}
return ans;
}
};
複雑さの分析
時間計算量: O(m×n)。ここで、mm は特定のグリッドの行数、n は列数です。各グリッドを最大 1 回訪問します。
空間複雑さ: O(m×n) 再帰の最大の深さはグリッド全体のサイズになる可能性があるため、可能な最大スタック空間は O(m×n) です。
2. 深さ優先探索 + スタック
アルゴリズム
スタックを使用して深さ優先検索アルゴリズムを実装できます。この方法は基本的に方法 1 と同じですが、唯一の違いは次のとおりです。
方法 1 では、関数呼び出しを使用して、次にどのランドを通過するかを示し、関数の次の層がこれらのランドにアクセスできるようにします。2 番目の方法は、次に通過したい土地をスタックに置き、これらの土地を取り出すときにアクセスします。
各土地を訪れるときは、その周囲の 4 方向を探索し、まだ訪問していない土地を見つけてスタックに追加します。
さらに、スタックが空でない限り、アクセスできる土地がまだあるということなので、スタックから要素を取得してアクセスします。
class Solution {
public:
int maxAreaOfIsland(vector<vector<int>>& grid) {
int ans = 0;
for (int i = 0; i != grid.size(); ++i) {
for (int j = 0; j != grid[0].size(); ++j) {
int cur = 0;
stack<int> stacki;
stack<int> stackj;
stacki.push(i);
stackj.push(j);
while (!stacki.empty()) {
int cur_i = stacki.top(), cur_j = stackj.top();
stacki.pop();
stackj.pop();
if (cur_i < 0 || cur_j < 0 || cur_i == grid.size() || cur_j == grid[0].size() || grid[cur_i][cur_j] != 1) {
continue;
}
++cur;
grid[cur_i][cur_j] = 0;
int di[4] = {0, 0, 1, -1};
int dj[4] = {1, -1, 0, 0};
for (int index = 0; index != 4; ++index) {
int next_i = cur_i + di[index], next_j = cur_j + dj[index];
stacki.push(next_i);
stackj.push(next_j);
}
}
ans = max(ans, cur);
}
}
return ans;
}
};
複雑さの分析
時間計算量: O(m×n)。ここで、m は特定のグリッドの行数、n は列数です。各グリッドを最大 1 回訪問します。
空間複雑度: O(m×n) スタックに格納できるランドは最大ですべてのランド、ランド数は最大 m×n ブロックであるため、使用される空間は O(m×n) となります。
3. 幅優先検索
アルゴリズム
方法2のスタックをキューに変更し、その都度キューの先頭からランドを取り出し、次にたどりたいランドをキューの最後に置くことで幅優先探索アルゴリズムを実現します。
class Solution {
public:
int maxAreaOfIsland(vector<vector<int>>& grid) {
int ans = 0;
for (int i = 0; i != grid.size(); ++i) {
for (int j = 0; j != grid[0].size(); ++j) {
int cur = 0;
queue<int> queuei;
queue<int> queuej;
queuei.push(i);
queuej.push(j);
while (!queuei.empty()) {
int cur_i = queuei.front(), cur_j = queuej.front();
queuei.pop();
queuej.pop();
if (cur_i < 0 || cur_j < 0 || cur_i == grid.size() || cur_j == grid[0].size() || grid[cur_i][cur_j] != 1) {
continue;
}
++cur;
grid[cur_i][cur_j] = 0;
int di[4] = {0, 0, 1, -1};
int dj[4] = {1, -1, 0, 0};
for (int index = 0; index != 4; ++index) {
int next_i = cur_i + di[index], next_j = cur_j + dj[index];
queuei.push(next_i);
queuej.push(next_j);
}
}
ans = max(ans, cur);
}
}
return ans;
}
};
複雑さの分析
時間計算量: O(m×n)。ここで、m は特定のグリッドの行数、n は列数です。各グリッドを最大 1 回訪問します。
空間複雑さ: O(m×n) キューに最大ですべてのランドを格納可能 ランドの数は最大で m×n ブロックなので、使用される空間は O(m×n) です。