-
Notifications
You must be signed in to change notification settings - Fork 0
733. Flood Fill #9
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| 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; | ||
|
|
||
| // 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 | ||
|
|
||
| * 今回は時間がないので省略 | ||
| 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; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 二次元座標を表すにあたり、 std::pair を避け、独自の構造体を作る方もいらっしゃると思います。所属するチームの平均的なソフトウェアエンジニアの書き方に合わせることをおすすめします。 なお、二次元座標を表すにあたり、 std::complex を使うという方法もなくはないのですが、分かりづらく、利点は少ないように感じます。
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ありがとうございます。#11 でちょうどそのような感じの問題があったので
|
||
| 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; | ||
| } | ||
| }; | ||
| 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); | ||
| } | ||
| }; |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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++ ラムダについてはまだ全く使える気がしないので、機会があれば適宜練習してみたいと思います。