再帰で二分木のいちばん長い道の長さを求める

二分木の​中で、​いちばん離れた​ 2 つの​節を​結ぶ​道の​長さを、​辺の​数で​返します。​下から​高さを​返しながら、​ついでに​最長の​道を​数える、​木の​再帰の​練習です。

海外で定番の問題リスト 4 本のうち 2 本に入っています (NeetCode 150、Grind 75)

問題

二分木の根 root を受け取ります。木の中の 2 つの節を結ぶ道のうち、いちばん長い道の長さを返す関数を書いてください。

  • 道は、親と子を結ぶ辺をたどって、同じ節を 2 回通らずに 1 つの節から別の節へ行く経路です。根を通らなくてもかまいません
  • 長さは、道が通る辺の数で数えます (節の数ではありません)。節が 1 つだけの木は 0 です
  • 各節は val (値)、left (左の子)、right (右の子) を持ちます。子が無いところは、Python では None、JavaScript では null、Go では nil です。Go の節は TreeNode で、Val、Left、Right と大文字で始まります
  • 木が空のときは 0 を返します
  • 節の数は 0 以上 500 以下、値はどれも -100 以上 100 以下です

例では、木を上の段から左の順に読んだ値の並びで書きます。子が無いところは null です。

入力と出力の例

例 1

根が 8、その下が 3 と 10 の木です。3 の下に 1 と 6、6 の下に 4 と 7、10 の右に 14 があります。4 から 6、3、8、10 を通って 14 へ行く道が、辺 5 本でいちばん長くなります。

入力

root = [8, 3, 10, 1, 6, null,
  14, null, null, 4, 7]

出力

5

例 2

根の 5 には左の子 2 しかありません。いちばん長い道は 0 から 1、2、7 を通って 9 へ行く辺 4 本で、根の 5 を通りません。根を通る道は、いちばん長くても辺 3 本です。

入力

root = [5, 2, null, 1, 7, 0,
  null, null, 9]

出力

4

例 3

節が 1 つだけの木です。辺が無いので 0 です。

入力

root = [4]

出力

0

面接での聞かれ方

面接では、短く次のように渡されることが多いです。

  • 「二分木の直径、つまりいちばん長い道の長さを返してください」
  • 書き始める前に「長さは辺の数ですか、節の数ですか?」と確かめると、よい印象になります
  • 書き終えたあとに「その道は必ず根を通りますか?」と聞かれ、例 2 のような木で確かめさせられます

辺で数えるか節で数えるかで答えが 1 ずれるので、最初に口に出しておきます。

アルゴリズムのチートシートの「再帰と木」に、​この​型の​要点を​表と​コードで​まとめています。

main.py
def tree_diameter(root):
    # root の節は val、left、right を持ちます。空きは None です
    # ここに書きます
    pass


# 「実行」を押すと、この行の結果が出ます
# build_tree は、例と同じ並びから木を組みます
print(tree_diameter(build_tree([8, 3, 10, 1, 6, None, 14, None, None, 4, 7])))

できているか「例で確かめる」を押すと、問題の例を全部この関数に渡して、通ったかをここに出します