diff --git a/problems/617.merge-two-binary-trees/memo.md b/problems/617.merge-two-binary-trees/memo.md new file mode 100644 index 0000000..7c154c0 --- /dev/null +++ b/problems/617.merge-two-binary-trees/memo.md @@ -0,0 +1,64 @@ +## step1 +- 同じ位置関係のnodeのValueを足したものを出力する +- 同じように二つのnodeを動かしていけば良さそう +- NodeがNoneの場合だけ場合分けする +- 両方Noneが入ってくる場合もありそう + - 最初に片方がNoneの場合もう片方を返すようにする、両方Noneの場合もこれで問題なし + - Noneが入ってこない問題として考える +- 意外と一緒に辿っていくところや片方がNoneの場合の処理が難しく30分ほど実装にかかってしまった + - 片方がNoneの場合TreeNodeを擬似的に作ることで解決した +- 時間計算量はO(N),空間計算量はO(1)だと思う + +## step2 +- merge_nodeで処理をまとめることができたのは良かったと思うが、leftとrightでそれぞれ処理している部分もできればまとめたい気持ちがある + - いい方法が思いつかない + - ChatGPTに聞くと下記で左右の処理をまとめられるとのこと + +```py +left_merged_node = merge_node(node1.left, node2.left) +if left_merged_node: + frontier_node.left = left_merged_node + frontier_nodes.append((left_merged_node, node1.left, node2.left)) + +right_merged_node = merge_node(node1.right, node2.right) +if right_merged_node: + frontier_node.right = right_merged_node + frontier_nodes.append((right_merged_node, node1.right, node2.right)) +``` + +書き換え後👇(ここだけ差し替え) +```py +for side in ("left", "right"): + child1 = getattr(node1, side) + child2 = getattr(node2, side) + + merged_child = merge_node(child1, child2) + if merged_child: + setattr(frontier_node, side, merged_child) + frontier_nodes.append((merged_child, child1, child2)) +``` +- `getattr`、`setattr`は見たことある程度で何をしているのかを全く知らなかったが、属性名を変数で扱え今回のようなloop処理で処理を共通化できそうな時には使えそうだと思った + +- 最初にそのまま返す場合は元のNodeのオブジェクトなのに対して最初がNoneでなければ新しいオブジェクトを返すのがやめたい + - 最初にNone,Noneの場合だけ処理してしまえば、最初のmerged_rootをmerge_nodeで作成すれば問題なさそう +- 他の人のコードを見る +- https://github.com/Yuto729/LeetCode_arai60/pull/28/changes + - `この木構造は、何かを表していて mergeTrees 以外の破壊的な関数が存在しないならば、木の一部を共有してもいいわけですが、そうでないならば、このライブラリーを使う人にびっくりさせると思います。`この感覚があったので片方がNoneでも新しいNodeを作るようにした + - 扱う場所の近くに変数をまとめるというレビューの指摘はよかった +- https://github.com/05ryt31/leetcode/pull/14 + - https://ja.wikipedia.org/wiki/%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89%E3%82%AF%E3%82%A8%E3%83%AA%E5%88%86%E9%9B%A2 +- https://github.com/mamo3gr/arai60/pull/22 + - 再帰の選択肢はなかったので選択肢に入れたいと思った + - appendixの非対称に扱うやり方も勉強になった + - `if root1 is None: の再帰は root1, root2 = root2, root1 くらいでもいいかと思います。(個人的な感覚として、スワップだけで再帰スタックを掘るのは少し重いです。)`https://github.com/Shoichifunyu/shofun/pull/17#discussion_r2659314213 + - 入れ替えるのは再帰に任せない選択肢も持っておく +- https://github.com/plushn/SWE-Arai60/pull/23 + - https://github.com/plushn/SWE-Arai60/pull/23#discussion_r2653315768 + - 完全に理解できた自信はないが、Pythonの関数の仕組みが勉強になった + +- step2は再帰でも書いてみる + - 他の方の回答を忘れた状態で処理の概要だけイメージして5分ほどで書けたがまだバグがあっても不思議ではないくらいの明確な自信を持てない感じで書いている + - 1行1行のコードを読めば納得感は持って書けているが全体としてぼんやり本当にこれでいいのかが自信が持てないような感じ +## step3 +- 再帰の方が書く量が短く + diff --git a/problems/617.merge-two-binary-trees/step1.py b/problems/617.merge-two-binary-trees/step1.py new file mode 100644 index 0000000..6ad2f25 --- /dev/null +++ b/problems/617.merge-two-binary-trees/step1.py @@ -0,0 +1,59 @@ +# +# @lc app=leetcode id=617 lang=python3 +# +# [617] Merge Two Binary Trees +# + +# @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 mergeTrees( + self, root1: Optional[TreeNode], root2: Optional[TreeNode] + ) -> Optional[TreeNode]: + if root1 is None: + return root2 + if root2 is None: + return root1 + + merged_root = TreeNode(root1.val + root2.val) + + frontier_nodes = collections.deque() + frontier_nodes.append((merged_root, root1, root2)) + + def merge_node(child1, child2): + if child1 is None and child2 is None: + return + if child1 and child2: + return TreeNode(child1.val + child2.val) + elif child1: + return TreeNode(child1.val) + elif child2: + return TreeNode(child2.val) + + while frontier_nodes: + frontier_node, node1, node2 = frontier_nodes.popleft() + if node1 is None: + node1 = TreeNode() + if node2 is None: + node2 = TreeNode() + left_merged_node = merge_node(node1.left, node2.left) + if left_merged_node: + frontier_node.left = left_merged_node + frontier_nodes.append((left_merged_node, node1.left, node2.left)) + right_merged_node = merge_node(node1.right, node2.right) + if right_merged_node: + frontier_node.right = right_merged_node + frontier_nodes.append((right_merged_node, node1.right, node2.right)) + + return merged_root + + +# @lc code=end diff --git a/problems/617.merge-two-binary-trees/step2.py b/problems/617.merge-two-binary-trees/step2.py new file mode 100644 index 0000000..ba41d6c --- /dev/null +++ b/problems/617.merge-two-binary-trees/step2.py @@ -0,0 +1,58 @@ +# +# @lc app=leetcode id=617 lang=python3 +# +# [617] Merge Two Binary Trees +# + +# @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 mergeTrees( + self, root1: Optional[TreeNode], root2: Optional[TreeNode] + ) -> Optional[TreeNode]: + if root1 is None and root2 is None: + return + + def merge_node(child1, child2): + if child1 is None and child2 is None: + return + if child1 and child2: + return TreeNode(child1.val + child2.val) + elif child1: + return TreeNode(child1.val) + elif child2: + return TreeNode(child2.val) + + merged_root = merge_node(root1, root2) + + frontier_nodes = collections.deque() + frontier_nodes.append((merged_root, root1, root2)) + + while frontier_nodes: + frontier_node, node1, node2 = frontier_nodes.popleft() + if node1 is None: + node1 = TreeNode() + if node2 is None: + node2 = TreeNode() + + for side in ("left", "right"): + child1 = getattr(node1, side) + child2 = getattr(node2, side) + + merged_child = merge_node(child1, child2) + if merged_child: + setattr(frontier_node, side, merged_child) + frontier_nodes.append((merged_child, child1, child2)) + + return merged_root + + +# @lc code=end diff --git a/problems/617.merge-two-binary-trees/step2_recursion.py b/problems/617.merge-two-binary-trees/step2_recursion.py new file mode 100644 index 0000000..3af8936 --- /dev/null +++ b/problems/617.merge-two-binary-trees/step2_recursion.py @@ -0,0 +1,37 @@ +# +# @lc app=leetcode id=617 lang=python3 +# +# [617] Merge Two Binary Trees +# + +# @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 mergeTrees( + self, root1: Optional[TreeNode], root2: Optional[TreeNode] + ) -> Optional[TreeNode]: + if root1 is None and root2 is None: + return None + + if root1 is None: + root1, root2 = root2, root1 + + # root1 is not None + merged = TreeNode(root1.val) + if root2 is not None: + merged.val += root2.val + merged.left = self.mergeTrees(root1.left, root2.left) + merged.right = self.mergeTrees(root1.right, root2.right) + else: + merged.left = self.mergeTrees(root1.left, None) + merged.right = self.mergeTrees(root1.right, None) + + return merged + + +# @lc code=end diff --git a/problems/617.merge-two-binary-trees/step3.py b/problems/617.merge-two-binary-trees/step3.py new file mode 100644 index 0000000..7f24c53 --- /dev/null +++ b/problems/617.merge-two-binary-trees/step3.py @@ -0,0 +1,55 @@ +# +# @lc app=leetcode id=617 lang=python3 +# +# [617] Merge Two Binary Trees +# + +# @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 mergeTrees( + self, root1: Optional[TreeNode], root2: Optional[TreeNode] + ) -> Optional[TreeNode]: + def merge_node(root1, root2): + if root1 is None and root2 is None: + return None + if root1 and root2: + return TreeNode(root1.val + root2.val) + if root1 is None: + return TreeNode(root2.val) + if root2 is None: + return TreeNode(root1.val) + + merged_root = merge_node(root1, root2) + + frontier_nodes = collections.deque() + frontier_nodes.append((merged_root, root1, root2)) + + while frontier_nodes: + merged_node, node1, node2 = frontier_nodes.popleft() + if node1 is None: + node1 = TreeNode() + if node2 is None: + node2 = TreeNode() + + for side in ("left", "right"): + child1 = getattr(node1, side) + child2 = getattr(node2, side) + + merged_child = merge_node(child1, child2) + if merged_child: + setattr(merged_node, side, merged_child) + frontier_nodes.append((merged_child, child1, child2)) + + return merged_root + + +# @lc code=end