Pythonで二分探索木(BST)をシリアライズ・デシリアライズする方法
シリアライズとデシリアライズとは?
本記事では、二分探索木(BST:Binary Search Tree)をシリアライズおよびデシリアライズするアルゴリズムをPythonで設計する方法を解説します。
シリアライズ(直列化)とは、データ構造やオブジェクトをビット列や文字列へ変換し、ファイルやメモリバッファへの保存、あるいはネットワーク越しの送信を可能にする処理のことです。一方、デシリアライズ(逆直列化)は、シリアライズされたデータから元のデータ構造を復元する逆のプロセスを指します。
例として、次のような二分木を考えてみます。
[5, 2, 9, 1, 3, 7]
この場合、処理結果は以下のようになります。
- シリアライズ結果: 5.2.9.1.3.7.N.N.N.N.N.N.N
- デシリアライズ結果(中順走査): 1, 2, 3, 5, 7, 9,
ここでは「N」がnull(存在しないノード)を表し、各ノードの値はドット(.)で区切られています。この形式により、木の構造全体をひとつの文字列として正確に表現できます。
アルゴリズムの考え方
この問題は、幅優先探索(BFS:レベル順走査)を利用することで効率的に解けます。木を上から下へ、同じレベル内では左から右へと辿りながら、各ノードの情報を順番に記録していきます。
serialize() の手順
- 引数として根ノード
rootを受け取るserialize()関数を定義します。 - 結果を格納するための新しいリスト
resを用意します。 - キューを1つ作成し、
rootを挿入します。 - キューが空になるまで、以下を繰り返します。
- キューの先頭要素を
currentとして取り出し、resの末尾に追加します。 currentがnullの場合は、そこでループを終了します。current.leftが存在すればキューに追加し、存在しなければNoneを追加します。current.rightについても同様の処理を行います。
- キューの先頭要素を
- 空文字列
sを用意し、resの各要素を先頭から走査します。- 要素が有効なノードであれば、その値(
data)をsに連結します。 - nullであれば、代わりに「N」を連結します。
- 最後の要素以外の後ろには、区切り文字「.」を連結します。
- 要素が有効なノードであれば、その値(
- 完成した文字列
sを返します。
deserialize() の手順
- 文字列
dataを受け取るdeserialize()関数を定義します。 dataをドット(.)で分割し、要素のリストに変換します。- ノード管理用のスタック(リスト)を新しく用意します。
data[0]が「N」であれば、空の木を意味するためNoneを返します。data[0]の値を持つ新しいノードをrootとして作成し、スタックに追加します。- インデックス
i = 1、current = 0とし、iがdataの長さに達するまで以下を繰り返します。data[i]が「N」でなければ新しいノードを作成し、stack[current]の左の子として設定したうえでスタックに追加します。「N」であれば左の子をNoneとします。- 続く要素(右の子に相当)についても同様の処理を行います。
currentとiをそれぞれ1ずつ進めます。
- 復元した
rootを返します。
Pythonでの実装例
理解を深めるために、実際の実装コードを見ていきましょう。
class TreeNode:
def __init__(self, data, left=None, right=None):
self.data = data
self.left = left
self.right = right
def insert(temp, data):
que = []
que.append(temp)
while len(que):
temp = que[0]
que.pop(0)
if not temp.left:
if data is not None:
temp.left = TreeNode(data)
else:
temp.left = TreeNode(0)
break
else:
que.append(temp.left)
if not temp.right:
if data is not None:
temp.right = TreeNode(data)
else:
temp.right = TreeNode(0)
break
else:
que.append(temp.right)
def make_tree(elements):
Tree = TreeNode(elements[0])
for element in elements[1:]:
insert(Tree, element)
return Tree
def print_tree(root):
# 中順走査(inorder traversal)で表示
if root is not None:
print_tree(root.left)
print(root.data, end=', ')
print_tree(root.right)
class Codec:
def serialize(self, root):
res = []
queue = [root]
while queue:
while True and queue:
current = queue[0]
res.append(current)
queue.pop(0)
if current:
break
if not current:
break
if current.left:
queue.append(current.left)
else:
queue.append(None)
if current.right:
queue.append(current.right)
else:
queue.append(None)
s = ""
for i in range(len(res)):
if res[i]:
s += str(res[i].data)
else:
s += "N"
if i == len(res) - 1:
break
s += "."
return s
def deserialize(self, data):
data = data.split(".")
stack = []
if data[0] == 'N':
return None
root = TreeNode(int(data[0]))
stack.append(root)
i = 1
current = 0
while i < len(data):
if data[i] != 'N':
temp = TreeNode(int(data[i]))
stack[current].left = temp
stack.append(temp)
else:
stack[current].left = None
i += 1
if data[i] != 'N':
temp = TreeNode(int(data[i]))
stack[current].right = temp
stack.append(temp)
else:
stack[current].right = None
current += 1
i += 1
return root
ob = Codec()
root = make_tree([5, 2, 9, 1, 3, 7])
ser = ob.serialize(root)
print('Serialization:', ser)
print_tree(ob.deserialize(ser))
実行結果
入力
[5, 2, 9, 1, 3, 7]
出力
Serialization: 5.2.9.1.3.7.N.N.N.N.N.N.N 1, 2, 3, 5, 7, 9,
計算量について
このアルゴリズムでは、各ノードを一度だけ訪問するため、シリアライズ・デシリアライズともに時間計算量は O(n) です。また、キューとスタックが最大で木の全ノード分の参照を保持しうるため、空間計算量も O(n) となります。ノード数に比例して処理が線形に増加するため、大規模な木に対しても安定した性能が期待できます。
-
Pandasで学ぶPythonデータ分析と可視化の基本
Pandas(パンダス)は、データサイエンスやデータ分析の分野で最も人気のあるPythonライブラリの一つです。データの操作・分析・クレンジング(前処理)を得意としており、C言語で実装された低レベルライブラリ「NumPy」の上位に位置する高レベルな抽象化として機能します。本記事では、アナリストやデータサイエンティストとして必ず押さえておきたい重要ポイントを順を追って解説します。ライブラリのインストール必要なライブラリはpipコマンドで簡単にインストールできます。ターミナル(コマンドプロンプト)で以下のコマンドを実行してください。pip install pandasDataFrame と Ser
-
Pythonによるデータ分析と可視化入門|pandasの基本を徹底解説
Pythonによるデータ分析と可視化の概要 Pythonには、データ分析と可視化のための強力なライブラリが数多く用意されています。代表的なものとしては、NumPy、pandas、matplotlib、seabornなどが挙げられます。本記事では、その中でも中心的な役割を果たすpandasについて詳しく解説します。pandasはNumPyをベースに構築されたオープンソースライブラリで、高速なデータ分析やデータのクリーニング・前処理を効率的に行えます。さらに、pandas自体にも便利な可視化機能が組み込まれています。 pandasのインストール方法 pandasをインストールするには、ターミナル