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
122 changes: 122 additions & 0 deletions 104_MaximumDepthOfBinaryTree/solution.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
# 問題
https://leetcode.com/problems/maximum-depth-of-binary-tree

二分木 `root` が与えられる。その**最大の深さ**を返す。最大の深さとは、根ノードから最も遠い葉ノードまでの経路上にあるノード数。

- 例1: `root = [3,9,20,null,null,15,7]` → `3`
- 例2: `root = [1,null,2]` → `2`
- 例3: `root = []` → `0`(空の木)
- 制約: ノード数は `[0, 10^4]` の範囲、`-100 <= Node.val <= 100`

# 前提
- 答えを見ずに考えて、5分考えて分からなかったら答えを見てください。答えを見て理解したと思ったら、答えを隠して書いてください。筆が進まず5分迷ったら答えを見てください。そして、見ちゃったら一回全部消してやり直しです。答えを送信して、正解になったら、まずは一段階目です。
- 次にコードを読みやすくするようにできるだけ整えましょう。これで動くコードになったら二段階目です。
- そしたらまた全部消しましょう。今度は、時間を測りながら、もう一回、書きましょう。書いてアクセプトされたら文字を消してもう一回書きましょう。これを10分以内に一回もエラーを出さずに書ける状態になるまで続けてください。3回続けてそれができたらその問題はひとまず丸です。
---

# 見積もり
- 入力サイズ: 0 <= ノード数 <= 10^4
- 時間計算量: O(N)(全ノードをちょうど1回ずつ処理)
- 実行時間の見積もり: 10^4 / 10^8 = 10^-4 s = 0.1ms

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

10^8がGoの実行速度であることも明示したほうが良いと思います。

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.

たしかに.
何度も書いてますが,書かなくても自明と思っているのは自分だけですね.

ありがとうございます

- 空間計算量: O(H)(H = 木の高さ)
- 平衡木なら H = log N、最悪(連結リスト状)なら H = N で O(N)
- 必要メモリの見積もり: 最悪 10^4 段の再帰スタック。1フレームの引数がint,pointer,pointerなので,24byte,そしてreturn-addressがあるはずなので,32byte.
32byte * 10^4 = 320kb


# 1回目
```go
/**
* Definition for a binary tree node.
* type TreeNode struct {
* Val int
* Left *TreeNode
* Right *TreeNode
* }
*/


func maxDepth(root *TreeNode) int {
maxDepth := 0
maxDepth = countDepth(root)
return maxDepth
}

func countDepth(node *TreeNode) int {
if node == nil {
return 0
}
if node.Left == nil && node.Right == nil {
return 1
}

leftDepth := 0
rightDepth := 0
if node.Left != nil {
leftDepth = countDepth(node.Left)
}
if node.Right != nil {
rightDepth = countDepth(node.Right)
}
return max(leftDepth, rightDepth) + 1
}
```
- treeに関する知識がなく,なんとなく解いた.
- なんとなく解いたから,コードが整理されていないと感じる.
- あと問題の図と,exampleのinputと,go言語で与えられるinputの`*TreeNode`が違うもの渡されている感じがして,すこし戸惑った

# 2回目
```go
func maxDepth(root *TreeNode) int {
return countDepth(root)
}

func countDepth(node *TreeNode) int {
if node == nil {
return 0
}
if node.Left == nil && node.Right == nil {
return 1
}

return max(countDepth(node.Left), countDepth(node.Right)) + 1
}
```
- 短くはなったが,まだ自然には感じられない

@h-masder h-masder Jun 23, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

https://docs.google.com/document/d/11HV35ADPo9QxJOpJQ24FcZvtvioli770WWdZZDaLOfg/edit?tab=t.0
再帰は、この辺りを見るとよいと思います。

それはそれとして、言語化してみるとこんな感じです。
・木構造の各ノードには人が立っており、ルートがボス、その下に部下、さらにその下に部下の部下、、、と続きます。この解答では、末端は葉ノードではなく、葉のさらに下にぶら下がっている nil です(nilがぶら下がっているいうのはあくまでイメージです)。

  1. nilは上司に「深さ0です」と報告。
  2. 葉は上司に「深さ1です」と報告。
  3. それ以外は、左右両方の部下に「最大深さを調べて」と依頼。返ってきた値を比べ、 深い方に1を足して上司に報告。

というイメージです。

@h-masder h-masder Jun 23, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

この解答では、で末端は葉ノードではなく、葉のさらに下にいる nil です。

一方で最初の解答は、末端は葉ノードな感じがしました。

  1. 葉は上司に「深さ1です」と報告。
  2. それ以外は、左右両方の部下がnilでないときは「最大深さを調べて」と依頼。返ってきた値を比べ、 深い方に1を足して上司に報告。
    というイメージです

そう考えたとき、最初のコードにおけるnilの条件分岐は何かというと、これはroot=nilに対する処理ですね。ですので、最初のコードは以下のように書くほうが自然な気がします。

func maxDepth(root *TreeNode) int {
    if root == nil {
        return 0
    }
    maxDepth := 0
    maxDepth = countDepth(root)
    return maxDepth
}

func countDepth(node *TreeNode) int {
    if node.Left == nil && node.Right == nil {
        return 1
    }
    leftDepth := 0
    rightDepth := 0
    if node.Left != nil {
        leftDepth = countDepth(node.Left)
    }
    if node.Right != nil {
        rightDepth = countDepth(node.Right)
    }
    return max(leftDepth, rightDepth) + 1
}

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.

なるほど,root以外は,すでにnilチェックを呼び出し前に行っているのでたしかにそれも通りますね.

このへんが統一されていないのは,たしかに明確にイメージがついてなかったから,かもしれません.

- 日本語にすると
- nilが渡されたら0にする
- nilではないが,次のリーフ(LeftとRight)がどちらもnilなら1として返す
- どちらかのリーフがあるとき,両方を調べて,大きい方と現階層の分(1)を足し合わせて返す
- うーむ,多少わかるようなわからんような.まだ頭に馴染まない.
- 典型コメント https://docs.google.com/document/d/11HV35ADPo9QxJOpJQ24FcZvtvioli770WWdZZDaLOfg/edit?tab=t.0
- pythonには `nonlocal` というのがあるのね. ついでに `global` もあるらしい.
- https://docs.python.org/3.14/reference/simple_stmts.html#the-nonlocal-statement
- リンク先に飛ぶとgo, backとかが出てくるコードがあるが,直接は関係なさそうな概念が定義されていて読みづらかった
- DFSはstackを使ってループにすることができるのはその通り.自分でもやるかな.これは入力の制限が,緩和されたときにスタックオーバーフローにならないため,くらいでとらえるとよさそう
- 他の人のコード
- https://github.com/komdoroid/arai60/pull/17
- https://github.com/hiroki-horiguchi-dev/leetcode/pull/21
- https://github.com/nicah4o/arai60/pull/20
- ボトムアップ,トップダウンの話がわからないなと思ったので,LLMに聞いてみた.引数でdepthを渡して,一番下にきたときにすでにdepthが求まっているか,あるいは一番下から上に戻っていく過程でカウントされるかの違いと理解した.自分のstep1/step2は,ボトムアップ.
- https://github.com/Kazuuuuuuu-u/arai60/pull/22
- https://github.com/jjysogfy/arai60-202603/pull/10
- https://github.com/h-masder/Arai60/pull/22
- 幅優先探索の再帰は書いたことないな.と思った.ただ,queueでやっていることをlayer単位で次の再帰に渡すだけぽい.
- https://github.com/rimokem/arai60/pull/21
- `幅優先探索はmax_depthを保持しなくても` というのはDFSでもできるはずだな
- こっちはqueueを利用した,BFSのパターン
- 自分の2回目のコード,LeftとRightのnilチェックなくしても動くということがわかった

# 3回目
```go
func maxDepth(root *TreeNode) int {
if node == nil {
return 0
}

return max(maxDepth(node.Left), maxDepth(node.Right)) + 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.

node, node.Left, node.Rightはそれぞれ、root, root.Left, root.Rightですね。

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.

ああ,これは動作したコードをちゃんとコピペせず,適当に再現してしまったためですね.
大変失礼しました.

```
- 元の関数とシグネチャが一緒なので,実はヘルパは不要だった.
- めちゃめちゃ短くなった
- これを3回繰り返した