Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
112 changes: 112 additions & 0 deletions 733_flood_fill/memo.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
# 733. Flood Fill

https://leetcode.com/problems/flood-fill/

## Comments

### step1

* https://cpprefjp.github.io/reference/utility/make_pair.html
* https://cpprefjp.github.io/lang/cpp17/structured_bindings.html
* `auto` でいける
* 細かい修正はありつつ、AC
* 選択肢として、再帰関数を使う、queue を使った iterative approach などが頭にあった。どれでも良かったが、stack を使う iterative にしてみた
* in-place で変えていくので、`visited` は必須ではないもののこちらのほうが可読性が高いように思う
* 範囲チェックは stack に積む前にやってもよいのだが、4 方向に進むループをネストするのが嫌だったので、多少のオーバーヘッドは覚悟して一旦追加してからチェックすることにした。

```cpp
int dr[] = {-1, 1, 0, 0};
int dc[] = {0, 0, -1, 1};

// 2. Check all 4 neighbors
for (int i = 0; i < 4; ++i) {
int next_r = r + dr[i];
int next_c = c + dc[i];
...
}
```

### step2

* 一応再帰でも書いてみた (`step2.Solution`)
* 再帰関数を定義するとき、C++ に inner function というのはあるんだろうか。`image` その他引数が多くて邪魔な印象もある。
* Gemini には、メンバ変数を使うことを提案された。
* ラムダ式で同じようなことができるらしいということも言われた
* 一応 Gemini はこんなの出してきた。ラムダ式…なるほど…
* 今回自分で書くのは見送り。キャプチャという概念があるのか。


```
*Gemini*

ラムダ式の主な特徴と使い方
構文: [キャプチャ](引数) { 本体 } という基本的な形をしています。

変数キャプチャ: [] の部分で、外側のスコープの変数をどのように扱うかを指定します。

[x] : 変数 x を値でコピーして利用する。

[&x] : 変数 x を参照で利用する(外側の x を変更できる)。

[=] : スコープ内のすべての変数を値でキャプチャする。

[&] : スコープ内のすべての変数を参照でキャプチャする。

主な用途: 特に、標準ライブラリ(STL)のアルゴリズム(std::sort, std::for_each, std::find_if など)に短い処理をその場で渡す際に非常に強力です。
```

```cpp
#include <vector>
#include <functional> // std::function を使うために必要

class Solution {
public:
vector<vector<int>> floodFill(vector<vector<int>>& image, int sr, int sc, int color) {
int original_color = image[sr][sc];

// 開始色が目的の色と同じなら何もしない
if (original_color == color) {
return image;
}

int num_rows = image.size();
int num_cols = image[0].size();

// 1. 再帰呼び出し用のラムダ式を std::function として宣言
std::function<void(int, int)> dfs;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

念の為 C++ lambda と function class は異なるものです。

私は C++ の場合は外で関数にしたいですね。テストも書きやすいですし。このあたりに色々あります。
https://docs.google.com/document/d/11HV35ADPo9QxJOpJQ24FcZvtvioli770WWdZZDaLOfg/edit?tab=t.0#heading=h.yfb442pj2a6p

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

function class (https://cpprefjp.github.io/reference/functional/function.html) の方は、ラムダ式を含む、より包括的な概念だという理解です。
積極的に使うことは少ないかもしれませんが、C++ ラムダについてはまだ全く使える気がしないので、機会があれば適宜練習してみたいと思います。


// 2. ラムダ式を定義し、自身をキャプチャさせる
// [&] で dfs 自身と、image, color などの変数をキャプチャする
dfs = [&](int r, int c) {
// 範囲外か、色が異なる(訪問済み)場合は終了
if (r < 0 || r >= num_rows || c < 0 || c >= num_cols || image[r][c] != original_color) {
return;
}

// 色を塗り替える
image[r][c] = color;

// 再帰呼び出し
dfs(r + 1, c);
dfs(r - 1, c);
dfs(r, c + 1);
dfs(r, c - 1);
};

// 3. ラムダ式を実行開始
dfs(sr, sc);

return image;
}
};
```

* 今回は in-place で変更しているが、コピーして返したい場合は?
* `vector<vector<int>> result_image = image;` でコピーになるっぽい??
* `std::pair` をより一般化したのが `tuple` か
* https://cpprefjp.github.io/reference/utility/pair.html
* C++ は `std` namespace に色々定義されているのはいいとして、どのヘッダを include すればいいのかまだわからんやつが多い。データ構造系はその名前のヘッダであるのが多い気がする (`stack` とか)。`pair` は `utility`

### step3

* 今回は時間がないので省略
36 changes: 36 additions & 0 deletions 733_flood_fill/step1.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#include <set>
#include <stack>
#include <utility>

class Solution {
public:
vector<vector<int>> floodFill(vector<vector<int>>& image, int sr, int sc, int color) {
int original_color = image[sr][sc];
int num_rows = image.size();
int num_cols = image[0].size();
std::set<std::pair<int, int>> visited;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

二次元座標を表すにあたり、 std::pair を避け、独自の構造体を作る方もいらっしゃると思います。所属するチームの平均的なソフトウェアエンジニアの書き方に合わせることをおすすめします。

なお、二次元座標を表すにあたり、 std::complex を使うという方法もなくはないのですが、分かりづらく、利点は少ないように感じます。

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ありがとうございます。#11 でちょうどそのような感じの問題があったので class で書いてみました (C++ style guide の struct vs. class のあたりも参照しました)。

std::complex を知らなかったので調べてみたところ、虚数を表すためのものなんですね。
https://cpprefjp.github.io/reference/complex/complex.html
座標系を表すのはクラスの趣旨から外れるのではないかと思いましたが、こういった議論もあるんですね。
https://qiita.com/SaitoAtsushi/items/28e0ca10dd06f3a8c99b

std::stack<std::pair<int, int>> pixels;
pixels.push(std::make_pair(sr, sc));
while (!pixels.empty()) {
std::pair<int, int> position = pixels.top();
auto [x, y] = position;
pixels.pop();
if (!(0 <= x && x < num_rows && 0 <= y && y < num_cols)) {
continue;
}
if (visited.contains(position)) {
continue;
}
visited.insert(position);
if (image[x][y] != original_color) {
continue;
}
image[x][y] = color;
pixels.push(std::make_pair(x + 1, y));
pixels.push(std::make_pair(x - 1, y));
pixels.push(std::make_pair(x, y + 1));
pixels.push(std::make_pair(x , y - 1));
}
return image;
}
};
33 changes: 33 additions & 0 deletions 733_flood_fill/step2.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#include <set>
#include <utility>

class Solution {
public:
vector<vector<int>> floodFill(vector<vector<int>>& image, int sr, int sc, int color) {
int original_color = image[sr][sc];
std::set<std::pair<int, int>> visited;
floodFillHelper(image, sr, sc, original_color, color, visited);
return image;
}
private:
void floodFillHelper(vector<vector<int>>& image, int x, int y, int original_color, int color, std::set<std::pair<int, int>>& visited) {
int num_rows = image.size();
int num_cols = image[0].size();
if (!(0 <= x && x < num_rows && 0 <= y && y < num_cols)) {
return;
}
auto position = std::make_pair(x, y);
if (visited.contains(position)) {
return;
}
visited.insert(position);
if (image[x][y] != original_color) {
return;
}
image[x][y] = color;
floodFillHelper(image, x + 1, y, original_color, color, visited);
floodFillHelper(image, x - 1, y, original_color, color, visited);
floodFillHelper(image, x, y + 1, original_color, color, visited);
floodFillHelper(image, x, y - 1, original_color, color, visited);
}
};
Empty file added 733_flood_fill/step3.cpp
Empty file.