Skip to content

Lowest Common Ancestor Of A Binary Tree#111

Open
tom4649 wants to merge 3 commits into
mainfrom
236.Lowest-Common-Ancestor-of-a-Binary-Tree
Open

Lowest Common Ancestor Of A Binary Tree#111
tom4649 wants to merge 3 commits into
mainfrom
236.Lowest-Common-Ancestor-of-a-Binary-Tree

Conversation

@tom4649
Copy link
Copy Markdown
Owner

@tom4649 tom4649 commented May 26, 2026

if node == q:
found_q = True
if found_p and found_q:
break
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ここで next_frontier = [] をすると、ちょっとだけ計算量が減りますかね。

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

この break では for 文の処理を抜けるので、while 文の内側に戻ってしまうというご指摘ですね。
意図が明確になるように、 while 文の条件式を while frontier and not (found_p and found_q): とする形で修正しました。

depth += 1
frontier = next_frontier

depth_p, parent_p = node_to_depth_and_parent[p]
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

node_to_depth_and_parentは全量で子に対する親を入れることになりますが、BFSではなくDFSでやると祖先を一つのスタックで管理できるので、p,qが見つかった時点でそれぞれのスタックのコピーを作り、そのコピーたちの共通部分を取ることで見つけることができ、この辺のコードは少し簡単になります。

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DFSとバックトラックを用いた解法をstep2_hybrid_backtrack.pyに追加しました。ロジック(DFSとBFS以外)は同じですが、こちらの方が確かにシンプルだと思います。

class Solution:
def lowestCommonAncestor(
self, root: TreeNode, p: TreeNode, q: TreeNode
) -> TreeNode:
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

この解法自体はシンプルでとても良いと思いますが、rootと戻り値の型がOptional[TreeNode] (TreeNode | None)になりますね。

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

リンクをたどると

この広義の定義と狭義の定義を区別した解法

は見つかったのですが、一応この狭義の定義(というか本来の妥当な定義)をヘルパー関数で実現したものを貼っておきます。(AI生成です)

    def lowestCommonAncestor(
        self, root: TreeNode, p: TreeNode, q: TreeNode
    ) -> TreeNode:

        def find_lca_in_subtree(
            node: TreeNode | None,
        ) -> tuple[TreeNode | None, bool, bool]:
            if node is None:
                return None, False, False

            left_lca, left_has_p, left_has_q = find_lca_in_subtree(node.left)
            if left_lca is not None:
                return left_lca, True, True

            right_lca, right_has_p, right_has_q = find_lca_in_subtree(node.right)
            if right_lca is not None:
                return right_lca, True, True

            subtree_has_p = left_has_p or right_has_p or node is p
            subtree_has_q = left_has_q or right_has_q or node is q

            if subtree_has_p and subtree_has_q:
                return node, True, True

            return None, subtree_has_p, subtree_has_q

        lca_node, _, _ = find_lca_in_subtree(root)
        return lca_node

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rootと戻り値の型がOptional[TreeNode] (TreeNode | None)

注意力が足りていませんでした。

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(狭義のLCA、部分木にpが存在しているか、部分木にqが存在しているか) を返す関数ということですね。理解が深まりました。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants