-
Notifications
You must be signed in to change notification settings - Fork 0
617. Merge Two Binary Trees #23
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,106 @@ | ||
| 1st | ||
| 再帰が思いついたので、再帰で書く。 | ||
| 時間計算量:O(n) | ||
| 空間計算量:O(n) バランスしている木の場合は O(logn) | ||
|
|
||
| ```python | ||
| 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: | ||
| return root2 | ||
| if root2 is None: | ||
| return root1 | ||
|
|
||
| new_tree = TreeNode(root1.val + root2.val) | ||
|
|
||
| new_tree.left = self.mergeTrees(root1.left, root2.left) | ||
| new_tree.right = self.mergeTrees(root1.right, root2.right) | ||
| return new_tree | ||
| ``` | ||
|
|
||
| 2nd | ||
| 新しくツリーを作るのではなく、一つのツリーに重ねる方法にする。入力を破壊しても良い場合はこれでもよさそう。あとメモリ使用量も若干効率がいい。 | ||
| 個人的にはイミュータブルのほうが安全で嬉しいので、新しくツリーを作るほうを選ぶ。 | ||
| `if root1 is None and root2 is None`はなくてもよいことに気づいたので、削除した。 | ||
|
|
||
| ```python | ||
| 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 | ||
|
|
||
| root1.val += root2.val | ||
| root1.left = self.mergeTrees(root1.left, root2.left) | ||
| root1.right = self.mergeTrees(root1.right, root2.right) | ||
| return root1 | ||
|
|
||
| ``` | ||
|
|
||
| BFS で書いたが、左右両方に子がある場合、左側に子がある場合など条件が複雑になった。 | ||
| スタックオーバーフローは考慮しなければならないが、node の数が大きくない場合は再帰で書きたい。 | ||
|
|
||
| ```python | ||
| class Solution: | ||
| def mergeTrees(self, root1: TreeNode, root2: TreeNode) -> TreeNode: | ||
| if not root1: | ||
| return root2 | ||
| if not root2: | ||
| return root1 | ||
|
|
||
| merged_root = TreeNode(root1.val + root2.val) | ||
|
|
||
| queue = deque([(root1, root2, merged_root)]) | ||
|
|
||
| while queue: | ||
| node1, node2, merged_node = queue.popleft() | ||
| if node1.left and node2.left: | ||
| merged_left = TreeNode(node1.left.val + node2.left.val) | ||
| merged_node.left = merged_left | ||
| queue.append((node1.left, node2.left, merged_left)) | ||
| elif node1.left: | ||
| merged_node.left = node1.left | ||
| elif node2.left: | ||
| merged_node.left = node2.left | ||
|
|
||
| if node1.right and node2.right: | ||
| merged_right = TreeNode(node1.right.val + node2.right.val) | ||
| merged_node.right = merged_right | ||
| queue.append((node1.right, node2.right, merged_right)) | ||
| elif node1.right: | ||
| merged_node.right = node1.right | ||
| elif node2.right: | ||
| merged_node.right = node2.right | ||
|
|
||
| return merged_root | ||
| ``` | ||
|
|
||
| 過去ログを見る。 | ||
| https://github.com/fhiyo/leetcode/pull/25 | ||
|
|
||
| 3rd | ||
| 再帰の方法を選んだ。 | ||
| 理由は、 | ||
| もしライブラリーを使う人が、 | ||
| 「この関数は、root1 をただ読むだけだろう」と思い込んでいて、「マージの結果として新しいツリーを返すだけだろう」と予想している状態で、実際は root1 をガンガン上書きする関数が呼ばれたら、 | ||
| 後から root1 を再利用しようとしたときに予期せぬ値や構造になっていて「えっ、なんでツリーが書き換わってるの?」と驚くことになりそうだと思った。 | ||
| もし破壊的に書くなら、 | ||
| ライブラリのドキュメントに「破壊的です」と書くかな。 | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 全く新しい木を構築していく方法もあると思います。step3のコードでも下記のようなケースにroot1とmergeTreesの返り値でポインタの共有部分があるとroot1への変更が返り値にも影響してしまう場合があるのは気をつけないといけないと思いました。pythonのポインタについて誤解していたらごめんなさい root1 = TreeNode(1)
root2 = None
merged_tree = mergeTrees(root1, root2)
print(merged_tree.val) # 1
root1.val += 1
print(merged_tree.val) # 2There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ruby では ! をつけることによって表現する流儀がありますね。Python では変数名に使えないですが。 |
||
|
|
||
| ```python | ||
| class Solution: | ||
| def mergeTrees(self, root1: TreeNode, root2: TreeNode) -> TreeNode: | ||
| if root1 is None: | ||
| return root2 | ||
| if root2 is None: | ||
| return root1 | ||
|
Comment on lines
+96
to
+99
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Step 3の方法は、入力を破壊はしていないものの、マージ後の木と入力の木でノードが共有される可能性があります。 |
||
|
|
||
| new_node = TreeNode(root1.val + root2.val) | ||
| new_node.left = self.mergeTrees(root1.left, root2.left) | ||
| new_node.right = self.mergeTrees(root1.right, root2.right) | ||
|
|
||
| return new_node | ||
| ``` | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Python は、メンバ変数へのポインターが持てないので、どうしても繰り返し感がでますね。下のような方法は一応考えてみました。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
これも別に良いコードではないですが、C++ だとどこに書き込むかをスタックに積むことができるので、少し簡単になるのです。それを擬似的に Python で表現してみました。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
setattr、getattr、attrgetter あたりを使うべきでしょうね。
https://docs.python.org/3/library/functions.html#setattr
https://docs.python.org/3/library/operator.html#operator.attrgetter