Menu
Coddy logo textTech

二分木

最終更新

二分木は、各ノードが最大2つの子(左の子と右の子)を持つ階層構造です。最上位のノードが根、子を持たないノードが葉で、根から最も深い葉までの辺の数が木の高さです。二分探索木とは異なり、単純な二分木には順序の規則がなく、形状だけを表します。上の再生ボタンを押すと、木がレベルごとに埋まっていき、その後中間順(左・ノード・右)にたどられる様子を見られます。

二分木は多くの構造の基礎になっています。二分探索木、ヒープ、式木などです。走査は各ノードを定められた順序で訪れます。中間順・前順・後順の3つが深さ優先の走査で、それぞれ異なる用途に役立ちます。

用語

用語意味
親を持たない最上位のノード
子を持たないノード
高さ根から葉までの最長経路(辺の数)
深さあるノードの根からの距離
完全最後のレベルを除くすべてのレベルが埋まり、最後は左から右へ埋められる

3つの深さ優先走査

走査順序よくある用途
中間順左・ノード・右BSTのソート済み出力
前順ノード・左・右木のコピー / シリアライズ
後順左・右・ノード木の削除 / 評価

具体例

[4, 2, 6, 1, 3, 5] から構築した(レベルごとに埋めた)木の中間順走査:

ステップ対象ノード動作
144 を訪れる前に、その左部分木へ再帰する
222 を訪れる前に、その左部分木へ再帰する
31葉で左の子なし: 1 を訪れ、出力 [1]
42左が完了: 2 を訪れ、出力 [1, 2]、次に右へ再帰
53葉: 3 を訪れ、出力 [1, 2, 3]
64左部分木が完了: 4 を訪れ、出力 [1, 2, 3, 4]、次に右へ再帰
756 の左の子で葉: 5 を訪れ、出力 [1, 2, 3, 4, 5]
86左が完了で右の子なし: 6 を訪れ、出力 [1, 2, 3, 4, 5, 6]

二分木を使うべきとき

使うべきとき避けるべきとき
本質的に階層的なデータ(ファイルシステム、式木、DOM)をモデル化する必要があるときデータが平坦でリストや配列で足りるとき。木はオーバーヘッドを増やすだけ
順序付き操作が必要で、平衡を保てるとき(BSTや自己平衡木)キーによる平均 O(1) の探索が必要なとき。ハッシュテーブルがどの木よりも優れる
構造化データを中間順・前順・後順で処理する必要があるとき非平衡なBSTにソート済み順でノードを挿入するとき。O(n) のリストに退化する
範囲クエリや順序付き反復が重要なとき。ハッシュテーブルでは実現できないメモリが逼迫しているとき。各ノードはデータに加えて2つの子ポインタを持つ

Binary Treeのコード

Python, JavaScript, Java, C++, Cによるクリーンで実行可能なBinary Treeの実装です。言語を選んでコードをコピーするか、Coddyプレイグラウンドに読み込んだ状態で開けます。

PythonでのBinary Treeのコード

Python
1from collections import deque2
3
4class Node:5    def __init__(self, value):6        self.value = value7        self.left = None8        self.right = None9
10
11def insert(root, value):12    # Level-order insert: fill the first empty child slot found13    if root is None:14        return Node(value)15    queue = deque([root])16    while queue:17        node = queue.popleft()18        if node.left is None:19            node.left = Node(value)20            return root21        queue.append(node.left)22        if node.right is None:23            node.right = Node(value)24            return root25        queue.append(node.right)26
27
28def inorder(node):29    if node is None:30        return []31    return inorder(node.left) + [node.value] + inorder(node.right)32
33
34root = None35for value in [1, 2, 3, 4, 5, 6, 7]:36    root = insert(root, value)37
38print("Root:   ", root.value)39print("Inorder:", inorder(root))
このコードをPythonプレイグラウンドで実行

二分木のよくある質問

二分木と二分探索木の違いは何ですか?
二分木は各ノードを最大2つの子に制限するだけで、順序はありません。二分探索木は、左部分木のすべてが小さく、右部分木のすべてが大きいという規則を加えます。これが探索を高速にします。
中間順走査とは何ですか?
中間順走査は、再帰的に左部分木、次にノード、次に右部分木を訪れます。二分探索木ではこれにより値が昇順で得られるため、最もよく示される走査です。
完全二分木とは何ですか?
完全二分木は、最後のレベルを除くすべてのレベルが完全に埋まり、最後は左から右へ埋められます。この形状により、木を配列にコンパクトに(子ポインタなしで)格納でき、二分ヒープはこの方法で実装されます。
配列やハッシュテーブルではなく二分木を使うべきなのはいつですか?
データが自然に階層的であるとき、または範囲クエリやソート済み反復のような順序付き操作が必要なとき(ハッシュテーブルには提供できない)に木を選びます。順序不要でキーの高速探索だけが必要なら、ハッシュテーブルの平均 O(1) アクセスが木を上回り、平坦なデータには配列の方が単純でキャッシュにも優しいです。
二分木の高さと深さの違いは何ですか?
深さは根から特定のノードまで下向きに測る値で、根からそのノードへの経路上の辺の数です。高さはあるノードから最も深い葉まで下向きに測るので、木全体の高さは最も深いノードの深さになります。単一ノードの木は高さ 0 で、その唯一のノードの深さも 0 です。
なぜ非平衡な二分木は性能が悪いのですか?
二分探索木での探索・挿入・削除は、高さを h として O(h) かかります。キーをソート済み順で挿入すると木は一直線になり、hn まで伸びて各操作は O(n) に退化します。連結リストと変わりません。AVL木や赤黒木のような自己平衡木は、これを防ぐために高さを O(log n) に保ちます。
Coddy programming languages illustration

Coddy でアルゴリズムをマスターしよう

始める