From 6fe7978f0bedc8003e0256f1bbd0525e87823d9a Mon Sep 17 00:00:00 2001 From: tom4649 Date: Tue, 26 May 2026 06:44:17 +0900 Subject: [PATCH 1/3] 235. Lowest Common Ancestor of a Binary Search Tree --- .../memo.md | 79 +++++++++++++++++++ .../step1.py | 24 ++++++ .../step1_binary_tree.py | 51 ++++++++++++ .../step2.py | 23 ++++++ 4 files changed, 177 insertions(+) create mode 100644 0235.Lowest-Common-Ancestor-of-a-Binary-Search-Tree/memo.md create mode 100644 0235.Lowest-Common-Ancestor-of-a-Binary-Search-Tree/step1.py create mode 100644 0235.Lowest-Common-Ancestor-of-a-Binary-Search-Tree/step1_binary_tree.py create mode 100644 0235.Lowest-Common-Ancestor-of-a-Binary-Search-Tree/step2.py diff --git a/0235.Lowest-Common-Ancestor-of-a-Binary-Search-Tree/memo.md b/0235.Lowest-Common-Ancestor-of-a-Binary-Search-Tree/memo.md new file mode 100644 index 0000000..808631b --- /dev/null +++ b/0235.Lowest-Common-Ancestor-of-a-Binary-Search-Tree/memo.md @@ -0,0 +1,79 @@ +# 235. Lowest Common Ancestor of a Binary Search Tree + +## step1 + +root: 'TreeNode' + +16mぐらい。BFSで探索し、親ノードを残しておく。 + +元のコードにあった型をクォーテーションで囲むのは前方参照を解決するための、`from __future__ import annotation`以前の方法。TreeNodeのメソッドとして書くことを想定したのだろうか? + +binary **search** treeであったことに解き終わってから気がついた。valを探索に使うことができる。そうなると「二つのvalの間にある、深さ最小のノード」が答えとなる + +## 他の人のコードなど + +> Unreachable なところに raise を書くことですが、ありかもしれませんが、私はそこまで肯定的ではないです。結構微妙なところだと思います。 + +> まず、一般的に、dead code は避けるものです。 +> また、Python の場合、返り値があって到達する場合はあってもなくても同じだが return None を書き、到達しない場合は書かないことで、unreachable かの意図は表現されるはずです。 + +書かない方が望ましいようだ。 + +https://github.com/naoto-iwase/leetcode/pull/66 + +- 3つ以上に一般化するなあどかなり工夫されている +- lower, upper = sorted([p.val, q.val]) の書き方 +- p, q自体を交換するよりわかりやすいかも。 +- pathを保存しているが、自分のparentのみ保存でも良きがする + + + +- コメント + +```python +if lower <= node.val <= upper: + return node +``` +を一番初めに持ってきたほうが明確ではないですかね。 + +- p または q が見つかった場合にはそれを返し、見つからなかった場合には None を返すようなコードで再帰的に処理するイメージです。 + +「root以下にあるp, qのLCAを返す」アルゴリズム。ボトムアップ再帰。 + +```python +class Solution: + def lowestCommonAncestor( + self, + root: TreeNode, + p: TreeNode, + q: TreeNode + ) -> Optional[TreeNode]: + if root == p or root == q: + return root + + left = None + if root.left: + left = self.lowestCommonAncestor(root.left, p, q) + + right = None + if root.right: + right = self.lowestCommonAncestor(root.right, p, q) + + # 左右両方で見つかれば root が LCA + if left and right: + return root + + # 片方だけ見つかった場合、見つかった方を返す + return left if left else right +``` + +https://github.com/ryosuketc/leetcode_grind75/pull/10 + +https://github.com/huyfififi/coding-challenges/pull/10 + +step1_1.pyは上のボトムアップ再帰になっている。 + +> よく考えたら、せっかくBinary Search Treeが与えられているのに、その性質を利用していなかった。 + +問題文を読んだ時の誤解が自分だけではなかったのは少し安心 + diff --git a/0235.Lowest-Common-Ancestor-of-a-Binary-Search-Tree/step1.py b/0235.Lowest-Common-Ancestor-of-a-Binary-Search-Tree/step1.py new file mode 100644 index 0000000..44b6158 --- /dev/null +++ b/0235.Lowest-Common-Ancestor-of-a-Binary-Search-Tree/step1.py @@ -0,0 +1,24 @@ +# 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 p.val > q.val: + p, q = q, p # p.val <= q.val + node = root + while True: + if node.val > q.val: + node = node.left + elif node.val < p.val: + node = node.right + else: + return node + + raise RuntimeError("unreachable") diff --git a/0235.Lowest-Common-Ancestor-of-a-Binary-Search-Tree/step1_binary_tree.py b/0235.Lowest-Common-Ancestor-of-a-Binary-Search-Tree/step1_binary_tree.py new file mode 100644 index 0000000..218d3ec --- /dev/null +++ b/0235.Lowest-Common-Ancestor-of-a-Binary-Search-Tree/step1_binary_tree.py @@ -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 diff --git a/0235.Lowest-Common-Ancestor-of-a-Binary-Search-Tree/step2.py b/0235.Lowest-Common-Ancestor-of-a-Binary-Search-Tree/step2.py new file mode 100644 index 0000000..e48d2d5 --- /dev/null +++ b/0235.Lowest-Common-Ancestor-of-a-Binary-Search-Tree/step2.py @@ -0,0 +1,23 @@ +# 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: + smaller, larger = sorted((p.val, q.val)) + node = root + while True: + if smaller <= node.val <= larger: + return node + if node.val < smaller: + node = node.right + continue + if node.val > larger: + node = node.left + return None From 8214bf330928382d36693018a1f7768a8762f83d Mon Sep 17 00:00:00 2001 From: tom4649 Date: Tue, 26 May 2026 19:20:25 +0900 Subject: [PATCH 2/3] Add C++ --- .../memo.md | 2 ++ .../step2.cpp | 31 +++++++++++++++++++ 2 files changed, 33 insertions(+) create mode 100644 0235.Lowest-Common-Ancestor-of-a-Binary-Search-Tree/step2.cpp diff --git a/0235.Lowest-Common-Ancestor-of-a-Binary-Search-Tree/memo.md b/0235.Lowest-Common-Ancestor-of-a-Binary-Search-Tree/memo.md index 808631b..ef7cbf2 100644 --- a/0235.Lowest-Common-Ancestor-of-a-Binary-Search-Tree/memo.md +++ b/0235.Lowest-Common-Ancestor-of-a-Binary-Search-Tree/memo.md @@ -77,3 +77,5 @@ step1_1.pyは上のボトムアップ再帰になっている。 問題文を読んだ時の誤解が自分だけではなかったのは少し安心 +## C++ +今回は難しいコードではないので動くものは書けた。 diff --git a/0235.Lowest-Common-Ancestor-of-a-Binary-Search-Tree/step2.cpp b/0235.Lowest-Common-Ancestor-of-a-Binary-Search-Tree/step2.cpp new file mode 100644 index 0000000..d118323 --- /dev/null +++ b/0235.Lowest-Common-Ancestor-of-a-Binary-Search-Tree/step2.cpp @@ -0,0 +1,31 @@ +// Definition for a binary tree node. +#include +struct TreeNode { + int val; + TreeNode *left; + TreeNode *right; + TreeNode(int x) : val(x), left(NULL), right(NULL) {} +}; + +class Solution { + public: + TreeNode *lowestCommonAncestor(TreeNode *root, TreeNode *p, TreeNode *q) { + int smaller = std::min(p->val, q->val); + int larger = std::max(p->val, q->val); + TreeNode *node = root; + while (node) { + if (smaller <= node->val && node->val <= larger) { + return node; + } + if (node->val < smaller) { + node = node->right; + continue; + } + if (node->val > larger) { + node = node->left; + continue; + } + } + return nullptr; + } +}; From 5dcce4e900e1c2dce1df9a32f287680944dd9731 Mon Sep 17 00:00:00 2001 From: tom4649 Date: Wed, 27 May 2026 05:53:24 +0900 Subject: [PATCH 3/3] Add memo --- .../memo.md | 8 ++++++-- .../step1_binary_tree.py | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/0235.Lowest-Common-Ancestor-of-a-Binary-Search-Tree/memo.md b/0235.Lowest-Common-Ancestor-of-a-Binary-Search-Tree/memo.md index ef7cbf2..9d31c39 100644 --- a/0235.Lowest-Common-Ancestor-of-a-Binary-Search-Tree/memo.md +++ b/0235.Lowest-Common-Ancestor-of-a-Binary-Search-Tree/memo.md @@ -6,9 +6,13 @@ root: 'TreeNode' 16mぐらい。BFSで探索し、親ノードを残しておく。 -元のコードにあった型をクォーテーションで囲むのは前方参照を解決するための、`from __future__ import annotation`以前の方法。TreeNodeのメソッドとして書くことを想定したのだろうか? +(元のコードにあった型をクォーテーションで囲むのは前方参照を解決するための、`from __future__ import annotation`以前の方法らしい。TreeNodeのメソッドとして書くことを想定したのだろうか?) -binary **search** treeであったことに解き終わってから気がついた。valを探索に使うことができる。そうなると「二つのvalの間にある、深さ最小のノード」が答えとなる +binary **search** treeであったことに解き終わってから気がついた。valを探索に使うことができる。そうなると「二つのvalの間にある、深さ最小のノード」が答えとなる。 + +注:この次の問題がBinary Treeを扱う + +https://leetcode.com/problems/lowest-common-ancestor-of-a-binary-tree/description/?envType=problem-list-v2&envId=rab78cw1 ## 他の人のコードなど diff --git a/0235.Lowest-Common-Ancestor-of-a-Binary-Search-Tree/step1_binary_tree.py b/0235.Lowest-Common-Ancestor-of-a-Binary-Search-Tree/step1_binary_tree.py index 218d3ec..1110873 100644 --- a/0235.Lowest-Common-Ancestor-of-a-Binary-Search-Tree/step1_binary_tree.py +++ b/0235.Lowest-Common-Ancestor-of-a-Binary-Search-Tree/step1_binary_tree.py @@ -10,7 +10,7 @@ class Solution: def lowestCommonAncestor( self, root: TreeNode, p: TreeNode, q: TreeNode ) -> TreeNode: - node_to_depth_and_parent: dict[TreeNode, tuple(int, TreeNode | None)] = {} + node_to_depth_and_parent: dict[TreeNode, tuple[int, TreeNode | None]] = {} depth = 1 frontier = [(root, None)]