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
32 changes: 32 additions & 0 deletions problems/112.path-sum/memo.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
## step1
- 幅優先探索みたいなのしたらできそう
- Node.valにマイナスが入る場合があるので超えたら終わりみたいなことはできなさそう
- 元のNodeのvalを書き換えるとまずいので、累積の値を管理するものを作る必要がありそう
- 途中で切ってもいいのかと思っていたが最後の子まで行かないとだめだった

## step2
- 時間計算量、空間計算量はO(N)だと思う
- 他の人のコードを見る前に他にやり方がないか考える
- 取りたくない選択肢だが元のNodeのvalを累積値に書き換えるorTreeNodeに新しいメンバ変数を持たせる
- やっていることは同じだが、再帰でも同様のことができそう?
- 一旦書いてみる
- root is NoneでtargetSum == 0を最終的な条件にしようとしたが、初期条件のrootがNoneでtargetSumが0の場合はFalseにしたいのでhelperを作った
- 中々うまくかけず、試行錯誤して最終的にChatGPTに聞いてしまった
- 他の人のコードを見る
- https://github.com/05ryt31/leetcode/pull/16/files
- 自分としては少し気をつけたところだが、Trueが見つかった場合に打ち切ってくれるかどうかの観点はあった方が良いと思った
- https://github.com/mamo3gr/arai60/pull/24
- https://github.com/mamo3gr/arai60/pull/24/files#r2678467960
- 再帰とキューの比較
- 書いている際に条件は確認したがスタックにどのくらい積まれるかは毎回忘れずにmemoでも言及したいと思った
- `is_leaf`と変数にするのは情報が増えていいと思った
- https://github.com/naoto-iwase/leetcode/pull/29
- `もし葉でtargetSumになるような経路を返却するとしたらどうでしょうか?設問としてはTrue/Falseなのですが、単純にTrue/Falseを得るよりはpathを実際に知るほうが意味があるのかなと思ったので...自分が面接だったら質問しそうです。`
follow-upの質問があったので考えると、累積を加えて渡すのと同様に通ってきたNodeのlistをtupleに足して管理するようにしそうだが、毎回Listをcopyしないと壊れそうで結構重い処理になりそう
`そうですね、通ったノードのその時点の和を記録するsetまたは辞書を持つようにし、ゴールの葉からスタートに向かって再びBFS/DFSするのが(空間)計算量が節約できて良さそうですね。さらに、そのようなpathの総数/pathを全部知りたい場合は、1回目の前向きの探索をearly returnなしで完遂し、和の記録も通った回数を辞書で記録しておくのが良さそうに思います。`この解法は思いつかなかったが和を記録したとして親を辿る方法が和が一致する物の中から子を見て一致したら親のNodeと判定しないといけないような気がするので大変だと思った
- 自分で再帰でも書けそうだと思った部分は進歩だと思うが、やはりすんなり書けないのでもっと練習が必要だと思った

## step3
- `is_leaf`を使って書くように修正した
- step1と違いやるべきことが明確に頭の中にあったので5分ほどでかけた

41 changes: 41 additions & 0 deletions problems/112.path-sum/step1.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#
# @lc app=leetcode id=112 lang=python3
#
# [112] Path Sum
#

# @lc code=start
# 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
import collections


class Solution:
def hasPathSum(self, root: Optional[TreeNode], targetSum: int) -> bool:
if root is None:
return False
accumulate = root.val
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

動詞の原型を変数名に付けると、関数名のように感じられます。 accumulated はいかがでしょうか?

frontier = collections.deque()
frontier.append((root, accumulate))

while frontier:
frontier_node, accumulate = frontier.popleft()
if frontier_node.left is None and frontier_node.right is None:
if accumulate == targetSum:
return True
if frontier_node.left is not None:
next_accumulate = accumulate + frontier_node.left.val
frontier.append((frontier_node.left, next_accumulate))

if frontier_node.right is not None:
next_accumulate = accumulate + frontier_node.right.val
frontier.append((frontier_node.right, next_accumulate))

return False


# @lc code=end
27 changes: 27 additions & 0 deletions problems/112.path-sum/step2.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#
# @lc app=leetcode id=112 lang=python3
#
# [112] Path Sum
#

# @lc code=start
# 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

if root.left is None and root.right is None:
return targetSum == root.val

return self.hasPathSum(root.left, targetSum - root.val) or self.hasPathSum(
root.right, targetSum - root.val
)
Comment on lines +22 to +24
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

ここ、左右対称な話なのに、フォーマッタに整形させると非対称みたいに見えちゃうんですよね…。

Suggested change
return self.hasPathSum(root.left, targetSum - root.val) or self.hasPathSum(
root.right, targetSum - root.val
)
left_has_path_sum = self.hasPathSum(root.left, targetSum - root.val)
right_has_path_sum = self.hasPathSum(root.right, targetSum - root.val)
return left_has_path_sum or right_has_path_sum

のような選択肢も幅として持っておくといいのかもな、と思いました。



# @lc code=end
43 changes: 43 additions & 0 deletions problems/112.path-sum/step3.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#
# @lc app=leetcode id=112 lang=python3
#
# [112] Path Sum
#

# @lc code=start
# 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
import collections


class Solution:
def hasPathSum(self, root: Optional[TreeNode], targetSum: int) -> bool:
if root is None:
return False
total_val = root.val

frontier = collections.deque()
frontier.append((root, total_val))

while frontier:
frontier_node, val = frontier.popleft()
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

frontier から pop したので、これ以降は frontier_ が無くても大丈夫そうです。

Suggested change
frontier_node, val = frontier.popleft()
node, val = frontier.popleft()


is_leaf = frontier_node.left is None and frontier_node.right is None

if is_leaf and val == targetSum:
return True

if frontier_node.left:
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

冒頭で root is None 判定しているなら、ここでも is not None と判定するほうが、一貫性があって良いと思います。
反対に冒頭を not root でも良いです。

frontier.append((frontier_node.left, val + frontier_node.left.val))

if frontier_node.right:
frontier.append((frontier_node.right, val + frontier_node.right.val))

return False


# @lc code=end