Pythonで実装する二分木のジグザグレベル順走査(Zigzag Level Order Traversal)
二分木が与えられたとき、そのジグザグレベル順走査(Zigzag Level Order Traversal)の結果を求める問題を考えます。これは、第1レベルは左から右へ、第2レベルは右から左へ、第3レベルは再び左から右へ……というように、階層ごとに走査の向きを交互に切り替えながらノードを訪問する手法です。
例として、次のような二分木を扱います。

この木に対する走査結果は [[3], [20, 9], [15, 7]] になります。ルートの 3 を含む第1レベル、右から左へ読む第2レベル(20, 9)、そして左から右へ読む第3レベル(15, 7)という具合です。
アルゴリズムの流れ
キュー(FIFO)と走査方向を表すフラグを使い、レベルごとに次の手順で処理を進めます。
- 木が空の場合は、空のリストを返します。
- キューを作成してルートを追加し、各レベルのノードを保持する
res、最終結果となる値のリストres2、走査方向を示すフラグflag = Trueを用意します。 - キューが空になるまで、以下を繰り返します。
- 現在キューに入っているノードの一覧を
resに、それらの値をres2に追加します。 flagがTrueの場合(次のレベルを右から左へ読む):現在のレベルを末尾のノードから順に調べ、右の子 → 左の子の順で新しいキューに追加します。flagがFalseの場合(次のレベルを左から右へ読む):同様に末尾のノードから順に、今度は左の子 → 右の子の順で追加します。- キューを新しいレベルのノードで置き換え、
flagを反転させます。
- 現在キューに入っているノードの一覧を
- すべてのレベルを処理し終えたら、
res2を返します。
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 = [temp]
while que:
temp = que.pop(0)
if not temp.left:
temp.left = TreeNode(data if data is not None else 0)
break
que.append(temp.left)
if not temp.right:
temp.right = TreeNode(data if data is not None else 0)
break
que.append(temp.right)
def make_tree(elements):
# リストから二分木を構築する
tree = TreeNode(elements[0])
for element in elements[1:]:
insert(tree, element)
return tree
class Solution(object):
def zigzagLevelOrder(self, root):
if not root:
return []
queue = [root]
res = [] # 各レベルのノードオブジェクトを保持
res2 = [] # 最終的に出力する値のリスト
flag = True # True: 次のレベルは右から左へ
while queue:
res.append(list(queue))
res2.append([node.data for node in queue if node.data != 0])
next_queue = []
if flag:
# 末尾のノードから順に、右の子→左の子の順で追加
for node in reversed(queue):
if node.right:
next_queue.append(node.right)
if node.left:
next_queue.append(node.left)
else:
# 末尾のノードから順に、左の子→右の子の順で追加
for node in reversed(queue):
if node.left:
next_queue.append(node.left)
if node.right:
next_queue.append(node.right)
queue = next_queue
flag = not flag
return res2
ob = Solution()
tree = make_tree([3, 9, 20, None, None, 15, 7])
print(ob.zigzagLevelOrder(tree))
なお、この実装では存在しないノードを値 0 のダミーノードとして表現しているため、結果を組み立てる際に data != 0 の条件でダミーを除外しています。
入力
[3,9,20,null,null,15,7]
出力
[[3], [20, 9], [15, 7]]
計算量
各ノードをちょうど一度ずつ処理するため、時間計算量は O(n) です。また、各レベルのノードをキューとリストに保持する必要があるため、空間計算量も O(n) となります(n はノードの総数)。
-
Pythonで二分木の直径を求める方法【DFSを使った実装解説】
二分木の直径とは二分木が与えられたとき、その木の直径(diameter)を計算することを考えます。二分木の直径とは、木の中の任意の2つのノードをつなぐ最長経路の長さのことです。重要なポイントとして、この経路は必ずしも根(ルート)を通るとは限りません。例えば、次のような木を考えてみましょう。この場合、経路 [4, 2, 1, 3] または [5, 2, 1, 3] の長さが3本の辺で構成されているため、直径は3となります。解法のアプローチこの問題はDFS(深さ優先探索)を使うことで効率的に解くことができます。手順は以下の通りです。DFSで各ノードを訪問しながら直径を求めます。まず答えを格納する変
-
Pythonで二分木を反転する方法:再帰を使った実装を解説
二分木の反転とは二分木が与えられたとき、その左右の子ノードを入れ替えて「鏡像」のような木を作ることを二分木の反転(Invert Binary Tree)と呼びます。これはアルゴリズムの学習やコーディング面接でも頻出のトピックです。例えば、次のような二分木があったとします。これを反転すると、すべてのノードの左部分木と右部分木が入れ替わり、以下のような木になります。解き方:再帰的アプローチこの問題は再帰を使うと非常にシンプルに解けます。考え方は以下の3ステップです。ルートが None(null)であれば、そのまま返す(ベースケース)現在のノードの左ポインタと右ポインタを入れ替える左部分木と右部分木