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
139 changes: 139 additions & 0 deletions 112. Path Sum.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
### Step1

- 本当は部下に仕事を任せてreturnしてもらう実装が好きだが、値の候補がたくさんreturnされてしまうのがどうかと思い、backtrackingで書いた

Choose a reason for hiding this comment

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

答えを一つ見つけたら、そこで打ち切った方が良いでしょう。

- nonlocalでもいいが、mutableなリストで書いた

Choose a reason for hiding this comment

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

nonlocalの方が意図が明確だと思います。

- 最初、子供がいるかは判定せずに、if not node: の時にtargetと照合しようとして失敗。片方しか子供がいない時にも判定されてしまう。
- このbacktrackingのアルゴリズムをリファクタリンぎしたやつをStep2の最後に記載

```python

class Solution:
def hasPathSum(self, root: Optional[TreeNode], targetSum: int) -> bool:
def search_target_sum(sum_so_far, node: Optional[TreeNode]):
sum_so_far += node.val
if not (node.left or node.right):

Choose a reason for hiding this comment

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

not node.left and not node.rightの方がわかりやすく感じます。

if sum_so_far == targetSum:
has_target[0] = True

Choose a reason for hiding this comment

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

ここでreturnしたら良いでしょう。

if node.left:
search_target_sum(sum_so_far, node.left)
if node.right:
search_target_sum(sum_so_far, node.right)
sum_so_far -= node.val

Choose a reason for hiding this comment

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

sum_so_far -= node.valこれ必要でしょうか。

Choose a reason for hiding this comment

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

不要でしょう。


if not root:
return False
has_target = [False]
search_target_sum(0, root)
return has_target[0]
```

### Step2
- [参考]
- (https://github.com/TORUS0818/leetcode/pull/27/files)
- (https://github.com/kazukiii/leetcode/pull/26/files)
- 再帰をするなら、単にFalse, Trueを返す関数を作ればよかった。(なぜか変にsumを全部返さなきゃとか思ってしまった)

Choose a reason for hiding this comment

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

Step 1でも、sumは返していないようです。

- 下でこれをさらにリファクタしてます

```python

class Solution:
def hasPathSum(self, root: Optional[TreeNode], targetSum: int) -> bool:
def search_target_sum(sum_so_far, node: Optional[TreeNode]):
sum_so_far += node.val
if not (node.left or node.right):
return sum_so_far == targetSum
if node.left and search_target_sum(sum_so_far, node.left):
return True
if node.right and search_target_sum(sum_so_far, node.right):
return True
return False

if not root:

Choose a reason for hiding this comment

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

search_target_sumの中に入れたほうが、コードの重複が減りますね。

return False
return search_target_sum(0, root)
```

- if not node: でreturn Falseすればいいのか〜
- なんか、葉であるのとnodeがそもそもないのを両方関数内で判定すると言う発想がなかった
- こう言うのは最後のないところまで探索するのがいいね

```python

class Solution:
def hasPathSum(self, root: Optional[TreeNode], targetSum: int) -> bool:
def search_target_sum(sum_so_far, node: Optional[TreeNode]):
if not node:
return False
sum_so_far += node.val
if not (node.left or node.right):
return sum_so_far == targetSum
return search_target_sum(sum_so_far, node.left) or search_target_sum(sum_so_far, node.right)

return search_target_sum(0, root)

Choose a reason for hiding this comment

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

ここにtargetSumを渡して、関数内で引いていく方式にすればスコープ外の変数を使わなくて済むのでそれも手だと思います

付随した質問: よくPythonコードでスコープ外の変数を使っている人を見かけるのですが、これってPythonでは一般的なんでしょうか?
個人的には変数のスコープは明確であってほしいと思ってしまいます

Copy link
Owner Author

Choose a reason for hiding this comment

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

「スコープ外の変数を使う」とは今回の場合、69行目において、targetSumが外側にある、ということですよね。それをしたくない理由は、挙動が追いにくいからでしょうか。(言われれば確かにそう感じる理由もわかる気がします)
自分は大規模コードの開発経験がないのでいまいち一般的な感覚がわからないのですが、どうなんでしょう…?

Choose a reason for hiding this comment

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

特に問題ないと思います。

```

- 手でやるならbacktrackingのように足し引きしながら計算する気がするので、Step1を綺麗にしたやつを書いてみる
- [True]みたいに要素数1のリストを持たせるのも微妙かと思い、path_sumsをsetで持たせることにした

Choose a reason for hiding this comment

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

全ての根から葉までの和を保持しておくのはやや贅沢な気がします。
少し設定を変えてtargetSumに合致するようなパスがいくつあるか数える時、とかならこういう実装も良さそうですね。

- returnに()をつけるか迷った(つけないと分かりにくいか、不要か)
Copy link

Choose a reason for hiding this comment

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

個人的には不要かなと思いました。分かりやすくなっていない気がするので。

Choose a reason for hiding this comment

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

不要でしょう。


```python

class Solution:
def hasPathSum(self, root: Optional[TreeNode], targetSum: int) -> bool:
def caluculate_path_sums(sum_so_far, node: Optional[TreeNode], path_sums: set):

Choose a reason for hiding this comment

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

path_sumsを引数として渡す必要はなさそうです。

if not node:
return
sum_so_far += node.val
if not node.left and not node.right:
path_sums.add(sum_so_far)
caluculate_path_sums(sum_so_far, node.left, path_sums)
caluculate_path_sums(sum_so_far, node.right, path_sums)
sum_so_far -= node.val

Choose a reason for hiding this comment

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

sum_so_far -= node.valこれ必要でしょうか。

Copy link
Owner Author

Choose a reason for hiding this comment

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

手作業でbacktrackingするなら戻る時に引くなあと思って入れたんですが、よく考えたら引いた後に関数内でなんか操作している訳ではないのでいらなかったですね。
Pythonでは変数はその関数内でローカルなので、関数抜けたら戻りますし。
ありがとうございます。

Copy link

Choose a reason for hiding this comment

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

sum_so_farをcall stackに積まないで、nonlocalとして扱う書き方もありますね。その場合は、一歩戻るときに sum_so_far -= node.val を入れてあげないとですね。

Copy link
Owner Author

Choose a reason for hiding this comment

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

確かに、自分が頭でシミュレーションしてたのをコードに書くならそれになりますね。
スッキリしました、ありがとうございます。


path_sums = set()
caluculate_path_sums(0, root, path_sums)
return (targetSum in path_sums)
```

## Step3(レビュー反映復習)
```python

class Solution:

Choose a reason for hiding this comment

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

l62の方が良かったと思います。

Copy link
Owner Author

Choose a reason for hiding this comment

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

確かに自分もどれを選ぶかを選ぶならl62のやつで書くかもです。(今回はStep1の書き直しという感じで書いてみました)
やり直しとかも見てくださりありがとうございます🙇

def hasPathSum(self, root: Optional[TreeNode], targetSum: int) -> bool:
def search_target_sum(sum_so_far, node: Optional[TreeNode]):
nonlocal has_target
if not node:
return
Copy link

Choose a reason for hiding this comment

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

if has_target: return
で枝狩りしておくと、ツリー全体を舐めなくて済みますね。

Copy link
Owner Author

Choose a reason for hiding this comment

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

あ、書いた時頭が混乱していましたが、これはL111のreturnとは別で、探索をかなりスルーできますね。(逆にL111はその後すぐNoneのノードが来るのでreturnする意味そんなにないですね)
ありがとうございます

sum_so_far += node.val
if not node.left and not node.right:
if sum_so_far == targetSum:
has_target = True
return
search_target_sum(sum_so_far, node.left)
search_target_sum(sum_so_far, node.right)

has_target = False
search_target_sum(0, root)
return has_target
```

- こうは書かないけど一応

```python
python
class Solution:
def hasPathSum(self, root: Optional[TreeNode], targetSum: int) -> bool:
def calculate_path_sums(node, sum_so_far):
if not node:
return
sum_so_far += node.val
if not node.left and not node.right:
path_sums.add(sum_so_far)
calculate_path_sums(node.left, sum_so_far)
calculate_path_sums(node.right, sum_so_far)

path_sums = set()
calculate_path_sums(root, 0)
return targetSum in path_sums

```