Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions 0236.Lowest-Common-Ancestor-of-a-Binary-Tree/memo.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# 236. Lowest Common Ancestor of a Binary Tree

## step1
pとqが見つかるまで木を探索し、見つかったら
以前"Lowest Common Ancestor of a Binary Search Tree"で書いた解法。

## 他の人のコード
https://github.com/huyfififi/coding-challenges/pull/46

かなりシンプルな解法になっている。自分の解法が冗長であったことを認識した。

左右に分岐する点を求める解法は計算量的に O(N^2)になってしまう(step2_top_down.py)。
この解法はtop-downであると言えるだろう。

> lowest common ancestorの解釈を少し変えているのでやや邪道な感じがするが、`p`と`q`が左右に分かれているパターンを判定するために、`q`を含まない`p`をrootとした部分木においても、`p`がlowest common ancestorだとしておく(`q`が部分木のrootの場合も同様に)。

この広義の定義と狭義の定義を区別した解法が「Cracking The Coding Interviewに載っていた」という解法であることも認識した。

この解法はbottom-upといえる(step2_bottom_up.py)。
時間計算量O(N)、空間計算量O(h)(最悪O(N))

top-down、bottom-upという観点でいえば、step1の解法はハイブリッドと言える。
再帰を使っていない点では優れている。
51 changes: 51 additions & 0 deletions 0236.Lowest-Common-Ancestor-of-a-Binary-Tree/step1_hybrid.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# Definition for a binary tree node.
class TreeNode:
def __init__(self, x):
self.val = x
self.left = None
self.right = None


class Solution:
def lowestCommonAncestor(
self, root: TreeNode, p: TreeNode, q: TreeNode
) -> TreeNode:
node_to_depth_and_parent: dict[TreeNode, tuple[int, TreeNode | None]] = {}

depth = 1
frontier = [(root, None)]
found_p = False
found_q = False
while frontier:
next_frontier = []
for node, parent in frontier:
if node is None:
continue
node_to_depth_and_parent[node] = (depth, parent)
if node == p:
found_p = True
if node == q:
found_q = True
if found_p and found_q:
break
next_frontier.append((node.left, node))
next_frontier.append((node.right, node))
depth += 1
frontier = next_frontier

depth_p, parent_p = node_to_depth_and_parent[p]
depth_q, parent_q = node_to_depth_and_parent[q]
if depth_p < depth_q:
depth_p, depth_q = depth_q, depth_p
parent_p, parent_q = parent_q, parent_p
p, q = q, p
while depth_p > depth_q:
p = parent_p
depth_p, parent_p = node_to_depth_and_parent[p]
while p != q:
p = parent_p
depth_p, parent_p = node_to_depth_and_parent[p]
q = parent_q
depth_q, parent_q = node_to_depth_and_parent[q]

return p
29 changes: 29 additions & 0 deletions 0236.Lowest-Common-Ancestor-of-a-Binary-Tree/step2_bottom_up.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@

// Definition for a binary tree node.
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};

class Solution {
public:
TreeNode *lowestCommonAncestor(TreeNode *root, TreeNode *p, TreeNode *q) {
if (root == nullptr) {
return nullptr;
}
if (root == p || root == q) {
return root;
}
TreeNode *left_found = lowestCommonAncestor(root->left, p, q);
TreeNode *right_found = lowestCommonAncestor(root->right, p, q);
if (left_found == nullptr) {
return right_found;
}
if (right_found == nullptr) {
return left_found;
}
return root;
}
};
25 changes: 25 additions & 0 deletions 0236.Lowest-Common-Ancestor-of-a-Binary-Tree/step2_bottom_up.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Definition for a binary tree node.
class TreeNode:
def __init__(self, x):
self.val = x
self.left = None
self.right = None


class Solution:
def lowestCommonAncestor(
self, root: TreeNode, p: TreeNode, q: TreeNode
) -> TreeNode:
if root is None:
return None
if root == p or root == q:
return root

left_found = self.lowestCommonAncestor(root.left, p, q)
right_found = self.lowestCommonAncestor(root.right, p, q)

if left_found is None:
return right_found
if right_found is None:
return left_found
return root
35 changes: 35 additions & 0 deletions 0236.Lowest-Common-Ancestor-of-a-Binary-Tree/step2_top_down.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Definition for a binary tree node.
class TreeNode:
def __init__(self, x):
self.val = x
self.left = None
self.right = None


class Solution:
def lowestCommonAncestor(
self, root: TreeNode, p: TreeNode, q: TreeNode
) -> TreeNode:
def contain(node: TreeNode, target: TreeNode) -> bool:
if node is None:
return False
if node == target:
return True
return contain(node.left, target) or contain(node.right, target)

def lowest_common_ancester_helper(
node: TreeNode, target1: TreeNode, target2: TreeNode
):
if node == target1 or node == target2:
return node

left_contain_1 = contain(node.left, target1)
left_contain_2 = contain(node.left, target2)

if left_contain_1 and left_contain_2:
return lowest_common_ancester_helper(node.left, target1, target2)
if not (left_contain_1 or left_contain_2):
return lowest_common_ancester_helper(node.right, target1, target2)
return node

return lowest_common_ancester_helper(root, p, q)