-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathflatten-binary-tree-to-linked-list.py
171 lines (147 loc) · 5.93 KB
/
flatten-binary-tree-to-linked-list.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
# 114. Flatten Binary Tree to Linked List
# 🟠 Medium
#
# https://leetcode.com/problems/flatten-binary-tree-to-linked-list/
#
# Tags: Linked List - Stack - Tree - Depth-First Search - Binary Tree
import timeit
from typing import Optional
from data import (
TreeNode,
deserializeStringArrayToBinaryTree,
serializeTreeToList,
)
# 1e3 calls
# » Recursive 0.01742 seconds
# » Stack 0.01461 seconds
# » Morris 0.01495 seconds
# Use recursive DFS to flatten the tree.
# This solution should be slower than the other two but it is faster
# with the LeetCode tests, it could be due to the tests themselves.
#
# Time complexity: O(n) - We visit each node once.
# Space complexity: O(n) - For the call stack.
#
# Runtime: 50 ms, faster than 68.94% of Python3 online submissions for
# Flatten Binary Tree to Linked List.
# Memory Usage: 15.2 MB, less than 47.67% of Python3 online submissions
# for Flatten Binary Tree to Linked List.
class Recursive:
def flatten(self, root: Optional[TreeNode]) -> None:
# Recursive function to flatten subtrees.
def dfs(root):
if not root:
return
# Recursively process the subtrees.
left_subtree = dfs(root.left)
right_subtree = dfs(root.right)
# If the current tree has a left child, update the links.
if left_subtree:
left_subtree.right = root.right
root.right = root.left
root.left = None
return right_subtree or left_subtree or root
dfs(root)
# Explore the tree starting at the root, for each node that has a right
# and left children, push the right child into a stack, make the right
# pointer point to the left child and set the left pointer to null.
# If a node only has a right child, then process it next.
# If a children does not have either, try to pop from the stack.
#
# Time complexity: O(n) - We visit each node once or twice.
# Space complexity: O(n) - The stack could hold n/2 nodes depending on
# the shape of the input tree.
#
# Runtime: 86 ms, faster than 5.85% of Python3 online submissions for
# Flatten Binary Tree to Linked List.
# Memory Usage: 15.2 MB, less than 87.68% of Python3 online submissions
# for Flatten Binary Tree to Linked List.
class Stack:
def flatten(self, root: Optional[TreeNode]) -> None:
if not root:
return
current = root
queue = []
while current:
# If the current node has a left child, try to push the
# right sub-tree into the queue.
if current.left:
if current.right:
queue.append(current.right)
# Update the right pointer to point to the left node
# and set the left pointer to null
current.right = current.left
current.left = None
# If the current node does not have a right child, pop from
# the stack, it could be None.
if not current.right and queue:
current.right = queue.pop()
# Move to processing the next node, if None, we will exit
# from the while loop.
current = current.right
# The root of the tree is still the same one and they ask us to
# not return it.
# return root
# What the exercise is asking for is almost a Morris traversal of the
# binary tree. We can make use of that fact to provide a solution with
# O(1) space complexity, no recursion and no stack.
#
# Time complexity: O(n) - We visit each node once, the outer while loop
# will visit each node once, the inner while loop will visit each node
# none or one time.
# Space complexity: O(1) - We only keep a pointer to the current node
# we are processing.
#
# Runtime: 67 ms, faster than 31.00% of Python3 online submissions for
# Flatten Binary Tree to Linked List.
# Memory Usage: 15.2 MB, less than 47.67% of Python3 online submissions
# for Flatten Binary Tree to Linked List.
class Morris:
def flatten(self, root: Optional[TreeNode]) -> None:
if not root:
return
current = root
# Process the current node.
while current:
# If the current node has a left child, travel all the way
# down its rightmost path and append the current node's
# right child as its right child.
if current.left:
temp = current.left
while temp.right:
temp = temp.right
temp.right = current.right
# Now we have the right subtree appended to the
# rightmost node of the left subtree, update links.
current.right = current.left
current.left = None
# Move to processing the next right node.
current = current.right
def test():
executors = [Recursive, Stack, Morris]
tests = [
["[1,2,5,3,4,null,6]", "[1,null,2,null,3,null,4,null,5,null,6]"],
["[]", "[]"],
["[0]", "[0]"],
]
for executor in executors:
start = timeit.default_timer()
for _ in range(int(float("1"))):
for i, t in enumerate(tests):
sol = executor()
# Do not return anything, modify root in-place instead.
root = deserializeStringArrayToBinaryTree(t[0])
sol.flatten(root)
result = serializeTreeToList(root)
exp = serializeTreeToList(
deserializeStringArrayToBinaryTree(t[1])
)
assert result == exp, (
f"\033[93m» {result} <> {exp}\033[91m for "
+ f"test {i} using \033[1m{executor.__name__}"
)
stop = timeit.default_timer()
used = str(round(stop - start, 5))
res = "{0:20}{1:10}{2:10}".format(executor.__name__, used, "seconds")
print(f"\033[92m» {res}\033[0m")
test()