再帰で並びから高さのそろった二分探索木を作る

小さい​順に​並んだ​数から、​左右の​高さが​そろった​二分探索木を​作って​返します。​まん中を​根に​して​左右を​同じ形で​作る、​分けて​解く​再帰の​基本の​練習です。

海外で定番の問題リスト 4 本のうち 1 本に入っています (LeetCode Top Interview 150)

問題

小さい順に並んだ整数の並び nums を受け取ります。この数を全部 1 つずつ使って二分探索木を作り、その根を返す関数を書いてください。どの節でも、左の部分木と右の部分木の高さの差が 1 以下になるようにします。

  • 二分探索木は、どの節でも、左の部分木の値は全部その節より小さく、右の部分木の値は全部その節より大きい木です
  • 作り方は 1 通りに決めます。いま使う範囲の左端の番号と右端の番号を足して 2 で割り、端数を切り捨てた番号の値を根にします。その左側の範囲から左の部分木を、右側の範囲から右の部分木を、同じ決め方で作ります
  • 新しい節は、Python では TreeNode(値)、JavaScript では new TreeNode(値)、Go では &TreeNode{} で作れます。どれも val (値)、left (左の子)、right (右の子) を持ち、子は最初は空です。Go では Val、Left、Right と大文字で始まります
  • 数の個数は 1 以上 500 以下、値はどれも -1000 以上 1000 以下で、同じ値はありません

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

入力と出力の例

例 1

7 つの数です。まん中の 11 が根になり、左の 3 つと右の 3 つからそれぞれ同じ形の木ができます。

入力

nums = [2, 5, 8, 11, 14, 17, 20]

出力

[11, 5, 17, 2, 8, 14, 20]

例 2

5 つの数です。左の範囲は 2 つしかないので、切り捨てで左寄りの -10 が根になり、-3 はその右の子になります。右の範囲の 5 と 9 も同じです。

入力

nums = [-10, -3, 0, 5, 9]

出力

[0, -10, 5, null, -3, null, 9]

例 3

2 つだけの数です。まん中の候補は 2 つありますが、切り捨てで左の 3 が根になり、6 はその右の子です。

入力

nums = [3, 6]

出力

[3, null, 6]

面接での聞かれ方

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

  • 「小さい順の並びから、高さの釣り合った二分探索木を作ってください」
  • 書き終えたあとに「なぜまん中を根にすると高さがそろうのですか?」と理由を聞かれます
  • 「並びではなく、小さい順の連結リストで渡されたらどうしますか?」と形を変えて聞かれることもあります

まん中が 2 つあるときにどちらを根にするかで答えの木が変わるので、最初に決め方を口に出しておきます。

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

main.py
def sorted_to_bst(nums):
    # ここに書きます
    pass


# 「実行」を押すと、この行の結果が出ます
print(sorted_to_bst([2, 5, 8, 11, 14, 17, 20]))

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