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

ソート済み連結リストをC++で高さ平衡な二分探索木(BST)に変換する方法

問題の概要

昇順にソートされた単方向連結リストが与えられたとき、それを高さ平衡な二分探索木(BST)に変換することを考えます。例えば、リストが [-10, -3, 0, 5, 9] の場合、生成される木は次のようになります。

ソート済み連結リストをC++で高さ平衡な二分探索木(BST)に変換する方法

アルゴリズムのポイント

この問題を効率よく解く鍵は、リストの中央ノードを見つけて、それを木のルートにすることです。中央ノードより前の部分リストからは左部分木を、後ろの部分リストからは右部分木を再帰的に構築します。中央ノードの探索には、2つずつ進む高速ポインタ(fast)と1つずつ進む低速ポインタ(slow)を組み合わせる手法が便利です。

手順

  • リストが空の場合はNULLを返します。

  • リストの先頭ノードを引数に取る再帰メソッド sortedListToBST() を定義します。

  • x := リストaにおける中間ノードの直前ノードのアドレス

  • mid := 中間ノードそのもの

  • midの値を用いて新しいノードを作成します。

  • nextStart := midノードの次のノード

  • midのnextをNULLに設定します。

  • nodeのright := sortedListToBST(nextStart)

  • xがNULLでない場合は、xのnextをNULLにしたうえで、nodeのleft := sortedListToBST(a)

  • nodeを返します。

C++による実装例

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

#include <bits/stdc++.h>
using namespace std;
class ListNode{
   public:
   int val;
   ListNode *next;
   ListNode(int data){
      val = data;
      next = NULL;
   }
};
ListNode *make_list(vector<int> v){
   ListNode *head = new ListNode(v[0]);
   for(int i = 1; i<v.size(); i++){
      ListNode *ptr = head;
      while(ptr->next != NULL){
         ptr = ptr->next;
      }
      ptr->next = new ListNode(v[i]);
   }
   return head;
}
class TreeNode{
   public:
   int val;
   TreeNode *left, *right;
   TreeNode(int data){
      val = data;
      left = right = NULL;
   }
};
void inord(TreeNode *root){
   if(root != NULL){
      inord(root->left);
      cout << root->val << " ";
      inord(root->right);
   }
}
class Solution {
   public:
   pair <ListNode*, ListNode*> getMid(ListNode* a){
      ListNode* prev = NULL;
      ListNode* fast = a;
      ListNode* slow = a;
      while(fast && fast->next){
         fast = fast->next->next;
         prev = slow;
         slow = slow->next;
      }
      return {prev, slow};
   }
   TreeNode* sortedListToBST(ListNode* a) {
      if(!a)return NULL;
      pair<ListNode*, ListNode*> x = getMid(a);
      ListNode* mid = x.second;
      TreeNode* Node = new TreeNode(mid->val);
      ListNode* nextStart = mid->next;
      mid->next = NULL;
      Node->right = sortedListToBST(nextStart);
      if(x.first){
         x.first->next = NULL;
         Node->left = sortedListToBST(a);
      }
      return Node;
   }
};
main(){
   vector<int> v = {-10,-3,0,5,9};
   ListNode *head = make_list(v);
   Solution ob;
   inord(ob.sortedListToBST(head));
}

このコードでは、まず getMid() 関数がfast・slowポインタを使って中間ノードとその直前ノードを求めます。sortedListToBST() は中間ノードの値でルートを作成し、リストを前後に分割しながら再帰的に左右の部分木を構築していきます。最後に、変換後の木を中順走査(inorder)で出力して結果を確認しています。

入力

[-10,-3,0,5,9]

出力

-10 -3 0 5 9

計算量

時間計算量は O(n log n) です。各再帰レベルでリスト全体を走査して中間ノードを求めるためです。空間計算量は O(log n) で、これは再帰スタックの深さ(平衡木の高さ)に相当します。


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

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

  2. Pythonでソート済み配列を高さバランスの二分探索木に変換する方法

    ソートされた配列 A が与えられたとき、そこから高さバランスの取れた二分探索木(BST)を生成することを考えます。ここで「高さバランスの取れた二分木」とは、すべてのノードにおいて、左右の部分木の深さの差が常に1以下であるような二分木のことを指します。例として、配列が [-10, -3, 0, 5, 9] の場合、出力の一例は [0, -3, 9, -10, null, 5] のようになります。解法のアプローチこの問題は、配列の中央要素をルートに選ぶというシンプルな発想で解くことができます。具体的な手順は以下の通りです。配列 A が空の場合は、Null(None)を返します。配列の中央の要素を見