From 1cdf98e7b47f8b1ebd36ddb20cb59f9d298e1020 Mon Sep 17 00:00:00 2001 From: nittoco <166355467+nittoco@users.noreply.github.com> Date: Sat, 27 Jul 2024 19:04:41 +0900 Subject: [PATCH] 617. Merge Two Trees https://leetcode.com/problems/merge-two-binary-trees/description/ You are given two binary trees root1 and root2. Imagine that when you put one of them to cover the other, some nodes of the two trees are overlapped while the others are not. You need to merge the two trees into a new binary tree. The merge rule is that if two nodes overlap, then sum node values up as the new value of the merged node. Otherwise, the NOT null node will be used as the node of the new tree. Return the merged tree. Note: The merging process must start from the root nodes of both trees. --- 617. Merge Two Trees.md | 188 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 188 insertions(+) create mode 100644 617. Merge Two Trees.md diff --git a/617. Merge Two Trees.md b/617. Merge Two Trees.md new file mode 100644 index 0000000..562932e --- /dev/null +++ b/617. Merge Two Trees.md @@ -0,0 +1,188 @@ +### Step1 + +- nodeがNoneの時も合わせて処理を一般化するために関数を作ったけど、ここまでする必要はなかったかも +- returnする値が多くて見にくい気も +- このときはdeepcopyしてrootをreturnする選択肢に気づいていない +- merged_rootとmerged_nodeで変数名を迷う(再帰をしていく中でこの変数はrootでなくなるので) + +```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): + 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 + + 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 + +- どちらかなくなったらもう一方をそのまま返せば良いと言うことになぜか気づかなかった… +- 非破壊的にしたければdeepcopy()か +- 再帰を使わない手もある +- 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): + 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) + + 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回実装を読んだつもりだったが、ちゃんと理解できてなかったことが判明、、、 + + ```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 + ```