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
188 changes: 188 additions & 0 deletions 617. Merge Two Trees.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
### Step1

- nodeがNoneの時も合わせて処理を一般化するために関数を作ったけど、ここまでする必要はなかったかも
- returnする値が多くて見にくい気も
- このときはdeepcopyしてrootをreturnする選択肢に気づいていない
- merged_rootとmerged_nodeで変数名を迷う(再帰をしていく中でこの変数はrootでなくなるので)

Choose a reason for hiding this comment

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

頭にはあると思うのですが、再帰上限について軽く呟いておくと良いのかなと思います。

Copy link
Owner Author

Choose a reason for hiding this comment

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

これ確かに自分抜けがちです。ありがとうございます

```python

class Solution:
def mergeTrees(self, root1: Optional[TreeNode], root2: Optional[TreeNode]) -> Optional[TreeNode]:
# node.valと、node.leftと、node.rightについて、nodeがNoneの場合でも共通して処理できるようにする
def process_node_when_not_existing(node):

Choose a reason for hiding this comment

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

Noneをチェックするだけなので、processという動詞と合っていないように思いました。

node_val = node.val if node else 0
node_left = node.left if node else None
node_right = node.right if node else None
return node_val, node_left, node_right
Comment on lines +14 to +17

Choose a reason for hiding this comment

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

何度もinline ifを使っているのが気になります。

if not node:
  return 0, None, None
return node.val, node.left, node.right

Copy link
Owner Author

Choose a reason for hiding this comment

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

ありがとうございます。確かにそれでいいですね


if not root1 and not root2:
return None
root1_val, root1_left, root1_right = process_node_when_not_existing(root1)
root2_val, root2_left, root2_right = process_node_when_not_existing(root2)
merged_node = TreeNode(root1_val + root2_val)
merged_node.left = self.mergeTrees(root1_left, root2_left)
merged_node.right = self.mergeTrees(root1_right, root2_right)
return merged_node
```

### Step2

- 参考資料

https://github.com/TORUS0818/leetcode/pull/25

- どちらかなくなったらもう一方をそのまま返せば良いと言うことになぜか気づかなかった…

Choose a reason for hiding this comment

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

最初からこれに気づくのは難しいですね。

- 非破壊的にしたければdeepcopy()か

Choose a reason for hiding this comment

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

インプットのツリーは参照されているものの、破壊はされていないです。https://en.wikipedia.org/wiki/Copy-on-write

Copy link
Owner Author

Choose a reason for hiding this comment

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

これ、root1とかが後で変更されると、すでに実行した関数の返り値も変更されてしまうということだったんですね。
ありがとうございます。

- 再帰を使わない手もある
- https://discord.com/channels/1084280443945353267/1201211204547383386/1218151037697917028
- https://github.com/kazukiii/leetcode/pull/24/files
- https://discord.com/channels/1084280443945353267/1201211204547383386/1217821129297629364
- 部下から返ってきたときに、それを紐づける仕事をどこでやるか
- 部下に仕事を押し付けない再帰
- 上のStep1のやつ
- 部下に仕事を押し付ける(書き込んでしまう)再帰
- 部下に仕事を渡す前に、自分の上司との処理をしてしまう
- Step1より、下のようにダミーのnodeを作るのが賢いと気づいた(しかもこれなら入力も変更されない)

```python

from copy import deepcopy

class Solution:
def make_dummy_node_when_none(self, node):
if node:
return node
dummy = TreeNode(0)
dummy.left = None
dummy.right = None
return dummy

def mergeTrees(self, root1: Optional[TreeNode], root2: Optional[TreeNode]) -> Optional[TreeNode]:
def merge_helper(node1, node2, merged):
if not(node1.left or node2.left or node1.right or node2.right):
Copy link

Choose a reason for hiding this comment

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

細かいですがkeywordであるnotが関数のように見えるのでnotと()の間は空白を入れた方が良い気がします。
(実際は関数名と括弧の間は空白入れられるようではありますが...)

Choose a reason for hiding this comment

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

この場合はドモルガンで展開した方が分かりやすそうですかね。
また、このifは不要に思えます。

Copy link
Owner Author

Choose a reason for hiding this comment

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

ありがとうございます。
not のようなキーワードの場合空白入れるんですね。確かにandでもよかったかもですね。
確かにearly returnを意識しすぎましたが不要な条件分岐でしたね。

return
if node1.left or node2.left:
node1_left = self.make_dummy_node_when_none(node1.left)
node2_left = self.make_dummy_node_when_none(node2.left)
merged.left = TreeNode(node1_left.val + node2_left.val)
merge_helper(node1_left, node2_left, merged.left)
if node1.right or node2.right:
node1_right = self.make_dummy_node_when_none(node1.right)
node2_right = self.make_dummy_node_when_none(node2.right)
merged.right = TreeNode(node1_right.val + node2_right.val)
merge_helper(node1_right, node2_right, merged.right)
Comment on lines +65 to +74

Choose a reason for hiding this comment

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

deepcopyが使えそうですか。

if node1.left and node2.left:
  merged.left = TreeNode(node1.left.val + node2.left.val)
  merge_helper(node1.left, node2.left, merged.left)
else:
  merged.left = deepcopy(node1.left if node1.left else node2.left)

Copy link
Owner Author

Choose a reason for hiding this comment

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

なるほど、これは思いついてなかったです、ありがとうございます


if not root1:
return deepcopy(root2)
if not root2:
return deepcopy(root1)
merged_root = TreeNode(root1.val + root2.val)
merge_helper(root1, root2, merged_root)
return merged_root
```

- 部下に仕事を押し付けないstack
- [参考](https://discord.com/channels/1084280443945353267/1227073733844406343/1236682759649497099)
- [これ](https://discord.com/channels/1084280443945353267/1227073733844406343/1236324993839792149)でもいいが、あえて練習のためis_goingのフラグをつけた
- liquo_riceさんの最初でpopせず帰りにpopする実装と、行き帰りのフラグをつける実装だと、よく考えたら探索順も異なる
- 以下実装時のメモ(フラグがあるバージョン)
- 帰りの時に行うのは、取り出したmerged_nodeに対してleftのmerged_nodeとrightのmerged_nodeをくっつける
- よって、引き継ぎで必要な情報は、今のmerged_nodeと、leftのmerged_nodeと、rightのmerged_node。(行きには現在のnodeが必要)
- appendする時には、mutableなlistにしてbackのmerged_leftとgoのcurrent_merged(=次に取り出されるbackのmerge)が同じものを指すようにする
- listがその要素への参照なので、1要素のlistがCとかの参照の代わりになるというの、確かに
- めっちゃ時間かかった、以前1回実装を読んだつもりだったが、ちゃんと理解できてなかったことが判明、、、

Choose a reason for hiding this comment

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

これ私も苦手です。
読んで思い出せますが、思い出しながら書こうとしたら怪しいです。

Copy link

Choose a reason for hiding this comment

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

Python でのこれは半分概念の説明のためのもので実用性はあまりないですね。


```python

from copy import deepcopy

class Solution:
def make_dummy_node_when_none(self, node):
if node:
return node
dummy = TreeNode(0)
dummy.left = None
dummy.right = None
return dummy

def mergeTrees(self, root1: Optional[TreeNode], root2: Optional[TreeNode]) -> Optional[TreeNode]:
if not root1:
return deepcopy(root2)
if not root2:
return deepcopy(root1)
merged_root = [None]
nodes_stack = [(root1, root2, merged_root, [None], [None], "go")]
while nodes_stack:
node1, node2, merged_current, merged_left, merged_right, go_or_back = nodes_stack.pop()
if go_or_back == "go":
nodes_stack.append((node1, node2, merged_current, merged_left, merged_right, "back"))
if node1.left or node2.left:
node1_left = self.make_dummy_node_when_none(node1.left)
node2_left = self.make_dummy_node_when_none(node2.left)
nodes_stack.append((node1_left, node2_left, merged_left, [None], [None], "go"))
if node1.right or node2.right:
node1_right = self.make_dummy_node_when_none(node1.right)
node2_right = self.make_dummy_node_when_none(node2.right)
nodes_stack.append((node1_right, node2_right, merged_right, [None], [None], "go"))
else:
merged_current[0] = TreeNode(node1.val + node2.val)
merged_current[0].left = merged_left[0]
merged_current[0].right = merged_right[0]
return merged_root[0]
```

- 部下に仕事を押し付けるstack

```python

from copy import deepcopy

class Solution:
def make_dummy_node_when_none(self, node):
if node:
return node
dummy = TreeNode(0)
dummy.left = None
dummy.right = None
return dummy

def mergeTrees(self, root1: Optional[TreeNode], root2: Optional[TreeNode]) -> Optional[TreeNode]:
if not root1:
return deepcopy(root2)
if not root2:
return deepcopy(root1)
merged_root = TreeNode(root1.val + root2.val)
nodes_stack = [(root1, root2, merged_root)]
while nodes_stack:
node1, node2, merged_node = nodes_stack.pop()
if node1.left or node2.left:
node1_left = self.make_dummy_node_when_none(node1.left)
node2_left = self.make_dummy_node_when_none(node2.left)
merged_node.left = TreeNode(node1_left.val + node2_left.val)
nodes_stack.append((node1_left, node2_left, merged_node.left))
if node1.right or node2.right:
node1_right = self.make_dummy_node_when_none(node1.right)
node2_right = self.make_dummy_node_when_none(node2.right)
merged_node.right = TreeNode(node1_right.val + node2_right.val)
nodes_stack.append((node1_right, node2_right, merged_node.right))
return merged_root
```

### Step3

```python

from copy import deepcopy

class Solution:
def mergeTrees(self, root1: Optional[TreeNode], root2: Optional[TreeNode]) -> Optional[TreeNode]:
if not root1:
return deepcopy(root2)
if not root2:
return deepcopy(root1)
merged_root = TreeNode(root1.val + root2.val)
merged_root.left = self.mergeTrees(root1.left, root2.left)
merged_root.right = self.mergeTrees(root1.right, root2.right)
return merged_root
```