Skip to content
Open
Show file tree
Hide file tree
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
161 changes: 161 additions & 0 deletions leetcode/arai60/memo.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
# 104. Maximum Depth of Binary Tree
* 問題: https://leetcode.com/problems/maximum-depth-of-binary-tree/
* 言語: Python

# Step1
* 二分木の最大の深さ(高さ)を求める
* 幅優先探索(BFS)で深さを記録していく

## すべてのテストケースを通過しないコード
```py
class Solution:
def maxDepth(self, root: Optional[TreeNode]) -> int:
if root is None:
return 0

node = root
visited_nodes = deque([node])

Choose a reason for hiding this comment

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

この辺りの変数名にもう少し時間を使ってもよいと思います。visited_nodesと書くと訪れたノードが全部入っているように感じますが、実際はpopしているので違和感を感じました。

candidate_tree_depth = []
tree_depth = 1

while visited_nodes:
node = visited_nodes.popleft()
if node.left:
visited_nodes.append(node.left)
tree_depth += 1
if node.right:
visited_nodes.append(node.right)
tree_depth += 1
if not node.left and not node.right:
candidate_tree_depth.append(tree_depth)
tree_depth = 1

return max(candidate_tree_depth)
```
* 完全二分木の場合はうまく動くが、そうでない場合は誤答を返す
* 40分ほど経っていたので正答を見る

## 正答
```py
class Solution:
def maxDepth(self, root: Optional[TreeNode]) -> int:
if not root:
return 0

visited_nodes = deque([root])
tree_depth = 0

while visited_nodes:
tree_depth += 1

for _ in range(len(visited_nodes)):
Copy link

Choose a reason for hiding this comment

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

直前の層のノード数だけループを回し、 1 層ずつ処理するという書き方は、あまり直感的ではないように思います。 deque を 2 つ用意して入れ替えるか、 deque の要素に高さも一緒に格納してあげたほうが直感的に感じます。

Copy link
Owner Author

Choose a reason for hiding this comment

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

以下のように修正してみました。

  • 2つのdequeを使う方法(BFS)
class Solution:
    def maxDepth(self, root: Optional[TreeNode]) -> int:
        if not root:
            return 0
        
        current_level = deque([root])
        next_level = deque()
        tree_depth = 0
        
        while current_level:
            tree_depth += 1
            # 現在の階層のノードを処理
            while current_level:
                node = current_level.popleft()
                if node.left:
                    next_level.append(node.left)
                if node.right:
                    next_level.append(node.right)
            
            # 次の階層に移動(dequeを入れ替え)
            current_level, next_level = next_level, current_level
        
        return tree_depth
  • dequeに高さの情報を入れる方法(BFS)
class Solution:
    def maxDepth(self, root: Optional[TreeNode]) -> int:
        if not root:
            return 0
        
        # (ノード, 深さ)のタプルをキューに格納
        node_and_depth = deque([(root, 1)])
        max_depth = 0
        
        while node_and_depth:
            node, depth = node_and_depth.popleft()
            max_depth = max(max_depth, depth)
            
            if node.left:
                node_and_depth.append((node.left, depth + 1))
            if node.right:
                node_and_depth.append((node.right, depth + 1))
        
        return max_depth

node = visited_nodes.popleft()
if node.left:
visited_nodes.append(node.left)
if node.right:
visited_nodes.append(node.right)

return tree_depth
```
* 同じくBFSを使った方法
* 木のそれぞれの高さ(level)ごとに、その次の同じ高さにあるノードを追加していく

# Step2
## 他人のコードを読む
* 典型コメント: https://docs.google.com/document/d/11HV35ADPo9QxJOpJQ24FcZvtvioli770WWdZZDaLOfg/edit?tab=t.0#heading=h.27jfjzhov3la
* https://github.com/sakupan102/arai60-practice/pull/21
- Python
- 深さ優先探索(DFS)を使った方法
- 再帰によるDFS・下から走査するボトムアップ方式
```py
class Solution:
def maxDepth(self, root: Optional[TreeNode]) -> int:
if not root:
return 0
right_depth = self.maxDepth(root.right)
left_depth = self.maxDepth(root.left)
return max(right_depth, left_depth) + 1
```
- スタックによるDFS・上から配るトップダウン方式
```py
class Solution:
def maxDepth(self, root: Optional[TreeNode]) -> int:
if not root:
return 0
node_and_depth = [(root, 1)]
max_depth = 1
while node_and_depth:
node, depth = node_and_depth.pop()
max_depth = max(max_depth, depth)
if node.right:
node_and_depth.append((node.right, depth + 1))
if node.left:
node_and_depth.append((node.left, depth + 1))
return max_depth
```
- 再帰によるDFS・上から配るトップダウン方式
```py
class Solution:
def maxDepth(self, root: Optional[TreeNode]) -> int:
max_depth = 0
def dive_into_leaf(node, depth):

Choose a reason for hiding this comment

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

関数名から何が返ってくるか処理が想像できませんでした。update_max_depthとかの方が分かりやすいと思いました。

nonlocal max_depth
if not node:
return
max_depth = max(max_depth, depth)
dive_into_leaf(node.left, depth + 1)
dive_into_leaf(node.right, depth + 1)
dive_into_leaf(root, 1)
return max_depth
```
- スタックによるDFS・下から走査するボトムアップ方式
Copy link
Owner Author

Choose a reason for hiding this comment

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

同じスタックDFSかつボトムアップ走査でフラグを使わないパターン・辞書でノードごとの深さを管理してボトムアップにする方法もある

  • フラグを使わないパターン
class Solution:
    def maxDepth(self, root: Optional[TreeNode]) -> int:
        res_depth_ref = [None]
        # (node, ret_depth_ref, left_depth_ref, right_depth_ref)
        stack = [(root, res_depth_ref, [None], [None])] 
        while stack:
            node, ret_depth_ref, left_depth_ref, right_depth_ref = stack[-1]
            if not node:
                ret_depth_ref[0] = 0
                stack.pop()
                continue
            if left_depth_ref[0] is None:
                stack.append((node.left, left_depth_ref, [None], [None]))
                stack.append((node.right, right_depth_ref, [None], [None]))
                continue
            ret_depth_ref[0] = max(left_depth_ref[0], right_depth_ref[0]) + 1
            stack.pop()
        return res_depth_ref[0]
  • 辞書でノードごとの深さを管理するパターン
def maxDepth(self, root: Optional[TreeNode]) -> int:
    if not root:
        return 0
    
    stack = []
    depths = {}  # ノードごとの深さを記録
    node = root
    
    while stack or node:
        if node:
            stack.append(node)
            node = node.left
        else:
            node = stack.pop()
            left_depth = depths.get(node.left, 0)
            right_depth = depths.get(node.right, 0)
            depths[node] = max(left_depth, right_depth) + 1
            node = node.right
    
    return depths[root]

```py
class Solution:
def maxDepth(self, root: Optional[TreeNode]) -> int:
if not root:
return 0
node_info = [("go", (root, None, [None], [None], [None]))] # (node, direction, depth, left_depth, right_depth)
while node_info:
go_back_flag, args = node_info.pop()
if go_back_flag == "go":
node, direction, depth_ref, left_depth_ref, right_depth_ref = args
if not node:
depth_ref[0] = 0
continue
node_info.append(("back", args))
node_info.append(("go", (node.left, "left", left_depth_ref,[None], [None])))
node_info.append(("go", (node.right, "right", right_depth_ref, [None], [None])))
if go_back_flag == "back":
node, direction, depth_ref, left_depth_ref, right_depth_ref = args
depth_ref[0] = max(left_depth_ref[0], right_depth_ref[0]) + 1
if not direction:
return depth_ref[0]
```
![](stack-visualization.svg)
- Claude 4 Opusで作成

# Step3
* スタックによるDFS・上から配るトップダウン方式
```py
class Solution:
def maxDepth(self, root: Optional[TreeNode]) -> int:
if not root:
return 0

node_and_depth = [(root, 1)]
max_depth = 0

while node_and_depth:
node, depth = node_and_depth.pop()
max_depth = max(max_depth, depth)
if node.right:
node_and_depth.append((node.right, depth + 1))
if node.left:
node_and_depth.append((node.left, depth + 1))

return max_depth
```
* 解答時間
- 1回目: 1:45
- 2回目: 2:08
- 3回目: 2:17
Loading