-
Notifications
You must be signed in to change notification settings - Fork 0
Solved: 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,123 @@ | ||
| ## 取り組み方 | ||
| - step1: 5分以内に空で書いてAcceptedされるまで解く | ||
| - step2: discord内で他の方のコードを5人分読む + 何をよい/悪いと思ったか、何を避けたいと思ったか等感じたことを3つ以上かく | ||
| - step3: 10分以内に1回もエラーを出さずに3回連続で解く | ||
|
|
||
| ## step1 | ||
| ### 考えたこと | ||
| 再帰で書くのがシンプルで良さそう。 | ||
| 「再帰の終了条件->再帰のメイン処理->次の再帰の呼び出し->結果の返却」の流れで考える。 | ||
|
|
||
| 再帰の終了条件は、root1,root2がどちらもnullの時、もしくは、どちらか一方がnullの時で、それぞれnullとどちらか一方のrootを返せばよい。 | ||
| 再帰のメイン処理では、merged treeの現在のvalが何なのかを計算する。 | ||
| 次の再帰の呼び出しは、現在のmergedの頂点からleftとrightがどうなっているかを探索する。 | ||
| 結果の返却として、mergedを返す。 | ||
|
|
||
| 練習のため、DFSの再帰の実装とスタックの実装それぞれで解く。 | ||
|
|
||
| ##### 再帰 | ||
| ```python | ||
| class Solution: | ||
| def mergeTrees(self, root1: Optional[TreeNode], root2: Optional[TreeNode]) -> Optional[TreeNode]: | ||
| if not root1 and not root2: | ||
| return None | ||
| if not root1: | ||
| return root2 | ||
| if not root2: | ||
| return root1 | ||
| merged = TreeNode(root1.val + root2.val) | ||
| merged.left = self.mergeTrees(root1.left, root2.left) | ||
| merged.right = self.mergeTrees(root1.right, root2.right) | ||
| return merged | ||
| ``` | ||
|
|
||
| ##### stack | ||
| ```python | ||
| class Solution: | ||
| def mergeTrees(self, root1: Optional[TreeNode], root2: Optional[TreeNode]) -> Optional[TreeNode]: | ||
| if not root1 and not root2: | ||
| return None | ||
| if not root1: | ||
| return root2 | ||
| if not root2: | ||
| return root1 | ||
|
|
||
| merged = TreeNode() | ||
| stack = [(root1, root2, merged)] | ||
| while stack: | ||
| node1, node2, merged_node = stack.pop() | ||
|
|
||
| if node1 and node2: | ||
| merged_node.val = node1.val + node2.val | ||
| elif node1: | ||
| merged_node.val = node1.val | ||
| elif node2: | ||
| merged_node.val = node2.val | ||
|
Comment on lines
+52
to
+55
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. この場合、終端を表す番兵を使う手はありますよ。
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. ご指摘ありがとうございます。 node1 = node1 if node1 else TreeNode(0)
node2 = node2 if node2 else TreeNode(0)
merged_node.val = node1.val + node2.valThere 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. 簡単になるのはここから下もです。
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. 以下の三項演算子の部分も単にnode1.rightで済むようになるのですね。 node1.right if node1 else None,
node2.right if node2 else None,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. if (node1 and node1.left) or (node2 and node2.left):これもです。node1 node2 がある前提で書けます。
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. おっしゃる通りですね。 ご指摘ありがとうございます。 SENTINEL = TreeNode(0)
class Solution:
def mergeTrees(self, root1: Optional[TreeNode], root2: Optional[TreeNode]) -> Optional[TreeNode]:
if root1 is None and root2 is None:
return None
merged = TreeNode()
stack = [(root1, root2, merged)]
while stack:
node1, node2, merged_node = stack.pop()
if node1 is None:
node1 = SENTINEL
if node2 is None:
node2 = SENTINEL
merged_node.val = node1.val + node2.val
if node1.left is not None or node2.left is not None:
merged_node.left = TreeNode()
stack.append((node1.left, node2.left, merged_node.left))
if node1.right is not None or node2.right is not None:
merged_node.right = TreeNode()
stack.append((node1.right, node2.right, merged_node.right))
return merged |
||
|
|
||
| if (node1 and node1.left) or (node2 and node2.left): | ||
| merged_node.left = TreeNode() | ||
| stack.append(( | ||
| node1.left if node1 else None, | ||
| node2.left if node2 else None, | ||
| merged_node.left | ||
| )) | ||
| if (node1 and node1.right) or (node2 and node2.right): | ||
| merged_node.right = TreeNode() | ||
| stack.append(( | ||
| node1.right if node1 else None, | ||
| node2.right if node2 else None, | ||
| merged_node.right | ||
| )) | ||
|
|
||
| return merged | ||
| ``` | ||
|
|
||
| ## step2 | ||
| ### 読んだコード | ||
| - https://github.com/rossy0213/leetcode/pull/12/files | ||
| - https://github.com/ryoooooory/LeetCode/pull/26/files | ||
| - https://github.com/fhiyo/leetcode/pull/25/files | ||
| - https://github.com/seal-azarashi/leetcode/pull/22/files | ||
| - https://github.com/Ryotaro25/leetcode_first60/pull/25/files | ||
|
|
||
| ### 読んだ感想 | ||
| 再帰の方が自然に感じた。 | ||
|
|
||
| 一方で、簡単な実装だからというだけではなくて、合理的な理由も説明できるようにしていきたい。 | ||
| 観点は、可読性、探索対象の深度、メモリ使用量くらいか。 | ||
| 今回は、探索対象の深さが予測できてかつメモリの使用量も明示的に管理したいわけではないので、再帰で書いて読みやすさを重視したい。 | ||
|
|
||
| https://github.com/seal-azarashi/leetcode/pull/22/files#r1778932434 | ||
| > それで、どんなユースケースでこの mergeTrees は使われるんでしょうか。 | ||
|
|
||
| root1を基準に更新していく解法が LeetCode 上の Solutions によく載っていたが、破壊的な実装なので微妙ではと思った。 | ||
|
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. 一部のノードを共有すると、他の破壊的なメソッドとの相性で残念なことになるかもしれませんね。 |
||
|
|
||
| ```python | ||
| class Solution: | ||
| def mergeTrees(self, root1: Optional[TreeNode], root2: Optional[TreeNode]) -> Optional[TreeNode]: | ||
| if not root1 and not root2: | ||
| return None | ||
| if not root1 or not root2: | ||
| return root1 if root1 else root2 | ||
|
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. 三項演算子が読み手の認知負荷を上げてしまう可能はなくはないです。 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. Pythonの三項演算子読みにくいですね 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. これ、私が感じるのは、なんで、わざわざ混ぜた後に分離するのか、です。 右手も左手も何も持っていない場合は、None を返してください。 Python の三項演算子、目が左右に振られるんですよね。
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.
確かに身近な言葉に直してみると、不自然な操作ですね 今回ですと、「右手に何も持っていない、または、左手に何も持っていない場合は、持っている方を返してください」(return root1 or root2)が自然かと思いました。 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. 私は、 |
||
|
|
||
| merged = TreeNode(root1.val + root2.val) | ||
| merged.left = self.mergeTrees(root1.left, root2.left) | ||
| merged.right = self.mergeTrees(root1.right, root2.right) | ||
| return merged | ||
| ``` | ||
|
|
||
| ## step3 | ||
| ```python | ||
| class Solution: | ||
| def mergeTrees(self, root1: Optional[TreeNode], root2: Optional[TreeNode]) -> Optional[TreeNode]: | ||
| if not root1 and not root2: | ||
| return None | ||
| if not 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. Noneとの比較には is Noneを使うべきだと思います。
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. レビュー、リンクの共有までありがとうございます。 |
||
| return root2 | ||
| if not root2: | ||
| return root1 | ||
| merged = TreeNode(root1.val + root2.val) | ||
| merged.left = self.mergeTrees(root1.left, root2.left) | ||
| merged.right = self.mergeTrees(root1.right, root2.right) | ||
| return merged | ||
| ``` | ||
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.
この場合、そのまま下に流せば、ノードが入力と出力で共有されませんね。
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.
deepcopyで返す or 下に流さないと、mergeTreesの返り値を操作したときに入力を書き換えてしまう可能性があるんですね。。見逃していました。
shining-ai/leetcode#23 (comment)