前順の走査で二分木を文字列にして、文字列から同じ木に組み直す

二分木を 1 本の文字列に直す serialize と、その文字列から同じ木を組み直す deserialize を書きます。空の子にも印を付けて前順で並べ、読むときも同じ順で消費する再帰の練習です。

海外で定番の問題リスト 4 本には入っていません

問題

二分木を保存や送信のために 1 本の文字列に直す関数 serialize と、その文字列から元と同じ木を組み直す関数 deserialize を書いてください。2 つが正しいかを確かめるため、二分木の根 root を受け取る関数 tree_codec (JavaScript と Go では treeCodec) も書きます。tree_codec は、root を serialize で文字列にし、その文字列を deserialize で木に組み直し、組み直した木をもう一度 serialize した文字列を返します。

  • 文字列の形は次の 1 つに決めます。根、左の部分木、右の部分木の順 (前順) に節を読み、値をそのまま数字で書きます。子が無いところは # と書きます。項目どうしは , で区切り、空白は入れません
  • 空の木は # だけです
  • 各節は val (値)、left (左の子)、right (右の子) を持ちます。子が無いところは、Python では None、JavaScript では null、Go では nil です。Go の節は TreeNode で、Val、Left、Right と大文字で始まります。新しい節は TreeNode(5) (JavaScript は new TreeNode(5)、Go は &TreeNode{Val: 5}) で作れます
  • 節の数は 0 以上 300 以下、値はどれも -1000 以上 1000 以下です

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

入力と出力の例

例 1

根が 5、その下が 3 と 8、3 の右に 4 の木です。前順に読むと、5、3、(3 の左は空) #、4、(4 の左右は空) #、#、8、(8 の左右は空) #、# です。

root = [5, 3, 8, null, 4]
"5,3,#,4,#,#,8,#,#"

例 2

空の木は # 1 つです。

root = []
"#"

例 3

負の値も、そのまま - を付けて書きます。根が -7、右に 12、12 の左に 9 です。

root = [-7, null, 12, 9]
"-7,#,12,9,#,#,#"

面接での聞かれ方

木の走査と、データの形を自分で決める力を見る問題として、シニアの面接でよく出ます。

  • 「二分木を文字列にして、元に戻せるようにしてください。形は自由です」
  • 形が自由なので、「なぜその形で元に戻せると言えますか?」と理由を聞かれます。空の子の印が無いと、前順だけでは形が決まりません
  • 「段の順 (BFS) でも書けますか?」「二分探索木なら印を省けますか?」と続くことがあります。二分探索木は値の大小で形が決まるので、前順の値だけで組み直せます

シニアの問題なので、書く側と読む側が同じ順で動くことを説明し、端の場合 (空の木、負の値、1 本道) まで扱えるかが見られます。

ヒント

まだ開いていません

付箋は 4 枚。下の段ほど答えに近づきます

  1. ヒント 2

  2. ヒント 3

  3. ヒント 4答えに近い

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

再帰は、同じ形の小さい問題に分けて自分を呼ぶ書き方です。木の深さや、全部の組み合わせを数える問題で使います。

main.py
def tree_codec(root: TreeNode | None) -> str:
    """
    Goal: 何を返すか

    idea1: まず思いつく案
    -> runtime: O(?)  space: O(?)

    idea2: もっと速い案
    -> runtime: O(?)  space: O(?)

    edge case: 空、1 つだけ、など
    """
    # root の節は val、left、right を持ちます。空きは None です
    # ここに書きます
    pass


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

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