バイナリツリーの基本操作(C++実装)

基本的な考え方

バイナリ ツリーは、有限のノードのセットで構成されます。
この有限セットは、空のセット、またはルート ノードと、ルートの左右のサブツリーと呼ばれる 2 つの互いに素なバイナリ ツリーで構成されるセットのいずれかです。

二分木の特徴

  1. 各ノードには最大 2 つのサブツリーがあるため、バイナリ ツリーには次数が 2 を超えるノードはありません。
  2. 左側のサブツリーと右側のサブツリーには順序があり、順序を自由に逆転させることはできません。
  3. ツリー内のノードにサブツリーが 1 つしかない場合でも、それが左側のサブツリーか右側のサブツリーかを区別する必要もあります。

二分木の 5 つの基本的な形式

  1. 空の二分木
  2. ルートノードは 1 つだけ
  3. ルートノードには左側のサブツリーしかありません
  4. ルートノードには右のサブツリーのみがあります
  5. ルート ノードには左のサブツリーと右のサブツリーの両方があります

関連概念

エッジ: エッジと呼ばれる 2 つのノードの順序付きペア ノードの次数
: ノードに含まれるサブツリーの数がノードの次数になります
リーフ ノード: 次数が 0 です ノードはリーフ
ノードになります パス (パス)
パスの長さ (パスの長さ)
層: ルートは層 0 (他のノードの層番号は、その親ノードの層番号に 1 を加えたものに等しい)
深さ: 最大層数 葉ノードの層数

二分木関連の結論

ここに画像の説明を挿入

二分木記憶構造

バイナリ ツリーのシーケンシャル ストレージ構造は、通常、完全なバイナリ ツリーにのみ適用できます。通常、バイナリ ツリーを表すときは、連鎖ストレージ構造を使用します。

バイナリリンクリスト

バイナリ ツリーにはノードごとに最大 2 つの子があるため、1 つのデータ フィールドと 2 つのポインタ フィールドがあります。
このようなリンク リストをバイナリ リンク リストと呼びます。
図に示すように:

子供 データ 子供

このうち、data はデータ フィールドであり、lchild と rchild は両方ともポインタ フィールドで、それぞれ左の子と右の子へのポインタを格納します。

バイナリツリーのバイナリリンクリスト構造定義

/*定义二叉树的结构*/
typedef struct Node
{
    
    
    char data;                    /*数据域*/
    struct Node *lchild, *rchild; /*左子树和右子树*/
} * BiTree, BiNode;
/*整棵树和结点名称*/

バイナリツリートラバーサル

トラバーサルは、
各ノードが 1 回だけ訪問されるように、データ構造内のノードを体系的に訪問します。

バイナリ ツリーの深層検索限定トラバーサル:
以下は、3 つの深さ優先トラバーサルの再帰的定義です。

プレオーダートラバーサル

ルール: バイナリ ツリーが空の場合、操作は戻ります。そうでない場合は、最初にルート ノードにアクセスし、次に事前順序で左のサブツリーにアクセスし、次に事前順序で右のサブツリーにアクセスします。画像の説明を追加してください

インオーダートラバーサル

左側のサブツリーを順番にトラバースし、ルート ノードにアクセスし、右側のサブツリーを順番にトラバースします。
画像の説明を追加してください

私は以前、その偉い人による順序トラバーサルの説明を読んだことがあります。各ノードをブドウとして扱い、これらのブドウを同じ平面上に垂直に投影し、次に左から右にアクセスします。これが順序トラバーサルです。

ポストオーダートラバーサル

左側のサブツリーをポストオーダーでトラバースし、右側のサブツリーをポストオーダーでトラバースして、ルート ノードにアクセスします。画像の説明を追加してください

小さな要約

前順トラバーサル: 最初にルート、次に左、次に右
順順トラバーサル: 最初に左、次にルート、次に右
事後トラバーサル: 最初に左、次に右、次にルート
このルートは、分岐した各サブツリーのルートを指します (左右のサブツリーのルート ノード) ノードはツリー全体の単なるルート ノードではありません

コード

#include <iostream>
#include <string>
#include <algorithm>
using namespace std;
/*定义二叉树的结构*/
typedef struct Node
{
    
    
    char data;                    /*数据域*/
    struct Node *lchild, *rchild; /*左子树和右子树*/
} * BiTree, BiNode;
/*整棵树和结点名称*/

/*先需创建二叉树*/
void CreateBiTree(BiTree &T)
{
    
    
    char ch;
    cin >> ch;
    if (ch == '#')
        T = NULL;
    else
    {
    
    
        T = new BiNode; /*创建一个新节点*/
        T->data = ch;
        CreateBiTree(T->lchild);
        CreateBiTree(T->rchild);
    }
    /*递归创建*/
}
void InOrderTraverse(BiTree T)
{
    
    
    /*中序遍历*/
    if (T)
    {
    
    
        InOrderTraverse(T->lchild);
        cout << T->data;
        InOrderTraverse(T->rchild);
    }
}
void PreOrderTraverse(BiTree T)
{
    
    
    /*先序遍历*/
    if (T)
    {
    
    
        cout << T->data;
        PreOrderTraverse(T->lchild);
        PreOrderTraverse(T->rchild);
    }
}
void PostOrderTraverse(BiTree T)
{
    
    
    /*后序遍历*/
    if (T)
    {
    
    
        PostOrderTraverse(T->lchild);
        PostOrderTraverse(T->rchild);
        cout << T->data;
    }
}
/*统计二叉树中结点的个数*/
int NodeCount(BiTree T)
{
    
    
    if (T == NULL)
        return 0;
    else
        return NodeCount(T->lchild) + NodeCount(T->rchild) + 1;
}
/*求树的深度*/
int Depth(BiTree T)
{
    
    
    if (T == NULL)
        return 0;
    else
    {
    
    
        int i = Depth(T->lchild);
        int j = Depth(T->rchild);
        return i > j ? i + 1 : j + 1;
    }
}
/*复制二叉树*/
void Copy(BiTree T, BiTree &NewT)
{
    
    
    if (T = NULL)
    {
    
    
        NewT = NULL;
        return;
    }
    else
    {
    
    
        NewT = new BiNode;
        NewT->data = T->data;
        Copy(T->lchild, NewT->lchild);
        Copy(T->rchild, NewT->rchild);
    }
}
/*统计二叉树中叶子结点的个数*/
int LeafCount(BiTree T)
{
    
    
    if (!T)
        return 0;
    if (!T->lchild && !T->rchild)
        return 1;
    /*如果二叉树左子树和右子树皆为空,说明该二叉树根节点为叶子结点,结果为1*/
    else
        return LeafCount(T->lchild) + LeafCount(T->rchild);
}
/*二叉树中从每个叶子结点到跟结点的路径*/
void PrintAllPath(BiTree T, char path[], int pathlen)
{
    
    
    int i;
    if (T != NULL)
    {
    
    
        path[pathlen] = T->data; /*将当前结点放入路径中*/
        if (T->lchild == NULL && T->rchild == NULL)
        {
    
    
            /*若这个节点是叶子结点*/
            for (i = pathlen; i >= 0; i--)
                cout << path[i] << " ";
            cout << "\n";
        }
        else
        {
    
    
            PrintAllPath(T->lchild, path, pathlen + 1);
            PrintAllPath(T->rchild, path, pathlen + 1);
        }
    }
}
/*判断二叉树是否为空*/
int BiTree_empty(BiTree T)
{
    
    
    if (T)
        return 1;
    else
        return 0;
}
int main()
{
    
    
    BiTree T;
    //测试数据AB#CD##E##F#GH###
    cout << "先序遍历输入(以#结束):";
    CreateBiTree(T);
    cout << "中序遍历输出:";
    InOrderTraverse(T);
    cout << endl
         << "先序遍历输出:";
    PreOrderTraverse(T);
    cout << "\n"
         << "后序遍历输出:";
    PostOrderTraverse(T);
    cout << endl
         << "树的深度:" << Depth(T);
    cout << endl
         << "结点的个数:" << NodeCount(T);
    cout << endl
         << "二叉树中从每个叶子结点到根结点的所有路径:" << endl;
    char path[256];
    int pathlen = 0;
    PrintAllPath(T, path, pathlen);
    return 0;
}

特別な二分木

  1. 完全なバイナリ ツリー
    バイナリ ツリーでは、すべてのブランチ ノードが左右のサブツリーを持ち、左右のサブツリーが同じレベルにある場合、そのようなバイナリ ツリーは図に示すような完全なバイナリ ツリーになります

    画像の説明を追加してください

  2. すべてのノードには、
    左斜木と呼ばれる左サブツリーのみがあります。すべてのノードが右部分木のみを持つ二分木は右斜木と呼ばれます。両方を総称して斜木と呼びます。(これは実際には垂直方向に見ると線形テーブルです)

  3. 完全なバイナリ ツリー
    が n 個のノード (シーケンス ルールに従って番号付けされている) を持つバイナリ ツリーである場合、番号 i (1<=i<=n) のノードと、同じ深さの完全なバイナリ ツリー内の番号 i のノードは、バイナリ ツリー内の位置が同じ場合、そのツリーは次の図に示すように完全なバイナリ ツリーになります。画像の説明を追加してください

完全な二分木のプロパティ:
1) リーフ ノードは下の 2 つのレイヤーにのみ存在できます。
2) 最下位レイヤーのリーフは左側の連続した位置に集中している必要があります
。 3) ノードの次数が 1 の場合、ノードは左側にしか存在しません。子供たち。
4) 最後から 2 番目の層に葉ノードがある場合は、右側の連続した位置に配置する必要があります。
5) 同じノードのバイナリ ツリー、完全なバイナリ ツリーの深さが最も小さい

おすすめ

転載: blog.csdn.net/qq_52109814/article/details/119539568