Skip to content

Commit 8b06bc3

Browse files
committed
LeetCode 236. Lowest Common Ancestor of a Binary Tree
1 parent e56d94d commit 8b06bc3

File tree

2 files changed

+85
-2
lines changed

2 files changed

+85
-2
lines changed

README.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,8 @@ Proposed solutions to some LeetCode problems. The first column links to the prob
6767
| [215. Kth Largest Element in an Array][lc215] | 🟠 Medium | [![python](res/py.png)](leetcode/kth-largest-element-in-an-array.py) |
6868
| [217. Contains Duplicate][lc217] | 🟢 Easy | [![python](res/py.png)](leetcode/contains-duplicate.py) |
6969
| [226. Invert Binary Tree][lc226] | 🟢 Easy | [![python](res/py.png)](leetcode/invert-binary-tree.py) |
70-
| [235. Lowest Common Ancestor of a Binary Search Tree][lc235] | 🟠 Medium | [![python](res/py.png)](leetcode/lowest-common-ancestor-of-a-binary-search-tree.py) |
70+
| [235. Lowest Common Ancestor of a Binary Search Tree][lc235] | 🟠 Medium | [![python](res/py.png)][lc235py] |
71+
| [236. Lowest Common Ancestor of a Binary Tree][lc236] | 🟠 Medium | [![python](res/py.png)][lc236py] |
7172
| [238. Product of Array Except Self][lc238] | 🟠 Medium | [![python](res/py.png)][lc238py] |
7273
| [240. Search a 2D Matrix II][lc240] | 🟠 Medium | [![python](res/py.png)][lc240py] |
7374
| [242. Valid Anagram][lc242] | 🟢 Easy | [![python](res/py.png)](leetcode/valid-anagram.py) |
@@ -218,6 +219,9 @@ Proposed solutions to some LeetCode problems. The first column links to the prob
218219
[lc217]: https://leetcode.com/problems/contains-duplicate/
219220
[lc226]: https://leetcode.com/problems/invert-binary-tree/
220221
[lc235]: https://leetcode.com/problems/lowest-common-ancestor-of-a-binary-search-tree/
222+
[lc235py]: leetcode/lowest-common-ancestor-of-a-binary-search-tree.py
223+
[lc236]: https://leetcode.com/problems/lowest-common-ancestor-of-a-binary-tree/
224+
[lc236py]: leetcode/lowest-common-ancestor-of-a-binary-tree.py
221225
[lc238]: https://leetcode.com/problems/product-of-array-except-self/
222226
[lc238py]: leetcode/product-of-array-except-self.py
223227
[lc240]: https://leetcode.com/problems/search-a-2d-matrix-ii/
@@ -253,7 +257,7 @@ Proposed solutions to some LeetCode problems. The first column links to the prob
253257
[lc560]: https://leetcode.com/problems/subarray-sum-equals-k/
254258
[lc560py]: leetcode/subarray-sum-equals-k.py
255259
[lc567]: https://leetcode.com/problems/permutation-in-string/
256-
[lc567py]: leetcode/problems/permutation-in-string.py
260+
[lc567py]: leetcode/permutation-in-string.py
257261
[lc572]: https://leetcode.com/problems/subtree-of-another-tree/
258262
[lc576]: https://leetcode.com/problems/out-of-boundary-paths/
259263
[lc576py]: leetcode/out-of-boundary-paths.py
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
# 236. Lowest Common Ancestor of a Binary Tree
2+
# 🟠 Medium
3+
#
4+
# https://leetcode.com/problems/lowest-common-ancestor-of-a-binary-tree/
5+
#
6+
# Tags: Tree - Depth-First Search - Binary Tree
7+
8+
import timeit
9+
10+
from data import TreeNode, deserializeStringArrayToBinaryTree, drawTree
11+
12+
13+
# Explore the tree using DFS, for each branch, return the number of nodes found in that branch, then check the root.
14+
# When we find 2 matches, p and q, return the root of the subtree we are currently exploring.
15+
#
16+
# Time complexity: O(n) - We may need to visit every node.
17+
# Space complexity: O(n) - The recursive call stack grows linearly with the size of the tree.
18+
#
19+
# Runtime: 96 ms, faster than 66.38% of Python3 online submissions for Lowest Common Ancestor of a Binary Tree.
20+
# Memory Usage: 26.3 MB, less than 30.85% of Python3 online submissions for Lowest Common Ancestor of a Binary Tree.
21+
class DFSMatchCount:
22+
def lowestCommonAncestor(self, root: TreeNode, p: TreeNode, q: TreeNode) -> TreeNode:
23+
24+
# Define a recursive function that returns the number of p and q found in a subtree.
25+
def dfs(node: TreeNode) -> int:
26+
# Base case when dfs gets called with None as a parameter.
27+
if not node:
28+
return 0
29+
30+
# Recursive call with check to see if we already have a result.
31+
# If we already found the LCA, return it up the call chain.
32+
left = dfs(node.left)
33+
if isinstance(left, TreeNode):
34+
return left
35+
right = dfs(node.right)
36+
if isinstance(right, TreeNode):
37+
return right
38+
39+
# If the sum of the matches in each branch plus matching this node's value is 2, we have found the LCA.
40+
found = left + right + int(node.val in [p.val, q.val])
41+
if found == 2:
42+
return node
43+
return found
44+
45+
return dfs(root)
46+
47+
48+
# TODO add an iterative solution.
49+
50+
51+
def test():
52+
root1 = deserializeStringArrayToBinaryTree("[3,5,1,6,2,0,8,null,null,7,4]")
53+
root2 = deserializeStringArrayToBinaryTree("[3,5,1,6,2,0,8,null,null,7,4]")
54+
root3 = deserializeStringArrayToBinaryTree("[1,2]")
55+
executors = [DFSMatchCount]
56+
tests = [
57+
[root1, root1.left, root1.right, root1],
58+
[root2, root2.left, root2.left.right.right, root2.left],
59+
[root3, root3, root3.left, root3],
60+
]
61+
for executor in executors:
62+
start = timeit.default_timer()
63+
for _ in range(int(float("1"))):
64+
for col, t in enumerate(tests):
65+
sol = executor()
66+
result = sol.lowestCommonAncestor(t[0], t[1], t[2])
67+
exp = t[3]
68+
assert (
69+
result == exp
70+
), f"\033[93m» {result} <> {exp}\033[91m for test {col} using \033[1m{executor.__name__}"
71+
stop = timeit.default_timer()
72+
used = str(round(stop - start, 5))
73+
res = "{0:20}{1:10}{2:10}".format(executor.__name__, used, "seconds")
74+
print(f"\033[92m» {res}\033[0m")
75+
76+
77+
test()
78+
79+
# drawTree(deserializeStringArrayToBinaryTree("[1,2]"))

0 commit comments

Comments
 (0)