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
200 changes: 200 additions & 0 deletions maximum-depth-of-binary-tree/memo.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
## 問題

[Maximum Depth of Binary Tree - LeetCode](https://leetcode.com/problems/maximum-depth-of-binary-tree/description/)

- 入力
- `root`: ルートノード
- 木のノードの数は0以上10^4以下
- ノードの値は -100以上100以下
- 出力
- 木の深さ

## 解法

### 1. 再帰DFS

- 左右の部分木の深さを聞いて、大きい方に1を足して返すイメージ
- ノードが nil なら深さは 0
- nil でなければ左右の部分木の深さを求め、1を足す
- 大きい方の値を返す
- 木のノードの数を n とすると
- 時間計算量は O(n)
- 各ノードを訪れるので
- ノード数は10^4以下なので問題なさそう
- 空間計算量は O(n)
- スタックの深さ
- 最悪の場合は一直線

### 2. 反復DFS

- nilでない子ノードをスタックに積んでいく
- 左から見ていきたいので、右から積む
- そのノードの深さを一緒に管理する
- 計算量は同じ

### 3. BFS

- 木の深さごとにノードをキューに入れていく
- 計算量は同じ
- 空間計算量が最大になるのは子の数が最も多い深さのとき

## Step1

### 1.

```go
/**
* Definition for a binary tree node.
* type TreeNode struct {
* Val int
* Left *TreeNode
* Right *TreeNode
* }
*/
func maxDepth(root *TreeNode) int {
if root == nil {
return 0
}

leftDepth := maxDepth(root.Left)
rightDepth := maxDepth(root.Right)

return max(leftDepth, rightDepth) + 1
}
```

### 2.

```go
/**
* Definition for a binary tree node.
* type TreeNode struct {
* Val int
* Left *TreeNode
* Right *TreeNode
* }
*/
func maxDepth(root *TreeNode) int {
if root == nil {
return 0
}

var maxDepth int
nodesToVisit := []NodeWithDepth{
{node: root, depth: 1},
}

for len(nodesToVisit) > 0 {
topIndex := len(nodesToVisit) - 1
nodeWithDepth := nodesToVisit[topIndex]
// GC対象にするため参照を外す
nodesToVisit[topIndex].node = nil
Comment on lines +90 to +91
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

こういう小技もあるのですね。勉強になります。
ちなみに、ここで参照を外さなくても、この関数を抜けたらGCは走りますよね…?

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

これよく知らなかったのですが、ここの SliceTricks で推奨されていました。
https://go.dev/wiki/SliceTricks

NOTE If the type of the element is a pointer or a struct with pointer fields, which need to be garbage collected, the above implementations of Cut and Delete have a potential memory leak problem: some elements with values are still referenced by slice a’s underlying array, just not “visible” in the slice. Because the “deleted” value is referenced in the underlying array, the deleted value is still “reachable” during GC, even though the value cannot be referenced by your code. If the underlying array is long-lived, this represents a leak. The following code can fix this problem:

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.

コメントありがとうございます。

はい、 maxDepth から戻ったら nodesToVisit が参照されなくなるためGC対象となると思います。
手癖で書いていた側面もあるので、必要かどうか考えた上で nil 代入するかどうか考えるようにします。
生存期間が長くない、そもそもサイズ的に問題にならないなどの場合はなくてもいいと思いました。
(実務ではガイドラインに従います)

nodesToVisit = nodesToVisit[:topIndex]

maxDepth = max(nodeWithDepth.depth, maxDepth)

node := nodeWithDepth.node

if node.Right != nil {
nodesToVisit = append(nodesToVisit, NodeWithDepth{
node: node.Right,
depth: nodeWithDepth.depth + 1,
})
}
if node.Left != nil {
nodesToVisit = append(nodesToVisit, NodeWithDepth{
node: node.Left,
depth: nodeWithDepth.depth + 1,
})
}
}

return maxDepth
}

type NodeWithDepth struct {
node *TreeNode
depth int
}
```

### 3.

```go
/**
* Definition for a binary tree node.
* type TreeNode struct {
* Val int
* Left *TreeNode
* Right *TreeNode
* }
*/
func maxDepth(root *TreeNode) int {
if root == nil {
return 0
}

var maxDepth int
nodes := []*TreeNode{root}

for len(nodes) > 0 {
var nextNodes []*TreeNode
maxDepth++

for _, node := range nodes {
if node.Left != nil {
nextNodes = append(nextNodes, node.Left)
}
if node.Right != nil {
nextNodes = append(nextNodes, node.Right)
}
}

nodes = nextNodes
}

return maxDepth
}
```

## Step2

- 再帰版がいちばんしっくりくる

```go
func maxDepth(root *TreeNode) int {
if root == nil {
return 0
}

leftDepth := maxDepth(root.Left)
rightDepth := maxDepth(root.Right)

return max(leftDepth, rightDepth) + 1
}
```

### レビューを依頼する方のPR

- [104. Maximum Depth of Binary Tree by TakayaShirai · Pull Request #21 · TakayaShirai/leetcode_practice](https://github.com/TakayaShirai/leetcode_practice/pull/21)
- [104_maximum_depth_of_binary_tree by Hiroto-Iizuka · Pull Request #21 · Hiroto-Iizuka/coding_practice](https://github.com/Hiroto-Iizuka/coding_practice/pull/21)
- 左右の部分木の深さを変数にしないほうを好む人もいる
- 変数を使う箇所が1箇所しかない場合、変数を使わないようにするというレビューになるケースはある
- `return max(maxDepth(root.Left), maxDepth(root.Right)) + 1`
- 読みにくいと感じたので分けたけど、そこまで読みにくいというわけではない
- そういうレビューが付いたらまとめてもいい程度
- [104. Maximum Depth of Binary Tree by dxxsxsxkx · Pull Request #21 · dxxsxsxkx/leetcode](https://github.com/dxxsxsxkx/leetcode/pull/21)
- [104. Maximum Depth of Binary Tree by mamo3gr · Pull Request #21 · mamo3gr/arai60](https://github.com/mamo3gr/arai60/pull/21)
- [104. Maximum Depth of Binary Tree by Yuto729 · Pull Request #26 · Yuto729/LeetCode_arai60](https://github.com/Yuto729/LeetCode_arai60/pull/26)

## Step3

```go
func maxDepth(root *TreeNode) int {
if root == nil {
return 0
}

return max(maxDepth(root.Left), maxDepth(root.Right)) + 1
}
```