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
233 changes: 233 additions & 0 deletions 111. Minimum Depth of Binary Tree.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
### Step1

- 最初、こう間違えた
- 入力が空の考慮をしなかったのはダメだったが、空の時の出力が0というのもなんか微妙だな

```python
class Solution:
def minDepth(self, node: Optional[TreeNode]) -> int:
if not node:
return inf
if not node.left and not node.right:
return 1
return min(self.minDepth(node.left), self.minDepth(node.right)) + 1
```

- infはあんまり使いたくないなあ
- float型が出るのと、意図がわかりにくいかも
- 思ったよりちょっとだけややこしくなった
- 再帰の回数は、枝の数だけある。
- 10^5が最大で、環境によっては超えるかも

```python
class Solution:
def minDepth(self, node: Optional[TreeNode]) -> int:
if not node:
return 0
if not node.left and not node.right:
return 1
if not node.right:
return self.minDepth(node.left) + 1
if not node.left:
return self.minDepth(node.right) + 1
return min(self.minDepth(node.left), self.minDepth(node.right)) + 1
Comment on lines +27 to +33
Copy link

Choose a reason for hiding this comment

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

これ、まとまりますかね?

Copy link

Choose a reason for hiding this comment

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

あ、すみません、勘違いです。min ですね。

```

- ループにするため、まずは末尾再帰(ぽい形)に
- 別にnot_reached_leafとかで変数も作れるけど、まあinfで妥協

```python

class Solution:
def minDepth(self, root: Optional[TreeNode]) -> int:
if not root:
return 0
depth_min = inf

def min_depth_helper(node, depth):
nonlocal depth_min
if not node:
return
if not node.left and not node.right:
depth_min = min(depth_min, depth)
min_depth_helper(node.left, depth + 1)
min_depth_helper(node.right, depth + 1)

min_depth_helper(root, 1)
return depth_min
```

- 上の処理をループに
- leafの判定は切り出した方がわかりやすいかも
- タプルの中身2つだとdataclass作るまでもないかなあ

```python
class Solution:
def is_leaf(self, node: [TreeNode]) -> bool:

Choose a reason for hiding this comment

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

node: [TreeNode]これはnode: Optional[TreeNode]の間違えでしょうか?

(その場合だと、nodeにNoneが来る想定になるので、TreeNodeの方がいいですね)

Copy link
Owner Author

Choose a reason for hiding this comment

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

型注釈の書き方がよくわかっておらず変な書き方をしてしまいました🙏
単なるTreeNodeのつもりできた

return not node.left and not node.right

def minDepth(self, root: Optional[TreeNode]) -> int:
if not root:
return 0
stack = [(root, 1)]
result = inf
while stack:

Choose a reason for hiding this comment

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

この解法だけstackといった命名を用いているのが気になりました。他に合わせてdepth_minやnode_and_depthでもいいのかとおもいました。

Copy link
Owner Author

Choose a reason for hiding this comment

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

ありがとうございます そうですね、ちょっと不親切だったかもしれません

node, depth = stack.pop()
if not node:
continue
if self.is_leaf(node):
result = min(depth, result)
continue
stack.append((node.left, depth + 1))
stack.append((node.right, depth + 1))
return result

```

- BFSでも書いてみる

```python

class Solution:
def is_leaf(self, node: [TreeNode]) -> bool:
return not node.left and not node.right

def minDepth(self, root: Optional[TreeNode]) -> int:
if not root:
return 0
current_level = [(root, 1)]

Choose a reason for hiding this comment

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

level(depth?)の他にノードが入っているので、命名がやや気になるかもです。

Copy link
Owner Author

Choose a reason for hiding this comment

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

なるほど、levelで終わってるとdepthしか入ってないように確かに見えるかもですね。
ありがとうございます。

result = inf
while True:
if not current_level:
return result
next_level = []
for node, depth in current_level:
if not node:
continue
if self.is_leaf(node):
result = min(depth, result)
next_level.append((node.left, depth + 1))
next_level.append((node.right, depth + 1))
current_level = next_level
```

### Step2

https://github.com/seal-azarashi/leetcode/pull/21

- BFSはlevel別に配列管理した方が好み
- BFSでループが回る場合の処理を書くなら、無限ループの方が意図がわかりやすい、確かに

https://github.com/goto-untrapped/Arai60/pull/46

- DFSだったら枝狩りできるの気がつかなかった〜
- こういうの自分気づかないがち
- while Trueを嫌がるコメント、他の方のでも見たことあるが、個人的に賛成はできない。while(条件)の条件部分を、if not 条件 breakって中に書けば同じなので
- ループの停止が決定不能問題だから、ループそのもの自体そもそも気をつけようというのはあるかも
- infをINITIALIZEDにするのはなるほどと思ったが、それなら別変数で定義する方がわかりやすいかもなあ
- rootがnullの判定をやめて最後にreturn 0するのはあまり好みではない。return 0がなんの時なのかがわかりにくいため

https://github.com/Ryotaro25/leetcode_first60/pull/24

- nullの場合を探索しない再帰も実装してもいいかも

https://github.com/TORUS0818/leetcode/pull/24

- 単位元が0のminをカスタム定義は、中身がわけわかんなくなるかも?

https://github.com/hroc135/leetcode/pull/21/files

- BFS、よく考えたらdepthはタプルに入れなくてもdepth+=1でよかった
- depth += 1をループの最初に書くか、最後に書くか、迷う
Copy link

Choose a reason for hiding this comment

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

私は最後派ですが、どちらでもいいのではないでしょうか。

Copy link
Owner Author

Choose a reason for hiding this comment

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

@oda さんはちなみにどういう理由で最後派ですか?
(結果としてどちらを採用するかはともかく、判断基準を持っておきたいなと思い…)

Copy link

Choose a reason for hiding this comment

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

C++ だったら気持ちとして、

for (int depth = 1; !current_level_nodes.empty(); ++depth) {
    ...
    current_level_nodes.swap(next_level_nodes);
}

じゃないですかね。

Copy link
Owner Author

Choose a reason for hiding this comment

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

なるほど、C++だとそういう書き方ができるんですね。
ありがとうございます。

Copy link

Choose a reason for hiding this comment

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

要するに、depth はループのテーマのようなもので、++depth というのがループを次の行に行くための処理であると見て、ループの中では depth は最後に変化するほうが理解しやすいだろうということです。

Copy link
Owner Author

Choose a reason for hiding this comment

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

そうですね、テーマが最初にいきなり変化するのは確かにちょっと追いにくいかもですね(ループを引き継ぎだと考えると)
ありがとうございます

- メインの処理で、最初にないと読んでて不安になる?
- でも重要な変数が最初にいきなり変わるのは混乱する?
- う〜む難しい
- 償却計算量、CSライザップでやったが、ちゃんとした式を追ってなかったので勉強になった

https://github.com/Yoshiki-Iwasa/Arai60/pull/25/files

- .leftと.rightをループで回してもいいのか、迷う

- 枝刈りBFSを書いてみる
- 試しに今回はnullは突っ込まないように

```python

class Solution:
def is_leaf(self, node: [TreeNode]) -> bool:
return not node.left and not node.right

def minDepth(self, root: Optional[TreeNode]) -> int:
if not root:
return 0
current_level_nodes = [root]
depth = 0
while True:
depth += 1
next_level_nodes = []
for node in current_level_nodes:
if self.is_leaf(node):
return depth
if node.left:
next_level_nodes.append(node.left)
if node.right:
next_level_nodes.append(node.right)
current_level_nodes = next_level_nodes

Choose a reason for hiding this comment

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

current_level_nodesとnext_level_nodeは一つに纏めてもいいなかと思いました。

Copy link
Owner Author

Choose a reason for hiding this comment

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

Choose a reason for hiding this comment

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

全部読んでみないとわからないという事ですかね。
確かに一度解いたことがあり知っているから言えたことですね。
ありがとうございます!

```

- infの代わりに、まだ見てないかを別変数で定義

Choose a reason for hiding this comment

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

宗派にもよりそうですが、Pythonだとやり過ぎじゃないですかねー?C, C++歴が長い人は別変数にする方を好みそうな気もします。知らないですが。

- やりすぎ?

```python

class Solution:
def is_leaf(self, node: [TreeNode]) -> bool:
return not node.left and not node.right

def minDepth(self, root: Optional[TreeNode]) -> int:
if not root:
return 0
node_with_depth = [(root, 1)]
reached_leaf_so_far = False

Choose a reason for hiding this comment

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

なんか変数名が分かりづらいです。is_first, first_nodeあたりはどうですかね?

Choose a reason for hiding this comment

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

nittocoさん、変数名にso_farつけがちですが、自分はそんないい命名じゃないなーという気がしています。「これまでに」くらいの意味だと思っていて、具体性が低いのとあえて明示的に加えて嬉しい場面が少ない気がします。

たとえば、is_visited_so_farという変数名で「これまでに訪れたかどうかを」表そうとしても、is_visitedだけでもこのニュアンスは含まれていると思うんですよね。

Copy link
Owner Author

Choose a reason for hiding this comment

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

ありがとうございます。
そうですね。確かに変数名に情報が重なっている気がしますね。
なかなか自分で書いていると気づきにくいです。

result = None
while node_with_depth:
node, depth = node_with_depth.pop()
if not node:
continue
if self.is_leaf(node):
if reached_leaf_so_far and depth >= result:
continue
result = depth
reached_leaf_so_far = True
Comment on lines +197 to +201

Choose a reason for hiding this comment

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

if self.is_leaf(node) and (result is None or depth < result):
    result = depth

Copy link
Owner Author

Choose a reason for hiding this comment

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

それも考えたんですけど、Noneと整数を同じ変数にするのがどうかなと思ったんですよね(考えすぎですかね)

Choose a reason for hiding this comment

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

192行目でresult = Noneとなっていますが…

Copy link
Owner Author

@nittoco nittoco Nov 29, 2024

Choose a reason for hiding this comment

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

すみません、書いた時のことを思い出すとそういう判断基準ではなかったですね。
is Noneとただ書くより、別変数で定義した方が意図がわかりやすいと書いたときは思ってた気がします。

node_with_depth.append((node.right, depth + 1))
node_with_depth.append((node.left, depth + 1))
return result
```

## Step3

- BFSが自然かなあ

```python

class Solution:
def is_leaf(self, node: [TreeNode]) -> bool:
return not node.left and not node.right

def minDepth(self, root: Optional[TreeNode]) -> int:
if not root:
return 0
current_level_nodes = [root]
depth = 0
while True:
depth += 1
next_level_nodes = []
for node in current_level_nodes:
if not node:
continue
if self.is_leaf(node):
return depth
next_level_nodes.append(node.left)

Choose a reason for hiding this comment

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

自分はnode.leftがNoneかどうかはここでチェックしたいです。まあ好みかもしれないです。

next_level_nodes.append(node.right)
current_level_nodes = next_level_nodes
```