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

JavaScriptで二分探索木にキーを挿入する方法【反復・再帰の両方を解説】

二分木への挿入の基本

新しく作成した二分木への最初の挿入では、ノードがルートとして配置されます。2回目以降の挿入では、二分探索木(BST)の特性に従ってノードが配置されます。つまり、左の子は親より小さい値右の子は親より大きい値を持つというルールです。

ここでは、このアルゴリズムをコードで実装する方法を、反復(イテレーティブ)と再帰の2つのアプローチで解説します。

反復版の挿入メソッド

insertIter(data) {
    let node = new this.Node(data);

    // ツリーが空かどうかを確認
    if (this.root === null) {
        // 最初の要素として挿入
        this.root = node; return;
    }

    let currNode = this.root;
    while (true) {
        if (data < currNode.data) {
            // 葉ノードに到達したので、ここに値をセット
            if (currNode.left === null) {
                currNode.left = node;
                break;
            } else {
                currNode = currNode.left;
            }
        } else {
            // 葉ノードに到達したので、ここに値をセット
            if (currNode.right === null) {
                currNode.right = node;
                break;
            } else {
                currNode = currNode.right;
            }
        }
    }
}

処理の流れ

この関数の動作を順に見ていきましょう。

まず、ルートがnullかどうかを確認します。nullであればツリーが空であることを意味するため、新しいノードをルートとして代入すれば完了です。

空でない場合は、currNode変数を作成してルートを指させます。その後、挿入するデータがcurrNodeの値より小さいかどうかを判定します。小さい場合は左の子がnullかどうかを確認し、nullであればそこにデータを配置して終了します。nullでなければ、さらに左の子へ移動して処理を繰り返します。

これを葉ノードに到達するまで繰り返し、最終的にその位置にデータを配置します。

反復版の実行例

let BST = new BinarySearchTree();
BST.insertIter(10);
BST.insertIter(15);
BST.insertIter(5);
BST.insertIter(50);
BST.insertIter(3);
BST.insertIter(7);
BST.insertIter(12);

再帰版の挿入メソッド

同じ処理は再帰を使っても実装できます。ツリーは本質的に再帰的な構造であるため、この特性を活かすのは非常に自然なアプローチです。以下が再帰版のinsertメソッドです。

insertRec(data) {
    let node = new this.Node(data);

    // ツリーが空かどうかを確認
    if (this.root === null) {
        // 最初の要素として挿入
        this.root = node;
    } else {
        insertRecHelper(this.root, node);
    }
}

ヘルパー関数について

再帰処理を行うためのヘルパー関数が必要ですが、この関数をクラスのプロパティとして公開したくないため、クラス定義の外に配置します。

function insertRecHelper(root, node) {
    if (node.data < root.data) {
        // 葉ノードに到達したので、ここに値をセット
        if (root.left === null) {
            root.left = node;
        } else {
            // 左部分木を引数にして再帰的に呼び出し
            insertRecHelper(root.left, node);
        }
    } else {
        // 葉ノードに到達したので、ここに値をセット
        if (root.right === null) {
            root.right = node;
        } else {
            // 右部分木を引数にして再帰的に呼び出し
            insertRecHelper(root.right, node);
        }
    }
}

再帰版の実行例

let BST = new BinarySearchTree();
BST.insertRec(10);
BST.insertRec(15);
BST.insertRec(5);
BST.insertRec(50);
BST.insertRec(3);
BST.insertRec(7);
BST.insertRec(12);

まとめ

二分探索木へのキー挿入は、反復でも再帰でも実装できます。反復版はwhileループで葉ノードまで辿るためスタックオーバーフローの心配がなく、再帰版はコードが簡潔でツリーの構造と相性が良いという特徴があります。用途やツリーの深さに応じて使い分けるとよいでしょう。

  1. JavaScriptの木構造における通りがけ順(In-order)走査の解説

    通りがけ順走査(In-order Traversal)とは通りがけ順走査は、木構造の走査方法のひとつで、左部分木 → 根(ルート) → 右部分木 の順にノードを訪問します。ここで重要なのは、すべてのノードがそれ自体ひとつの部分木とみなせるという点です。つまり、各ノードに対して同じルールを再帰的に適用していくことになります。二分探索木(BST)を通りがけ順で走査すると、キーの値が昇順にソートされた状態で出力されるという特徴があります。これは二分探索木の非常に便利な性質のひとつです。走査の流れまず A から出発し、通りがけ順のルールに従って左部分木の B へ移動します。B に対しても同様に通りがけ

  2. JavaScriptで実装するプリム法:最小全域木を求めるアルゴリズムの基本と実装例

    プリム法(Prims Algorithm)とはプリム法は、重み付き無向グラフから最小全域木(MST: Minimum Spanning Tree)を求めるための貪欲法(グリーディアルゴリズム)です。グラフ内のすべての頂点を含み、かつ辺の重みの合計が最小になるような辺の部分集合(木)を見つけ出します。アルゴリズムは、任意の開始頂点から木の構築を始め、1ステップごとに「木に属する頂点」と「木に属さない頂点」をつなぐ辺の中から、最もコスト(重み)の小さいものを1本追加していくことで動作します。プリム法の動作の流れ以下の図を使って、プリム法がどのように動作するのかを順番に見ていきましょう。ステップ1: