-
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,43 @@ | ||
| # 617. Merge Two Binary Trees | ||
|
|
||
| https://leetcode.com/problems/merge-two-binary-trees/description/ | ||
|
|
||
| ## step1(まず通す) | ||
|
|
||
| 再帰で書くのがぱっと見わかりやすそうだったのでまずはそれで書く。再帰でスタックする数も高々木の高さなので問題ないはず。 | ||
|
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.
日本語として不自然に感じました。スタックは stack と stuck の英単語があり、どちらもソフトウェアエンジニアリングの文脈で使われると思います。スタックすると日本語で書いた場合は、後者を指すことが多いように思います。後者は、止まる、行き詰まるという意味です。
Owner
Author
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. ありがとうございます。
このような正確な表現を心がけます。 |
||
|
|
||
| ## step2(整形&他の人のコードを読む) | ||
|
|
||
| 場合によって新しいオブジェクトが作られたり入力が再利用されたりしていて統一感のないコードになってしまった。挙動が一貫していないのは利用者からすると困ると思うので、両極端で書いてみる。 | ||
|
|
||
| 入力をインプレースで変更するパターン: step2-1.py | ||
|
|
||
| 全ノード再構築するパターン: step2-2.py | ||
|
|
||
| 書いてみたが、個人的には一番書きやすく感じられるのは step1.py。そもそも入力が破壊される可能性があるなら気をつけないといけないのは一緒だと思うと、step2-1.py よりは step1.py のほうが良さそうか。 | ||
|
|
||
| とはいえ、自分が使う立場になって一番嬉しいのは step2-2.py だと思う。 | ||
|
|
||
| ### 他の人はどう書いているか | ||
|
|
||
| コメント集を見ても、入力を破壊するか否かは重要な論点っぽい。パッと書いてすぐそこを気にすることができたので思考回路としては割と良さそう。 | ||
|
|
||
| - https://github.com/shintaro1993/arai60/pull/27/changes | ||
| - 片方が None のときに入力を破壊しないためのやり方にもいくつかあって | ||
| - (step2-2.py で私がやっているように)新しく作るノードの子を再帰的に計算する時に、引数の片方を None にする | ||
| - 番兵を使う = None を 「値が 0 で、左右の子が None」のノードに置き換えてから root1 も root2 も None でないときと同じ処理をする | ||
| - 本質的にはにはどちらも同じことをやっているが、見やすさでいうと後者かな | ||
| - 前者の方が宣言的で好きではある | ||
| - https://github.com/Fuminiton/LeetCode/pull/23/changes#r1997660222 | ||
| - 「一部のノードを共有すると、他の破壊的なメソッドとの相性で残念なことになるかもしれませんね。」 | ||
| - step1.py や step2-1.py は単に入力を破壊しているというだけでなくて一部ノードが tree1 と tree2 で共有されてしまう、というのはあまり明示的に意識してなかった。 | ||
|
|
||
| ## step3(10分以内にさっとかける \* 3回) | ||
|
|
||
| 破壊しないで書く。 | ||
|
|
||
| ## step4 | ||
|
|
||
| 再帰→スタックの変換。 | ||
|
|
||
| tree1, tree2 の left, right をスタックに詰めた時点で merged_node もデフォルトの TreeNode を受け皿として一緒に詰めておかないと親と子を紐づけられない、というのを理解するのにちょっと時間がかかった。 | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| from typing import Optional | ||
|
|
||
|
|
||
| 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: | ||
| return root2 | ||
|
|
||
| if root2 is None: | ||
| return root1 | ||
|
|
||
| return TreeNode( | ||
| val=root1.val + root2.val, | ||
| left=self.mergeTrees(root1.left, root2.left), | ||
| right=self.mergeTrees(root1.right, root2.right), | ||
| ) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| from typing import Optional | ||
|
|
||
|
|
||
| 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: | ||
| 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 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| from typing import Optional | ||
|
|
||
|
|
||
| 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: | ||
| return TreeNode( | ||
| val=root2.val, | ||
| left=self.mergeTrees(None, root2.left), | ||
| right=self.mergeTrees(None, root2.right), | ||
| ) | ||
|
|
||
| if root2 is None: | ||
| return TreeNode( | ||
| val=root1.val, | ||
| left=self.mergeTrees(root1.left, None), | ||
| right=self.mergeTrees(root1.right, None), | ||
| ) | ||
|
|
||
| return TreeNode( | ||
| val=root1.val + root2.val, | ||
| left=self.mergeTrees(root1.left, root2.left), | ||
| right=self.mergeTrees(root1.right, root2.right), | ||
| ) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| from typing import Optional | ||
|
|
||
|
|
||
| class TreeNode: | ||
| def __init__(self, val=0, left=None, right=None): | ||
| self.val = val | ||
| self.left = left | ||
| self.right = right | ||
|
|
||
|
|
||
| class Solution: | ||
|
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. 再帰は明示的なstackを用いてiterativeに書き直せるので、それも書いておくといいと思います。
Owner
Author
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. ありがとうございます。書いてみます。 |
||
| 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 = TreeNode(0) | ||
| if root2 is None: | ||
| root2 = TreeNode(0) | ||
|
|
||
| return TreeNode( | ||
| val=root1.val + root2.val, | ||
| left=self.mergeTrees(root1.left, root2.left), | ||
| right=self.mergeTrees(root1.right, root2.right), | ||
| ) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| from typing import Optional | ||
|
|
||
|
|
||
| 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 | ||
|
|
||
| merged_root = TreeNode() | ||
| stack = [(root1, root2, merged_root)] | ||
| while stack: | ||
| node1, node2, merged_node = stack.pop() | ||
| if node1 is None: | ||
| node1 = TreeNode(0) | ||
| if node2 is None: | ||
| node2 = TreeNode(0) | ||
|
|
||
| merged_node.val = node1.val + node2.val | ||
| if not (node1.left is None and node2.left is None): | ||
| merged_node.left = TreeNode() | ||
| stack.append((node1.left, node2.left, merged_node.left)) | ||
| if not (node1.right is None and node2.right is None): | ||
| merged_node.right = TreeNode() | ||
| stack.append((node1.right, node2.right, merged_node.right)) | ||
|
|
||
| return merged_root |
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の再帰の上限について簡単に調べてみたのですが,default recursion limitは1000らしいです.(詳しくないので頓珍漢なこと言ってたらすみません)
https://peps.python.org/pep-0651/
なので,0 <= N <= 2000 である今回の問題では,一概に問題ないとは言えないかもしれません.
自分のローカルで試したのが以下
leetcodeで同様にすると
550000とでました.引き上げられているのかもしれませんね.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.
ありがとうございます。デフォルトのリミットは 1000 ですね。Python で再起が好まれない理由の一つだと思います。
与えられた木が一本道で 2000 ノードあるみたいな状態だとスタックに積まれる数も2000になるのでデフォルトだと溢れる可能性ありますね。見積もり不正確でした。ご指摘ありがとうございます。