Skip to content

Commit 205f2a5

Browse files
committed
feat: solve DaleStudy#249 with python
1 parent 7944537 commit 205f2a5

File tree

1 file changed

+106
-0
lines changed
  • lowest-common-ancestor-of-a-binary-search-tree

1 file changed

+106
-0
lines changed
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
from collections import deque
2+
from unittest import TestCase, main
3+
4+
5+
# Definition of TreeNode:
6+
class TreeNode:
7+
def __init__(self, x):
8+
self.val = x
9+
self.left = None
10+
self.right = None
11+
12+
13+
class Solution:
14+
def lowestCommonAncestor(self, root: TreeNode, p: TreeNode, q: TreeNode) -> TreeNode:
15+
return self.solve_bfs(root, p, q)
16+
17+
"""
18+
Runtime: 50 ms (Beats 81.68%)
19+
Time Complexity:`
20+
- bfs를 이용해 ancetor와 nodes를 초기화 하는데 트리의 모든 p와 q를 포함할 때까지 모든 node를 조회하는데, O(n)
21+
- p로 부터 부모를 따라 올라가는데, 트리의 모든 node가 편향적으로 연결된 경우 최대 O(n), upper bound
22+
- q로 부터 부모를 따라 올라가는데, 단, 위 p의 추적 path와 겹치지 않는 곳만 올라가므로, 총합이 O(n)
23+
> O(n) + (O(P) + O(Q)) = O(n) + O(n) ~= O(n)
24+
25+
Memory: 21.20 MB (Beats 14.40%)
26+
Space Complexity: O(n)
27+
- p, q가 트리의 리프노드인 경우 dq의 크기는 O(n), upper bound
28+
- p_ancestors와 q_anscestor의 크기는 합쳐서 O(n)
29+
> O(n) + O(n) ~= O(n)
30+
"""
31+
def solve_bfs(self, root: TreeNode, p: TreeNode, q: TreeNode) -> TreeNode:
32+
dq = deque([root])
33+
ancestor = {root.val: root.val}
34+
nodes = {root.val: root}
35+
while dq:
36+
if p.val in ancestor and q.val in ancestor:
37+
break
38+
39+
curr_node = dq.popleft()
40+
if curr_node.left:
41+
ancestor[curr_node.left.val] = curr_node.val
42+
dq.append(curr_node.left)
43+
nodes[curr_node.left.val] = curr_node.left
44+
if curr_node.right:
45+
ancestor[curr_node.right.val] = curr_node.val
46+
dq.append(curr_node.right)
47+
nodes[curr_node.right.val] = curr_node.right
48+
49+
p_val = p.val
50+
p_ancestors = set()
51+
p_ancestors.add(root.val)
52+
while p_val in ancestor and p_val != root.val:
53+
p_ancestors.add(p_val)
54+
p_ancestors.add(ancestor[p_val])
55+
p_val = ancestor[p_val]
56+
57+
q_val = q.val
58+
q_ancestors = set()
59+
q_ancestors.add(q_val)
60+
while q_val in ancestor and q_val not in p_ancestors:
61+
q_ancestors.add(q_val)
62+
q_ancestors.add(ancestor[q_val])
63+
q_val = ancestor[q_val]
64+
65+
common_ancestor = p_ancestors & q_ancestors
66+
if common_ancestor:
67+
return nodes[common_ancestor.pop()]
68+
else:
69+
return root
70+
71+
72+
class _LeetCodeTestCases(TestCase):
73+
74+
def test_1(self):
75+
root = TreeNode(5)
76+
node1 = TreeNode(3)
77+
node2 = TreeNode(6)
78+
node3 = TreeNode(2)
79+
node4 = TreeNode(4)
80+
node5 = TreeNode(1)
81+
82+
root.left = node1
83+
root.right = node2
84+
node1.left = node3
85+
node1.right = node4
86+
node3.left = node5
87+
88+
p = node5
89+
q = node1
90+
output = root
91+
self.assertEqual(Solution().lowestCommonAncestor(root, p, q), output)
92+
93+
def test_2(self):
94+
root = TreeNode(2)
95+
node1 = TreeNode(1)
96+
97+
root.left = node1
98+
99+
p = TreeNode(2)
100+
q = TreeNode(1)
101+
output = root
102+
self.assertEqual(Solution().lowestCommonAncestor(root, p, q), output)
103+
104+
105+
if __name__ == '__main__':
106+
main()

0 commit comments

Comments
 (0)