Skip to content
Open
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
133 changes: 129 additions & 4 deletions graph-bfs-dns/200.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,131 @@
- 問題: []()
- コメント集: [](https://docs.google.com/document/d/11HV35ADPo9QxJOpJQ24FcZvtvioli770WWdZZDaLOfg/mobilebasic#h.o0jquy48e6cy)
- 問題: [200. Number of Islands](https://leetcode.com/problems/number-of-islands/submissions/2008160365/)
- コメント集: [200. Number of Islands](https://docs.google.com/document/d/11HV35ADPo9QxJOpJQ24FcZvtvioli770WWdZZDaLOfg/mobilebasic#h.o0jquy48e6cy)
- 一応全て確認したが、Python関係の話が多かったと思う
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

コメント集にあったか覚えてないですが、今回は与えられた配列を破壊するべきかしないべきかの検討があってもいいと思いました。

例えば、何か島を作る関数が別であった時などにデバッグで使うを想定すると、今回は引数で与えられた配列は破壊したくないですね

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

ありがとうございます。
ここ、対応しようと思って忘れてました。
取り込みます。

- コメント先のPullRequestで、column, row の変数名の話と引数を変更するのはどうなんだ?という話があったので考えてみた
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

考えてみた内容か、それを踏まえたコードがあると良いと思います。
変数名を変えた方が良いと思ったのかそうでないのか(あるいはよくわからないか)、読み取れずレビューで迷いました。

- 条件
- m == grid.length
- n == grid[i].length
- 1 <= m, n <= 300
- grid[i][j] is '0' or '1'.
- 方針1
- 時間計算量:
- 空間計算量:
- grid に対して二重ループを回しながら1件ずつ走査する
- 走査の過程で、`grid[column][row]` が land の場合、隣接する land を全て water に変更するような処理を再帰関数(dfs)で組む
- DFSのイメージ: 行けるところまで深く潜って戻ってきてね
- DFSのユースケース: 全部探索, `find`コマンドで目的のファイルを探すなどが該当する
- 方針2
- grid に対して二重ループを回しながら1件ずつ走査する
- 基本的にはDFSと同じだが、`queue`を使って波紋が広がるように調べていく点が少し違う
- BFSのイメージ: 波紋が広がるように、起点から調べていく. DFSとの差分でいうと、最初に到達した探索結果が必ず最短が保証されている.なぜかというと、Queueに順序付きQueueを使っており、挿入した順序が必ず守られているから
- BFSのユースケース: マップの最短経路探索
- 時間計算量: `O(m*n)`
- 空間計算量: `O(m*n)` --> 最悪、全ての要素がコールスタックに乗ることになる, BFSは全ての要素がQueueにのることになる

## DFS
- 20分
```java
class Solution {
public int numIslands(char[][] grid) {
if (grid.length == 0) return 0;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

こちらのコメントをご参照ください。
Shunii85/arai60#17 (comment)


int result = 0;
for (int rowIndex = 0; rowIndex < grid.length; rowIndex++) {
for (int columnIndex = 0; columnIndex < grid[0].length; columnIndex++) {
result += numIslandsHelper(grid, rowIndex, columnIndex);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

この関数に返り値はなくてもいいかなと思いました。島があるかないかで1か0が返ってくるだけなので。

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

おっしゃる通りですね。
別に += する必要がないですね。。

}
}

return result;
}

private int numIslandsHelper(char[][] grid, int rowIndex, int columnIndex) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

このメソッドは、numIslandsHelperと名付けるにはnumIslandsと違いすぎると思いました。返り値も0か1だけですし。
良い名前が思いつかないですが、たとえばtraverseはどうでしょうか。

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

同感です。今回は特に副作用としてgridの書き換えも行なっているのでそれがわかるようにしたいですね。
自分が見ていていいなと思ったのはsinkIslandsなどですね。1を0に置き換えることをsinkと表現するのがわかりやすいです

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

お二人ともありがとうございます、
Traversal は処理の意味として理解できていいですね。
自分的にはsink がかなりしっくり来ました。
陸を沈めるってことですね、いいですね!

// index ouf of range
if ((rowIndex < 0 || columnIndex < 0) || (rowIndex >= grid.length || columnIndex >= grid[0].length)) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

数直線上に一直線上に並ぶように書いたほうが、読み手にとって読みやすくなると思います。

if (!(0 <= rowIndex && rowIndex < grid.length && 0 <= columnIndex && columnIndex < grid[0].length)) {

return 0;
}
// find water
if (grid[rowIndex][columnIndex] == '0') {
return 0;
}
// find land
if (grid[rowIndex][columnIndex] == '1') {
grid[rowIndex][columnIndex] = '0';
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

こちらのコメントをご参照ください。
Hiroto-Iizuka/coding_practice#17 (comment)

// seach top, left, right, bottom of this land
// find top
numIslandsHelper(grid, rowIndex - 1, columnIndex);
// find bottom
numIslandsHelper(grid, rowIndex + 1, columnIndex);
// find left
numIslandsHelper(grid, rowIndex, columnIndex - 1);
// find right
numIslandsHelper(grid, rowIndex, columnIndex + 1);

return 1;
}

throw new IllegalArgumentException("The argument grid[][] contained a character other than 0 or 1");
}
}
```

## BFS
```java
class Solution {
public int numIslands(char[][] grid) {
if (grid.length == 0) return 0;

int result = 0;
for (int rowIndex = 0; rowIndex < grid.length; rowIndex++) {
for (int columnIndex = 0; columnIndex < grid[0].length; columnIndex++) {
result += numIslandsHelper(grid, rowIndex, columnIndex);
}
}

return result;
}

private int numIslandsHelper(char[][] grid, int rowIndex, int columnIndex) {
Queue<int[]> lands = new ArrayDeque<>();
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

int[]ではなくrecordを使う選択肢もあると思います。

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

可読性を考えるとおっしゃる通りかと。
ただ、うーーんこの規模であればint[]で十分だと思ってます

// find land
if (grid[rowIndex][columnIndex] == '1') {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

もう少しシンプルに early return したほうが、読み手にとって読みやすくなると思います。

if (grid[rowIndex][columnIndex] == '0') {
    return 0;
}

grid[rowIndex][columnIndex] = '0';
lands.add(new int[]{rowIndex, columnIndex});

grid[rowIndex][columnIndex] = '0';
lands.add(new int[]{rowIndex, columnIndex});
} else {
return 0;
}

while (!lands.isEmpty()) {
int[] index = lands.poll();
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

細かいかもしれませんが、キューへの追加と削除についてadd/pollを使っていますが、add/removeかoffer/pollのどちらかだけを使う方が統一感があり好みです。
https://docs.oracle.com/en/java/javase/26/docs/api/java.base/java/util/Queue.html
ArrayDequeなのでどちらでも差はなさそうですが。

Copy link
Copy Markdown
Owner Author

@hiroki-horiguchi-dev hiroki-horiguchi-dev May 23, 2026

Choose a reason for hiding this comment

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

コメントありがとうございます、読みました。
統一感、なるほど確かにそうかもしれないですね。実際私のコードでは統一されていません。

私の認識で恐縮ですが、どちらかというと、例外投げて欲しければ add/remove, 例外ではなく値が欲しいなら offer/poll を使おうということで、文脈に応じて使い分けるものかなと思っているのですが、どうでしょう?

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

すみません、そうですね。間違って覚えていました……。
addとofferはArrayDequeなら同じだと思いますが(ソースコード https://github.com/openjdk/jdk/blob/master/src/java.base/share/classes/java/util/ArrayDeque.java )、removeとpollは空の場合に違いますね。使い分けるものなのだろうと思いました。

int numRow = index[0];
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

考えているかもしれませんが、この変数名は見づらく感じました。numと付くと「rowの個数」のように見えるからです。
これをrowIndexとし、引数の方はstartRowIndexとするのはどうでしょうか。

int numColumn = index[1];

// 上下左右を探索して、1ならqueueにつめる
int nextNumRow = numRow + 1;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

これは変数に置かないほうが直接的で見やすいと感じました。

int previousNumRow = numRow - 1;
int nextNumColumn = numColumn + 1;
int previousNumColumn = numColumn - 1;
// top
if (previousNumRow >= 0 && grid[previousNumRow][numColumn] == '1') {
grid[previousNumRow][numColumn] = '0';
lands.add(new int[]{previousNumRow, numColumn});
}
// bottom
if (nextNumRow < grid.length && grid[nextNumRow][numColumn] == '1') {
grid[nextNumRow][numColumn] = '0';
lands.add(new int[]{nextNumRow, numColumn});
}
// left
if (previousNumColumn >= 0 && grid[numRow][previousNumColumn] == '1') {
grid[numRow][previousNumColumn] = '0';
lands.add(new int[]{numRow, previousNumColumn});
}
// right
if (nextNumColumn < grid[0].length && grid[numRow][nextNumColumn] == '1') {
grid[numRow][nextNumColumn] = '0';
lands.add(new int[]{numRow, nextNumColumn});
}
}

return 1;
}
}
```