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

C++で二分探索木(BST)にノードを挿入する方法を解説

二分探索木(Binary Search Tree、BST)が与えられたとします。ここでは、挿入したいノードをパラメータとして受け取り、挿入操作を行うメソッドを1つだけ実装します。重要なポイントは、挿入操作を行った後も木がBSTの性質(左の子 < 親 < 右の子)を維持していることです。

例えば、次のようなBSTがあるとします。

C++で二分探索木(BST)にノードを挿入する方法を解説

この木に「5」を挿入すると、BSTの規則に従って適切な位置が探索され、木は次のようになります。

C++で二分探索木(BST)にノードを挿入する方法を解説

解決のためのアプローチ

この問題は、再帰を使うことでシンプルに解くことができます。手順は以下の通りです。

  • insert() という再帰的なメソッドを実装します。引数として挿入する値 v を受け取ります。
  • ルートが null の場合、与えられた値 v を持つ新しいノードを作成し、それをルートとして返します。
  • ルートの値が v より大きい場合、値は左側の部分木に属するため、左部分木に対して再帰的に挿入を行います(root->left = insert(root->left, v))。
  • それ以外の場合(ルートの値が v 以下の場合)、右部分木に対して再帰的に挿入を行います(root->right = insert(root->right, v))。
  • 最後にルートを返します。

C++での実装例

より理解を深めるために、以下の実装例を見てみましょう。

#include <bits/stdc++.h>
using namespace std;
class TreeNode{
   public:
      int val;
      TreeNode *left, *right;
      TreeNode(int data){
         val = data;
         left = right = NULL;
      }
};
void insert(TreeNode **root, int val){
   queue<TreeNode*> q;
   q.push(*root);
   while(q.size()){
      TreeNode *temp = q.front();
      q.pop();
      if(!temp->left){
         if(val != NULL)
            temp->left = new TreeNode(val);
         else
            temp->left = new TreeNode(0);
         return;
      }
      else{
         q.push(temp->left);
      }
      if(!temp->right){
         if(val != NULL)
            temp->right = new TreeNode(val);
         else
            temp->right = new TreeNode(0);
         return;
      }
      else{
         q.push(temp->right);
      }
   }
}
TreeNode *make_tree(vector<int> v){
   TreeNode *root = new TreeNode(v[0]);
   for(int i = 1; i<v.size(); i++){
      insert(&root, v[i]);
   }
   return root;
}
void tree_level_trav(TreeNode*root){
   if (root == NULL) return;
      cout << "[";
   queue<TreeNode *> q;
   TreeNode *curr;
   q.push(root);
   q.push(NULL);
   while (q.size() > 1) {
      curr = q.front();
      q.pop();
      if (curr == NULL){
         q.push(NULL);
      }
      else {
         if(curr->left)
            q.push(curr->left);
         if(curr->right)
            q.push(curr->right);
         if(curr->val == 0 || curr == NULL){
            cout << "null" << ", ";
         }
         else{
            cout << curr->val << ", ";
         }
      }
   }
   cout << "]"<<endl;
}
class Solution {
public:
   TreeNode* insertIntoBST(TreeNode* root, int val) {
      if(!root)return new TreeNode(val);
      if(root->val > val){
         root->left = insertIntoBST(root->left, val);
      }
      else root->right = insertIntoBST(root->right, val);
         return root;
   }
};
main(){
   Solution ob;
   vector<int> v = {4,2,7,1,3};
   TreeNode *root = make_tree(v);
   tree_level_trav(ob.insertIntoBST(root, 5));
}

入力

[4,2,7,1,3]
5

出力

[4,2,7,1,3,5]

アルゴリズムのポイントと計算量

この挿入アルゴリズムでは、ルートから出発して値の大小を比較しながら挿入位置まで降りていくため、BSTの性質が自動的に保たれます。計算量について見てみましょう。

  • 時間計算量: 木の高さを h とすると O(h)。バランスの取れたBSTでは O(log n) ですが、最悪ケース(木が連結リストのように偏っている場合)では O(n) となります。
  • 空間計算量: 再帰呼び出しのスタック深さに依存するため O(h) です。

なお、サンプルコード内の make_tree() はテスト用に木を構築するための補助関数で、こちらはキューを使った幅優先の挿入を行っています。実際のBSTへの挿入ロジックは Solution クラスの insertIntoBST() メソッドに実装されています。

  1. C++で二分木を二分探索木(BST)へ変換する方法を解説

    二分木(Binary Tree)とは二分木とは、木構造の各ノードが最大で2つの子ノードを持つことができる特別な木構造です。これらの子ノードは、それぞれ「左の子ノード」と「右の子ノード」と呼ばれます。シンプルな二分木の例は以下の通りです。二分探索木(BST)とは二分探索木(BST)は、以下のルールに従う特別な木構造です。左の子ノードの値は、常に親ノードの値より小さい右の子ノードの値は、常に親ノードの値より大きいすべてのノードが、それぞれ独立して二分探索木の性質を満たす二分探索木(BST)の例は以下の通りです。二分探索木は、検索や最小値・最大値の探索といった操作の計算量を削減するために用いられるデ

  2. C++プログラムにおける二分探索(バイナリサーチ)の基本と実装

    二分探索(バイナリサーチ)とは二分探索は「半区間探索」「対数探索」「バイナリチョップ」とも呼ばれる検索アルゴリズムで、ソート済みの配列の中から目的の値が存在する位置を効率的に見つけ出します。基本的な仕組みは非常にシンプルです。まず、探したい値(ターゲット値)を配列の中央の要素と比較します。一致しなかった場合は、ターゲット値が存在し得ない半分を丸ごと排除し、残りの半分に対して同様の比較を繰り返します。この「中央との比較」と「範囲の絞り込み」を続け、ターゲット値が見つかるか、検索範囲が空になる(=配列にその値が存在しない)かのどちらかで処理が終了します。アイデア自体は簡単ですが、正しく実装するには