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

C++で二分木を双方向連結リストに変換する方法(セット2)

はじめに

このチュートリアルでは、C++を使って二分木を双方向連結リスト(Doubly Linked List)に変換するプログラムについて解説します。

入力として二分木が与えられ、それを双方向連結リストへ変換するのが課題です。変換の際には、木の左ポインタ右ポインタを、それぞれリストの前ポインタ(prev)次ポインタ(next)として再利用します。また、変換後のリストの並び順は、元の二分木を中間順走査(inorder traversal)した順序と一致していなければなりません。

アルゴリズムの考え方

ここでは、少し異なるアプローチを採用します。

  • 二分木を逆中間順(右部分木 → 根 → 左部分木)で走査します。
  • 各ノードを訪問するたびに、そのノードをリストの先頭に挿入し、head ポインタを最新のノードへ移動させます。
  • この処理により、双方向連結リストが末尾から先頭へ向かって構築されていきます。

処理の手順

  1. まず右部分木を再帰的に変換します。
  2. 現在のノードの right ポインタを、現在の head に接続します。
  3. head が NULL でない場合、head の left ポインタを現在のノードに向けます。
  4. head を現在のノードに更新します。
  5. 最後に左部分木を再帰的に変換します。

C++での実装例

#include <stdio.h>
#include <stdlib.h>
// 木のノード構造体
struct Node{
   int data;
   Node *left, *right;
};
// 二分木を双方向連結リストへ変換
void binary_todll(Node* root, Node** head_ref){
   if (root == NULL)
      return;
   // 右部分木を変換
   binary_todll(root->right, head_ref);
   // 現在のノードをリストの先頭に挿入
   root->right = *head_ref;
   // head ポインタを移動
   if (*head_ref != NULL)
      (*head_ref)->left = root;
   *head_ref = root;
   // 左部分木を変換
   binary_todll(root->left, head_ref);
}
// 新しいノードを生成
Node* newNode(int data){
   Node* node = new Node;
   node->data = data;
   node->left = node->right = NULL;
      return node;
}
// 双方向連結リストを出力
void print_dll(Node* head){
   printf("Doubly Linked list:\n");
   while (head) {
      printf("%d ", head->data);
      head = head->right;
   }
}
int main(){
   Node* root = newNode(5);
   root->left = newNode(3);
   root->right = newNode(6);
   root->left->left = newNode(1);
   root->left->right = newNode(4);
   root->right->right = newNode(8);
   root->left->left->left = newNode(0);
   root->left->left->right = newNode(2);
   root->right->right->left = newNode(7);
   root->right->right->right = newNode(9);
   Node* head = NULL;
   binary_todll(root, &head);
   print_dll(head);
   return 0;
}

出力結果

Doubly Linked list:
0 1 2 3 4 5 6 7 8 9

計算量

  • 時間計算量:O(n) — 各ノードを一度だけ訪問します(n はノード数)。
  • 空間計算量:O(h) — 再帰呼び出しのためのスタック領域が必要です(h は木の高さ)。

まとめ

逆中間順で走査しながら head ポインタを更新していくことで、余分なデータ構造を追加することなく、二分木のノードをそのまま双方向連結リストへ変換できます。中間順走査の順序が保たれるため、二分探索木の場合は結果としてソート済みの連結リストが得られる点も大きなメリットです。

  1. C++で二分木をリンクリストにフラット化(平坦化)する方法

    二分木が与えられたとき、それをその場(in-place)でリンクリストへフラット化(平坦化)することを考えます。具体的には、すべてのノードを右ポインタで連結し、左ポインタを null にした、一本の連結リストのような構造へ変換します。例えば、次のような二分木があるとします。これをフラット化すると、出力は次のようになります。アルゴリズムの手順この問題は、逆後順走査(右 → 左 → 根)を利用することで効率的に解けます。手順は以下の通りです。prev を null で初期化します。ルートを引数にとる再帰関数 solve() を定義します。root が null の場合は、そのまま戻ります。まず r

  2. C++で二分木がSumTree(総和木)かどうかを判定する方法

    ここでは、与えられた二分木が「SumTree(総和木)」であるかどうかを判定する方法を解説します。まずは、SumTreeとはどのような木なのかを確認しておきましょう。 SumTreeとは SumTreeとは、すべての内部ノードが「左の子と右の子の値の合計」を保持する特殊な二分木です。木の根(ルート)には、それより下位に存在する全要素の合計値が格納されます。なお、葉ノードのみからなる木や空の木も、定義上はSumTreeとみなされます。以下はSumTreeの一例です。 例えば上図の木では、根の値26が左部分木(10 + 4 + 6 = 20)と右部分木(3 + 3 = 6)の合計と一致しており