Skip to content

Conversation

garunitule
Copy link
Owner

# self.val = val
# self.left = left
# self.right = right
class Solution:

Choose a reason for hiding this comment

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

参考までに、私はこう書きますね

class Solution:
    def maxDepth(self, root: TreeNode | None) -> int:
        if root is None:
            return 0

        left_max_depth = self.maxDepth(root.left)
        right_max_depth = self.maxDepth(root.right)
        return max(left_max_depth, right_max_depth) + 1

(曖昧な表現ですが)すごい人のコードを読んでいると、if文が少ないことに気づき、私も気をつけて真似するようにしています。おそらくですが、部分問題の脳内フローチャートをできるだけシンプルにしているのだと思います。

少し昔に、Linus Torvaldsさんがどこかのスピーチで、問題の捉え方を変えることで条件分岐を減らすことが言及されていました。
https://medium.com/@bartobri/applying-the-linus-tarvolds-good-taste-coding-requirement-99749f37684a

また、本を読んでいて、Cyclomatic Complexityという概念に出会いました。コードがどれくらい複雑さを表す指標で、こちらを意識すると自然と条件分岐、例えばif、の数を減らそうという働きがあるように思います。

参考まで。この私の見方が良いものなのか悪いものなのか、フィードバックを受けたことはありません。

Choose a reason for hiding this comment

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

huyfififi さんのコードはシンプルで読みやすいですが現状のコードとの距離があると思うので、その間を埋めてみようと思います。長くなってしまいますが、ご参考になれば幸いです。
まず、left と right は対称に扱った方がわかりやすいです。

        def get_max_depth(node: TreeNode, depth: int) -> int:
            left_depth, right_depth = 0, 0
            if node.left:
                left_depth = get_max_depth(node.left, depth + 1)
            if node.right:
                right_depth = get_max_depth(node.right, depth + 1)
            return max(depth, left_depth, right_depth)

ここから本題で、おそらくなんですが、再帰のパターンの選択が問題かなという気がしました。
get_max_depth はノードとその深さを入力にとります。子がいない場合は深さを変更せず確定させ、子がいる場合は、深さを +1 して再帰します。深さを書いた紙を部下に手渡していく感じでしょうか。過去のPRも見ていますが、garunitule さんはこのパターンがファーストチョイスになっている気がします。上から処理する方法で stack で書くと自然な考え方です。
別の再帰の仕方として、再帰する関数 (depth_of_subtree) にノードのみを入力するパターンもあります。子がいない場合はそのノードの深さは 1 になり、子がいる場合は、深い方+1 になります。部下からは報告を受けるだけです。下から処理する方法で再帰の場合こちらがわかりやすいと思います。一旦もとの root は忘れて部分木の深さを求めているとも言えます。

    def maxDepth(self, root: Optional[TreeNode]) -> int:
        if root is None:
            return 0

        def depth_of_subtree(node: TreeNode) -> int:
            left_depth, right_depth = 0, 0
            if node.left is not None:
                left_depth = depth_of_subtree(node.left)
            if node.right is not None:
                right_depth = depth_of_subtree(node.right)
            return max(left_depth, right_depth) + 1
        
        return depth_of_subtree(root)

さらに、再帰する関数で None を入力することを許容し、それは結局 maxDepth と同じだと気がつくと huyfififi さんのコードになります。

Copy link
Owner Author

Choose a reason for hiding this comment

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

@huyfififi
シンプルで分かりやすいですね

Mediumの記事すごく面白かったです
何となくコーディング練習を通じて感じていたことに近い気がします
Arai60で色んなアルゴリズムの問題を解いていますが、結局プログラムは繰り返し・条件分岐・関数呼び出しの組み合わせでしかなく、綺麗なコードは自然に言語化できるように自然な形で概念化されているような気がしています

自然な概念化はこだわっていたのですが、シンプルさにもこだわっていくとより自然になると感じました、共有ありがとうございます!

Copy link
Owner Author

Choose a reason for hiding this comment

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

@tokuhirat
丁寧にありがとうございます
上から値を渡していく方法と、下から値を返す方法があるということですね
おっしゃる通り、再帰のときは上から渡していく方法がファーストチョイスになっていて下から値を返す方法を検討できていませんでした
選択肢として下から値を返す方法も選択肢として考えるようにします

Copy link
Owner Author

Choose a reason for hiding this comment

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

今回上から渡す方法を選択すると、depthを渡す必要があってもう一つ関数が必要になるのと、root is Noneのときに0を返すかdepthを返すかの2パターンあってどうしても複雑になりますね

以下、コメントを拝見したあとに自分て実装してみたコードです
再帰、上から渡す

class Solution:
    def maxDepth(self, root: Optional[TreeNode]) -> int:
        if root is None:
            return 0

        def depth_of_subtree(node: TreeNode, depth: int) -> int:
            if root is None:
                return depth
            
            if node.left is not None:
                depth = max(depth, self.depth_of_subtree(node.left, depth + 1))
            if node.right is not None:
                depth = max(depth, self.depth_of_subtree(node.right, depth + 1))
            return depth
        return depth_of_subtree(root, 1)

再帰、下から返す

class Solution:
    def maxDepth(self, root: Optional[TreeNode]) -> int:
        if root is None:
            return 0

        left_length = self.maxDepth(root.left)
        right_length = self.maxDepth(root.right)
        return max(left_length, right_length) + 1

@garunitule garunitule merged commit 69b111d into main Jul 20, 2025
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.

3 participants