C++
 Computer >> コンピューター >  >> プログラミング >> C++

C++で親ポインタを使った二分探索木(BST)へのノード挿入方法


二分探索木(BST)に新しいノードを挿入する場合、一般的には再帰的な方法が用いられ、その際に各部分木の根のアドレスを返します。本記事では、もうひとつのアプローチとして、親ポインタを維持しながら挿入を行う方法を紹介します。親ポインタを保持しておくと、特定のノードの祖先をたどったり、中順後続ノード(inorder successor)を求めたりする処理などで非常に役立ちます。

基本的な考え方は、再帰呼び出しによって左部分木・右部分木のアドレスを受け取り、その戻り値のノードに対して親ポインタを設定するというものです。これにより、挿入処理の中ですべての親ポインタが正しく設定されることが保証されます。なお、木の根(ルート)の親ポインタはNULLに設定されます。

アルゴリズム

insert(node, key) −

begin
    if node が NULL なら、key を持つ新しいノードを作成して返す
    if key が node のデータより小さい場合
        left_child := insert(node の左部分木, key)
        node の左ポインタ := left_child
        left_child の親ポインタ := node
    else if key が node のデータより大きい場合
        right_child := insert(node の右部分木, key)
        node の右ポインタ := right_child
        right_child の親ポインタ := node
    end if
    return node
end

実装例(C++コード)

#include<iostream>
using namespace std;
class Node {
    public:
        int data;
        Node *left, *right, *parent;
};
struct Node *getNode(int item) {
    Node *temp = new Node;
    temp->data = item;
    temp->left = temp->right = temp->parent = NULL;
    return temp;
}
void inorderTraverse(struct Node *root) {
    if (root != NULL) {
        inorderTraverse(root->left);
        cout << root->data << " ";
        if (root->parent == NULL)
            cout << "NULL" << endl;
        else
            cout << root->parent->data << endl;
        inorderTraverse(root->right);
    }
}
struct Node* insert(struct Node* node, int key) {
    if (node == NULL) return getNode(key);
    if (key < node->data) { // 左部分木へ挿入
        Node *left_child = insert(node->left, key);
        node->left = left_child;
        left_child->parent = node;
    }
    else if (key > node->data) { // 右部分木へ挿入
        Node *right_child = insert(node->right, key);
        node->right = right_child;
        right_child->parent = node;
    }
    return node;
}
int main() {
    struct Node *root = NULL;
    root = insert(root, 100);
    insert(root, 60);
    insert(root, 40);
    insert(root, 80);
    insert(root, 140);
    insert(root, 120);
    insert(root, 160);
    inorderTraverse(root);
}

実行結果

40 60
60 100
80 60
100 NULL
120 140
140 100
160 140

出力の各行は「ノードの値 親の値」のペアを表しています。例えば「40 60」という行は、値40を持つノードの親が60であることを意味します。木の根である100には親が存在しないため、「NULL」と表示されています。このように中順走査(inorder traversal)の結果を確認することで、すべてのノードの親ポインタが正しく設定されていることが分かります。

計算量と注意点

この挿入操作の時間計算量は、木のバランスが取れている場合は平均O(log n)ですが、ソート済みデータを挿入するなどして木が偏った場合は最悪O(n)になります。また、この実装では既に同じ値が木に存在する場合(key が node のデータと等しい場合)、そのキーは挿入されずに無視される点にも注意してください。


  1. C++で配列を実装した二分木

    二分木は、ツリーの各ノードが最大2つの子ノードを持つことができる特殊なタイプのツリーです。これらの子ノードは、右子および左子と呼ばれます。 単純な二分木は-です 木を表現するには、2つの方法があります。 リンクリストを使用する動的ノード表現 配列を使用する順次表現。 ここでは、二分木の配列表現について説明します。このために、BTのノードに番号を付ける必要があります。この番号付けは、0から(n-1)または1からnまで開始できます。 配列内のノードとその親ノードおよび子ノードの位置を導き出します。 0インデックスベースのシーケンスを使用する場合 親ノードがインデックスpであ

  2. C++の二分探索木(BST)で最小値のノードを見つける方法

    二分探索木(Binary Search Tree、BST)が与えられたとき、その木の中から最小の要素を見つけることを考えます。例えば、以下のようなBSTがあるとします。この場合、最小要素は 1 になります。考え方二分探索木の重要な性質として、左部分木には必ず親ノードより小さい値が格納されるというものがあります。この性質を利用すると、次の手順で最小要素を見つけることができます。ルートノードから探索を開始します。現在のノードの左の子が NULL でない間、左の子へ移動を繰り返します。左の子が NULL になったノードの値が、木全体の中で最小の要素です。この操作の計算量は木の高さに依存し、平衡な二分