-
Notifications
You must be signed in to change notification settings - Fork 0
Add 617. Merge Two Binary Trees.md #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,199 @@ | ||
# step 1 | ||
|
||
再帰する解法。ノードの数が最大2000とあり、デフォルトのrecursion limit(1000)は超える。inner functionが多くなった。処理は簡単なので許容範囲と判断した。 | ||
|
||
root1, root2の深さの大きい方の数だけ再帰する。それぞれのノードの数をm, nとすると、 | ||
- time complexity: O(m + n) | ||
- space complexity: O(m + n) | ||
```python | ||
class Solution: | ||
def mergeTrees(self, root1: Optional[TreeNode], root2: Optional[TreeNode]) -> Optional[TreeNode]: | ||
def get_val(node: Optional[TreeNode]) -> int: | ||
if node is None: | ||
return 0 | ||
return node.val | ||
def get_left(node: Optional[TreeNode]) -> Optional[TreeNode]: | ||
if node is None: | ||
return None | ||
return node.left | ||
def get_right(node: Optional[TreeNode]) -> Optional[TreeNode]: | ||
if node is None: | ||
return None | ||
return node.right | ||
|
||
if root1 is None and root2 is None: | ||
return None | ||
|
||
merged_val = get_val(root1) + get_val(root2) | ||
merged_root = TreeNode(val=merged_val) | ||
merged_root.left = self.mergeTrees(get_left(root1), get_left(root2)) | ||
merged_root.right = self.mergeTrees( | ||
get_right(root1), | ||
get_right(root2) | ||
) | ||
return merged_root | ||
``` | ||
|
||
非再帰DFSを使う方法はバックトラックをどうすればいいかわからなかった。 | ||
|
||
# step 2 | ||
- https://github.com/olsen-blue/Arai60/pull/23/files | ||
- 片方がNoneになったとき、もう片方をそのままmergeしたtreeに繋げていた。 | ||
- 非破壊だが、一部のオブジェクトを共有している | ||
- コピーするなり、そのときNoneとなったノードをTreeNode(0)で上書きするなりしていた。 | ||
- https://github.com/olsen-blue/Arai60/pull/23/files#r1929995500 | ||
- https://github.com/tarinaihitori/leetcode/pull/23/files | ||
- BFSによる解法。ノードとそれらをマージしたノードを持っていた。 | ||
- https://github.com/tarinaihitori/leetcode/pull/23/files#r1919824481 | ||
- 冗長さを排除した書き方。 | ||
- pythonでもまだまだ知らない書き方があるなと感じた。 | ||
- https://github.com/hroc135/leetcode/pull/22/files#r1826509019 | ||
- stackに入りうるサイズの概算 | ||
- > 事前に確保する配列の大きさをどの値にするのが有効かは実際に入力ノードの形状と大きさに依存するので、事前に最適なサイズを決定するのは難しいと思います | ||
このサイズが性能にクリティカルな影響を与えるわけではないですが、ハードコードよりも環境変数などで外部から注入できるようにしたほうが良いと思いました | ||
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. 「事前に確保するコードがあることで悪い影響が起きる可能性」というのは、
とかでしょうか? |
||
|
||
DFS | ||
- time complexity: O(m + n) | ||
- space complexity: O(log(m + n)) | ||
```python | ||
class Solution: | ||
def mergeTrees(self, root1: Optional[TreeNode], root2: Optional[TreeNode]) -> Optional[TreeNode]: | ||
def get_val(node: Optional[TreeNode]) -> int: | ||
if node is None: | ||
return 0 | ||
return node.val | ||
Comment on lines
+61
to
+64
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. これならば、root1, 2がNoneかどうかで4通りすべて書く必要がなくなりますね。ありがとうございます。 |
||
def get_left(node: Optional[TreeNode]) -> Optional[TreeNode]: | ||
if node is None: | ||
return None | ||
return node.left | ||
def get_right(node: Optional[TreeNode]) -> Optional[TreeNode]: | ||
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 node is None: | ||
return None | ||
return node.right | ||
|
||
if root1 is None and root2 is None: | ||
return None | ||
|
||
merged_val = get_val(root1) + get_val(root2) | ||
merged_root = TreeNode(val=merged_val) | ||
stack = [(root1, root2, merged_root)] | ||
while stack: | ||
node1, node2, merged_node = stack.pop() | ||
|
||
node1_left = get_left(node1) | ||
node2_left = get_left(node2) | ||
if node1_left is not None or node2_left is not None: | ||
left_val = get_val(node1_left) + get_val(node2_left) | ||
merged_node.left = TreeNode(val=left_val) | ||
stack.append((node1_left, node2_left, merged_node.left)) | ||
|
||
node1_right = get_right(node1) | ||
node2_right = get_right(node2) | ||
if node1_right is not None or node2_right is not None: | ||
right_val = get_val(node1_right) + get_val(node2_right) | ||
merged_node.right = TreeNode(val=right_val) | ||
stack.append((node1_right, node2_right, merged_node.right)) | ||
return merged_root | ||
``` | ||
|
||
BFS | ||
- time complexity: O(m + n) | ||
- space complexity: O(log(m + n)) | ||
```python | ||
class Solution: | ||
def mergeTrees(self, root1: Optional[TreeNode], root2: Optional[TreeNode]) -> Optional[TreeNode]: | ||
def get_val(node: Optional[TreeNode]) -> int: | ||
if node is None: | ||
return 0 | ||
return node.val | ||
def get_left(node: Optional[TreeNode]) -> Optional[TreeNode]: | ||
if node is None: | ||
return None | ||
return node.left | ||
def get_right(node: Optional[TreeNode]) -> Optional[TreeNode]: | ||
if node is None: | ||
return None | ||
return node.right | ||
|
||
if root1 is None and root2 is None: | ||
return None | ||
|
||
merged_val = get_val(root1) + get_val(root2) | ||
merged_root = TreeNode(val=merged_val) | ||
stack = [(root1, root2, merged_root)] | ||
while stack: | ||
node1, node2, merged_node = stack.pop() | ||
|
||
node1_left = get_left(node1) | ||
node2_left = get_left(node2) | ||
if node1_left is None and node2_left is None: | ||
merged_node.left = None | ||
else: | ||
left_val = get_val(node1_left) + get_val(node2_left) | ||
merged_node.left = TreeNode(val=left_val) | ||
stack.append((node1_left, node2_left, merged_node.left)) | ||
|
||
node1_right = get_right(node1) | ||
node2_right = get_right(node2) | ||
if node1_right is None and node2_right is None: | ||
merged_node.right = None | ||
else: | ||
right_val = get_val(node1_right) + get_val(node2_right) | ||
merged_node.right = TreeNode(val=right_val) | ||
stack.append((node1_right, node2_right, merged_node.right)) | ||
return merged_root | ||
``` | ||
|
||
もう少しコンパクトに書いたもの(https://github.com/tarinaihitori/leetcode/pull/23/files#r1919824481 の写経) | ||
- time complexity: O(m + n) | ||
- space complexity: O(log(m + n)) | ||
```python | ||
class Solution: | ||
def mergeTrees(self, root1: Optional[TreeNode], root2: Optional[TreeNode]) -> Optional[TreeNode]: | ||
roots = list(filter(None, [root1, root2])) | ||
if not roots: | ||
return None | ||
root = TreeNode() | ||
stack = [(root, roots)] | ||
while stack: | ||
dst_node, src_nodes = stack.pop() | ||
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. filterは初めてみましたが、None除去したりは便利だと感じました。 dst は、 src と対比されていそうですが、これから作りたい destination node みたいなイメージですかね。 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. そうなりますね(完全にodaさんのものの写経になりますが)。 ループの先頭時点で、dst_nodeはTreeNode()となっているが値や子ノードが適切に設定されていない、src_nodesはdst_nodeと位置的に対応するnodeとなっています。ループないでvalやleft, rightを決めていくことになります |
||
dst_node.val = sum(n.val for n in src_nodes) | ||
|
||
lefts = [n.left for n in src_nodes if n.left] | ||
if lefts: | ||
dst_node.left = TreeNode() | ||
stack.append((dst_node.left, lefts)) | ||
|
||
rights = [n.right for n in src_nodes if n.right] | ||
if rights: | ||
dst_node.right = TreeNode() | ||
stack.append((dst_node.right, rights)) | ||
return root | ||
``` | ||
|
||
# step 3 | ||
|
||
```python | ||
class Solution: | ||
def mergeTrees(self, root1: Optional[TreeNode], root2: Optional[TreeNode]) -> Optional[TreeNode]: | ||
def get_val(node: Optional[TreeNode]) -> int: | ||
if node is None: | ||
return 0 | ||
return node.val | ||
def get_left(node: Optional[TreeNode]) -> Optional[TreeNode]: | ||
if node is None: | ||
return None | ||
return node.left | ||
def get_right(node: Optional[TreeNode]) -> Optional[TreeNode]: | ||
if node is None: | ||
return None | ||
return node.right | ||
|
||
if root1 is None and root2 is None: | ||
return None | ||
merged_val = get_val(root1) + get_val(root2) | ||
merged_root = TreeNode(val=merged_val) | ||
merged_root.left = self.mergeTrees(get_left(root1), get_left(root2)) | ||
merged_root.right = self.mergeTrees(get_right(root1), get_right(root2)) | ||
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.
一貫性があると読みやすくなるので、改行するしないをleftと合わせるほうが良いと思いました。