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
84 changes: 84 additions & 0 deletions 0112-path-sum/0112-path-sum.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
## Step 1

- 問題文
- 二分木の根`root`と整数`targetSum`が与えられる。
- 根から葉までの経路で、valueを足し上げると`targetSum`に等しくなるような経路があれば`True`を返す。
- なお葉とは、子のないノードである。
- 制約:
- The number of nodes in the tree is in the range [0, 5000].
- -1000 <= Node.val <= 1000
- -1000 <= targetSum <= 1000

### 実装1

- アルゴリズムの選択
- BFS/DFSであらゆる経路について素直に葉まで足し合わせ、葉でtargetSumになればreturn Trueすれば良さそう。

Choose a reason for hiding this comment

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

もし葉でtargetSumになるような経路を返却するとしたらどうでしょうか?
設問としてはTrue/Falseなのですが、単純にTrue/Falseを得るよりはpathを実際に知るほうが意味があるのかなと思ったので...自分が面接だったら質問しそうです。

Copy link
Owner Author

Choose a reason for hiding this comment

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

follow-upありがとうございます。

そうですね、通ったノードのその時点の和を記録するsetまたは辞書を持つようにし、ゴールの葉からスタートに向かって再びBFS/DFSするのが(空間)計算量が節約できて良さそうですね。

さらに、そのようなpathの総数/pathを全部知りたい場合は、1回目の前向きの探索をearly returnなしで完遂し、和の記録も通った回数を辞書で記録しておくのが良さそうに思います。

- node.valは正も負も取りうることから、足し合わせていく和は単調に変動しないので、targetSum以上(以下)になった時点で打ち切りする、などの枝刈りはできないと判断。
- 実装
- whileループで探索する。
- キューに入れる前にnodeがNoneでないかを弾くと見通しが良いので、そうする。
- 最初にroot is Noneを弾いておく必要がある。今回は忘れずにできた。
- 計算量
- Time: O(V + E) = O(V)
- Space: O(width) = O(V)

```python3
from typing import Optional
from collections import deque


# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def hasPathSum(self, root: Optional[TreeNode], targetSum: int) -> bool:
if root is None:
return False

frontiers = deque()

Choose a reason for hiding this comment

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

私は英語に詳しくないのですが、なんだか開拓という感じがしてロマンを感じるなと思いました。探索だと結構使われることもあるのですね。
chromiumのコードにもfrontiersは出てきますね。

Copy link
Owner Author

Choose a reason for hiding this comment

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

その感覚わかります、"queue"が標準モジュールにあるので、語感が好きなfrontiersを第一候補にしがちです。

なるほど、そうなんですね。

frontiers.append((root, 0))
while frontiers:
node, total = frontiers.popleft()
total += node.val
if node.left is None and node.right is None:
if total == targetSum:
return True
continue
if node.left is not None:
frontiers.append((node.left, total))
if node.right is not None:
frontiers.append((node.right, total))
return False
```

ここまで5分。

## Step 2

- GPT-5によるレビュー
- > 現在は取り出し後に total += node.val。読みやすさを優先するなら、キューには「次に検査する時点での合計」を積むか、
- 積む時点で加算する場合、足し算の位置がコード中で分散してしまうのが好みでない。
- > あるいは subtractive 形式で「残り」を積むと、葉の判定が1行で済みます。
- これは思いつかなかった。しかし、差に注目するのは不必要にロジックを複雑化させているように感じてしまう。
Copy link

@sasanquaneuf sasanquaneuf Oct 23, 2025

Choose a reason for hiding this comment

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

再帰の場合は引数のtargetSumを減らして呼ぶのが自然なので発想として出てきますが、whileで書く場合はスタックに積むか親の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.

ありがとうございます。

再帰の場合は引数のtargetSumを減らして呼ぶのが自然なので発想として出てきますが

これは気づきませんでした、その通りですね。

whileで書く場合はスタックに積むか親のnodeに戻るときに足して戻すか、というのが必要になって複雑な印象になりますね。

そうなんですよね。個人的に最近はなるべくiterativeに書くようにしているのもあり。

- [コメント集](https://docs.google.com/document/d/11HV35ADPo9QxJOpJQ24FcZvtvioli770WWdZZDaLOfg/edit?tab=t.0#heading=h.ed3x3pkyeqkp)
- https://discord.com/channels/1084280443945353267/1225849404037009609/1258455843226255361
- > これ、引き算先にしちゃって、
```python3
if not node.left and not node.right:
return rest == 0
```
> のほうが素直ではないでしょうか
- 最初から同じ感覚で書けたのでよかった。
- 葉を先に例外的に処理し、その下で通常の子を持つノードを処理する。
- https://discord.com/channels/1084280443945353267/1228700203327164487/1229111777946505216
- > いわゆるスタックを使って書き直せますか?スタックは ArrayDeque でしたっけ。Stack は、もう使わないんでしたっけ。
- pythonなら.popleft()を.pop()にするだけなので簡単。
- javaでArrayDequeが推奨されていることは自分も以前のレビューで言及したことがある。
- https://discordapp.com/channels/1084280443945353267/1402285228722229258/1426823682239631420

## Step 3

[実装1](#実装1)