データ構造のツリーについて学ぶための記事

1. 樹木知識体系の構築

木

2. ツリーの基本

2.1 ツリー構造について

ツリー構造は非常に重要な非線形構造であり、分岐関係によって定義される 1 対多の階層構造です。
ツリーは、n (n≥0) ノードの有限集合です。n=0 のときを空木と呼びます。空でないツリーでは、以下を満たす必要があります:
a. ルートと呼ばれる特定のノードが 1 つだけ存在する。
b. n>1 の場合、残りのノードは m (m>0) 個の互いに素な有限集合 T、T2、...、Tm に分割でき、それぞれが
それ自体がツリーであり、ルートのサブツリーと呼ばれます

ツリー構造には次のプロパティがあります。

  • ツリーのルート ノードには先行ノードがなく、ルート ノードを除くすべてのノードには先行ノードが 1 つだけあります。
  • ツリー内のすべてのノードは、0 個以上のサクセサを持つことができます。

2.2 ツリーの関連概念

Node : データ要素とそのサブツリーを指すブランチ。
ノードの次数: ノードが所有するサブツリーの数をノードの次数と呼びます。
木の次数: 木のノードの次数の最大値を木の次数と呼びます。
リーフノード: ツリーの次数が 0 (下に枝分かれしていない) のノードは、リーフノード (またはターミナルノード) と呼ばれます。
非葉ノード: ツリー (下に枝がある) で次数が 0 でないノードは、非葉ノード (または非終端ノードまたは枝ノード) と呼ばれます。
ルート ノードに加えて、ブランチ ノードは内部ノードとも呼ばれます。
子ノード: ノードのサブツリーのルートは、ノードの子ノードまたは子ノードと呼ばれます。
親ノードまたは親ノード: サブツリーのルートである子ノードに対応します。
兄弟ノード: 同じ親ノードのすべての子ノードは、兄弟ノードと呼ばれます。
レベル: ツリーのルート ノードのレベルは 1 であり、その複数のノードのレベルは親ノードのレベルに 1 を加えたレベルに等しいと規定されています。
カズン ノード: 同じレイヤー上にあるが親が異なるすべてのノードは、カズン ノードと呼ばれます。
階層パス: ルート ノードから開始して、あるノード p に到達するまでに通過するすべてのノードは、ノード p の階層パスと呼ばれます (1 つしかありません)。
祖先ノード: ノード p の階層パス上のすべてのノード (p を除く) は、p の祖先と呼ばれます。
子孫ノード: 特定のノードをルートとするサブツリー内のノードは、そのノードの子孫ノードと呼ばれます。
ツリーの深さ: ツリー内のノードの最大レベル値。ツリーの高さとも呼ばれます。
枝の数 = 子の数 = 次数

2.3 樹木の主な性質

プロパティ 1 : ツリー内のノードの数は、すべてのノードの次数の合計 + 1 に等しくなります。
プロパティ 2 : m 次ツリーの場合、葉ノードの数をn 0 n_0として定義しますn0、次数1のノードの数はn 1 n_1ですn1,... , 次数 m のノード数はnm n_mnメートル
n 0 = n 2 + 2 * n 3 + 3 * n 4 + … + ( m − 1 ) * nm + 1 n_0=n_2+2*n_3+3*n_4+…+(m-1) * n_m +1n0=n2+2n3+3n4++(メートル1 )nメートル+1
性質 3: 空でない m 次木では、i 番目の層に高々mi − 1 m^{i -1}メートルi 1ノード (i≥1)。
性質 4: 高さ h の m(m>1) 次木、高々mh − 1 m − 1 \frac{m^{h}-1}{m-1}m 1メートルh1ノード。
性質 5 : n 個のノードを持つ m 次ツリーの最小の高さはlog ⁡ m [ n ∗ ( m − 1 ) + 1 ] \log_m[n * (m -1) +1] です。ログ_メートル[ n(メートル1 )+1 ]

2.4 ツリー表現

親表記
子チェーン表記
子兄弟表記

3.二分木

3.1 定義と主な特徴

バイナリ ツリーの定義では、ツリーの定義内の m 個のブランチを最大 2 個のブランチに変更するだけで済みます。

3.1.1 二分木に関する注意事項
  • a. 二分木は左と右のサブツリーに分割されます。これは、二分木が順序付けられた木であることを意味し、二分木とは異なります。二分木は順序付き木ではないかもしれませんが、二分木は順序付き木でなければなりません。
  • b. 二分木には最大で 2 つの分岐があります。つまり、二分木の次数は最大で 2 ですが、各ノードの次数が 2 であるという意味ではありません。
3.1.2 二分木の性質

プロパティ 1 : ツリー内のノードの数は、すべてのノードの次数の合計 + 1 に等しくなります。
プロパティ 2 : 任意の二分木の場合、葉ノードの数をn 0 n_0として定義しますn0、次数2のノードの数はn 2 n_2ですn2n 0 = n 2 + 1 n_0=n_2+1n0=n2+1
性質 3: 空でない二分木では、i 番目の層に最大で2 i − 1 2^{i -1}2i 1ノード (i≥1)。
性質 4: 高さ k の二分木は最大2 k − 1 {2^{k}-1}2k1ノード (k≥1)。
特性 5: n 個のノードを持つ二分木の最小の高さは、log ⁡ 2 ( n + 1 ) \log_2(n +1)ログ_2( n+1 )

3.1.3 二分木分類

完全二分木、完全二分木、二分ソート木、平衡二分木、二分木
、ヒープ。

  • フル二分木: フル、深さ k で2 k − 1 {2^{k}-1}の木2kノードが 1 つのあいまいなツリーは、 完全なあいまいなツリー と呼ばれます
  • 完全二分木: 深さが k の場合、n 個のノードで構成される二分木。各ノードが、深さ k の完全二分木で 1 から n までの番号が付けられたノードに対応する場合にのみ、二分木は完全二分木と呼ばれます。 . (対応ポジション、対応番号、ジャンプ番号なし)
3.1.4 完全二分木の特徴
  • 各レイヤーのノード数は常に最大ノード数です。
  • すべての非リーフ ノードには、左右のサブツリーがあります。
  • 完全な二分木のノードに連続して番号を付けます. ルート ノード 1 から開始するように規定されている場合は、「トップダウン、白左から右」の原則に従って実行されます.
3.1.5 完全二分木の性質
  • 性質 1: 空でない分岐木では、i 番目の層に最大で2 k − 1 2^{k-1}ある2k 1ノード (i≥1)。

  • 性質 2: 深さ k の二分木は最大で2 k − 1 2^{k}-1を持つ2k1ノード (k≥1)。

  • 特性 3: 任意の二分木について、葉ノードの数が n の場合、次数 2 のノードの数はn 2 n_2です。n2、次にn 0 = n 2 テン 1 n_0=n_2 テン テン 1n0=n2テン1

  • プロパティ 4: ツリー内のノードの数は、すべてのノードの次数の合計 + 1 に等しい

  • 特性 5: 深さ k の完全な二分木で 1 から n までの番号が付けられた最初の n 個のノードは、深さ k の完全な二分木を形成します。ここで、2 k − 1 ≤ n ≤ 2 k − 1 2^{k- 1 }\ le n \le2^k-12k 1n2k1

  • プロパティ 6: n 個のノードを持つ完全なあいまいなツリーの深さは(log 2 n ) + 1 (log_2n)+1です。(ログ_ _2n )+1またはログ 2 ( n + 1 ) log_2(n+1)ログ_ _2( n+1 )

  • 特性 7: 完全な二分木の深さが k の場合、すべての葉ノードは k 番目または k-1 番目のレイヤーに表示されます。

  • プロパティ 8: 任意のノードについて、右側のサブツリーの最大レベルが 1 の場合、左側のサブツリーの最大レベルは 1 または 2 です。

  • 性質 9: n 個のノードを持つ完全な二分木 (深さは( log 2 n ) + 1 (log_2n) + 1)の場合(ログ_ _2n )+1 ) ノードは階層化されています (レイヤー 1 から(log 2 n ) + 1 (log_2n)+1 まで)(ログ_ _2n )+1レイヤー) シーケンスは左から右に番号が付けられ、番号 1(1 ≤ i ≤ n 1\le i \le n1n )のノードには
    次のような状況があります: ①i=1 の場合、ノード i は二分木のルートであり、親ノードを持たない;それ以外の場合、i>1 の場合、親ノード番号は [i/2]
    ② 2i>n の場合、ノード i は葉ノードであり、左の子を持たない: そうでない場合、その左の子ノード番号は 2i. ③2i
    +1>n の場合、ノード i は右の子を持たない; そうでない場合、その右の子 ノード番号は 2i+1 です。

  • プロパティ 10: 完全な二分木の場合、次数 1 のノードの数は 1 または 0 です。

3.2 二分木ストレージ

3.2.1 逐次構造格納

二分木の逐次保存とは、各ノードの番号を配列の添え字として配列に保存することであり、完全な二分木が満たされない場合は、空白フラグを埋めることで完全な二分木を構築できます。
最悪の場合、深さ k の 1 分岐ツリーで k 個のノードのみが必要な長さは2 k − 1 2^{k-1} です。2k 1の 1 次元配列この時点では、単一の右分岐ツリーです。一般に、逐次構造には完全な二分木のみが格納され、一般的な二分木は逐次構造には適していません。

#define MAX_SIZE 100
typedef int sqbitree[MAX_SIZE];
3.2.2 チェーン構造ストレージ

バイナリ ツリーには左右のサブツリーがあるため、バイナリ ツリーのチェーン構造の各ノードには 3 つのフィールドがあります。データ フィールドと、左右の子ノードをそれぞれ指す 2 つのポインタ フィールドです。

3.3 二分木探索

二分木トラバーサルは、プレオーダートラバーサル、インオーダートラバーサル、ポストオーダートラバーサル、レベルトラバーサルに分けられます。
順序前のトラバーサル、順序内のトラバーサル、および順序後のトラバーサルではスタックを使用する必要がありますが、階層的なトラバーサルではキューを使用する必要があります。トラバーサルの種類に関係なく、トラバーサル プロセス中、リーフ ノードの相対的な順序は変更されません。

3.3.1 基本概念

アクセスとは, 情報の出力やノードの値の変更など, ノード上で何らかの処理を行うことを指します. ノードトラバーサルにアクセスする操作を呼びます. バイナリツリーのトラバースとは, バイナリツリーの各ノードを1回ずつ訪問することを指します.指定されたルール、そして一度だけ。

L、D、および R がそれぞれ左のサブツリーのトラバース、ルート ノードのトラバース、および右のサブツリーのトラバースを表す場合、LDR、LRD、DLR、DRL、RLD、および RDL の 6 つのトラバーサル スキームがあります。最初に左に、次に右に移動することが事前に合意されている場合、次の 3 つの状況しかありません。 ) トラバーサル。

3.3.2 予約注文トラバーサル

Preorder Traversal は、二分木のルート ノードから開始し、最初にノードに到達したときにノード データを出力し、最初に左に移動し、次に右に移動する方向にアクセスすることです。二分木が空の場合、トラバーサルは終了します。それ以外の場合は、次の状況があります:
a. ルート ノードにアクセスします。
b. 左側のサブツリーを前の順序でトラバースします (このアルゴリズムを再帰的に呼び出します)。
c. サブツリーの事前順序走査 (このアルゴリズムが呼び出されます)。

トラバーサルに再帰が使用される場合、トラバーサルのためにシステム スタックが呼び出されます。具体的なコードは次のとおりです。

//二叉树定义部分:
#include<stdio.h>
#include<stdbool.h>
typedef int ElementType;
typedef struct BTNode{
    
    
    ElementType data;
    struct BTNode * Lchild, *Rchild;
}BTNode;

void PreorderTraverse(BTNode * T){
    
    
    //先序遍历,递归写法
    //如果为空,则访问根结点
    if(T == NULL) return;
    printf("%d\n", T->data);
    PreorderTraverse(T->Lchild);
    PreorderTraverse(T->Rchild);
}

ユーザー スタックを使用する場合は、次のコードを使用できます。

//定义需要用的类型及函数
#include<stdio.h>
#include<stdbool.h>
#define MAX_SIZE 100
typedef int ElementType;
typedef struct BTNode{
    
    
    ElementType data;
    struct BTNode * Lchild, *Rchild;
}BTNode;
typedef struct Stack{
    
    
    //定义栈
    //定义一个存放二叉树指针的数组
    BTNode * data[MAX_SIZE];
    int top;
}SqStack;

void Init_stack(SqStack * S){
    
    
    S->top = 0;
}

int isEmpty(SqStack * S){
    
    
    if(S->top == 0) return 1;
    else return 0;
}

bool push(SqStack * S, BTNode * node){
    
    
    if (S->top == MAX_SIZE) return false;
    S->data[S->top] = node;
    S->top++;
    return true;
}

BTNode * pop(SqStack * S){
    
    
    //出栈
    if (S->top == 0) return NULL;
    S->top--;
    return S->data[S->top];
}

void PreorderTraverse_user(BTNode * root){
    
    
    //二叉树遍历,调用用户栈
    //用来暂存结点的栈
    //BTNode * stack[100];
    SqStack * stack;
    Init_stack(stack);
    // 新建一个工作结点,并指向根结点
    BTNode * node = root;
    // 当遍历到最后一个结点,若左右子树都为空,且栈也为空,则跳出循环。
    //while (node != NULL || !stack.isEmpty()){
    
    
    while (node != NULL || !isEmpty(stack)){
    
    
        while(node != NULL){
    
    
            printf("%d\n", node->data);
            //暂存该结点
            //stack.push(node);
            push(stack, node);
            node = node->Lchild;
        }
        //if (!stack.isEmpty()){
    
    
        if (!isEmpty(stack)){
    
    
            //左子树为空,再取出该元素,并获取其右子树
            //node = stack.pop();
            node = pop(stack);
            node = node->Rchild;
        }
    }
}


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

二分木のルートノードから順にたどり、2度目にノードに到達した時点でノードデータを出力し、まず左に行き、次に右に行くという方向にたどります。アルゴリズムの再帰的な定義は次のとおりです: 二分木が空の場合、トラバーサルは終了します; そうでない場合、次の状況が発生します:
a. 左のサブツリーの順不同のトラバーサル (このアルゴリズムを再帰的に呼び出す)。
b. ルート ノードにアクセスします。
c. 正しいサブツリーを順番にトラバースします (このアルゴリズムを再帰的に呼び出します)。
再帰メソッドを呼び出すための実装コードは次のとおりです。

void InorderTraverse(BTNode * T){
    
    
    // 中序遍历
    if (T == NULL) return;
    InorderTraverse(T->Lchild);
    printf("%d", T->data);
    InorderTraverse(T->Rchild);
}

ユーザー スタックを呼び出します。カスタム メソッドは次のとおりです。

void midorderTraversal(BTNode * root){
    
    
    SqStack * stack;
    Init_stack(stack);
    BTNode * node = root;
    while(node != NULL || !isEmpty(stack)){
    
    
        while(node != NULL){
    
    
            push(stack, node);
            node = node->Lchild;
        }
        if (!isEmpty(stack)){
    
    
            node = pop(stack);
            printf("%d", node->data);
            node = node ->Rchild;
        }
    }
}

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

ポスト オーダー トラバーサルは、二分木のルート ノードから開始し、3 回目のノードに到達した時点でノード データを出力し、最初に左に移動し、次に右に移動する方向にアクセスします。アルゴリズムの再帰的定義は次のとおりです: 二分木が空の場合、トラバーサルは終了します; そうでない場合、次の状況が発生します:
a. 左サブツリーの後順トラバーサル (このアルゴリズムを再帰的に呼び出します)。
b. 右のサブツリーをポストオーダー トラバースします (wood アルゴリズムを再帰的に呼び出します)。
c. ルート ノードにアクセスします。
システム スタックを使用する場合、再帰的な方法は次のとおりです。

void postTraverse(BTNode * T){
    
    
    //后续遍历
    if (T == NULL) return;
    postTraverse(T->Lchild);
    postTraverse(T->Rchild);
    printf("%d\n", T->data);
}

ユーザー スタックを使用する場合、カスタム メソッドは次のようになります。

void postTraversal(BTNode * root){
    
    
    SqStack * stack;
    BTNode * node = root;
    BTNode * last = root;
    while(node != NULL || !isEmpty(stack)){
    
    
        while (node != NULL){
    
    
            push(stack, node);
            node =node->Lchild;
        }
        // 查看当前栈顶元素
        //如果其右子树为空,或者右子树已经访问,则输出该结点的值
        if (node->Rchild == NULL || node->Rchild == last){
    
    
            printf("%d\n", node->data);
            pop(stack);
            last = node;
            node = NULL;
        }else{
    
    
            node = node->Rchild;
        }
    }
}

3.3.5 階層トラバーサル

バイナリ ツリーの階層的トラバーサルは、ルート ノードから開始し、「上から下、左から右」の階層順序でツリー内の各ノードを調べます。レベルごとのトラバーサルを保証するには、キューを設定する必要があります。キューは初期化時には空です。T をルート ノードを指すポインター変数とすると、階層トラバーサルの非再帰的な方法は、バイナリ ツリーが空の場合は戻り、そうでない場合は p=T とし、p はキューに入ります。
a. チームの最初の要素が p にデキューされます。
b. p が指すノードにアクセスします。
c. キューが空になるまで、p が指すノードの左右の子ノードを順番にキューに入れます。
実装コードは次のとおりです。

void leverTraverse(BTNode * T){
    
    
    // 定义一个队列来存储结点
    BTNode * Queue[MAX_SIZE];
    BTNode * p = T;
    int front = rear = 0;
    // 当结点部位空时,开始进出队列
    if(p != NULL){
    
    
        Queue[rear++] = p;
        while(front < rear){
    
    
            //当队列不为空,进行输出和访问。
            p = Queue[front++];
            printf("%d", p->data);
            //有左结点,就将左节点放到队列中
            if (p->Lchild != NULL)
                Queue[front++] = p->Lchild;
            if (p->Rchild != NULL)
                Queue[front++] = p->Rchild;
        }
    }
}

3.3.6 二分木の生成

次の 3 つの組み合わせは、二分木を一意に決定できます。

  • 事前順トラバーサル + インオーダートラバーサル
  • ポストオーダートラバーサル + インオーダートラバーサル
  • 階層トラバーサル + インオーダートラバーサル

3.4 スレッド二分木

バイナリ ツリーのノードに手がかりが追加されたバイナリ ツリーは、スレッド化されたバイナリ ツリーと呼ばれ、特定のトラバーサル メソッド (プリオーダー、インオーダー、ポストオーダー、または階層など) でバイナリ ツリーをトラバースするプロセスなど) スレッド化されたバイナリ ツリーに変換することを、スレッド化されたバイナリ ツリーと呼びます。

3.4.1 スレッド化された二分木のマーキング方法
  • ノードに左の子がある場合、Lchild はその左の子を指します。それ以外の場合は、直前の子を指します。
  • ノードに右の子がある場合、Rchild はその右の子を指し、それ以外の場合は直接の後継ノードを指します。
    手がかりバイナリ ツリーの構造は次のとおりです。
左の子または直前の前任者 残された子はいないの? データ 右の子はいないの? 右の子または直系の後継者
Ltag データ

Ltag を例にとると、マーキング方法は次のとおりです。

  • Ltag は 0 としてマークされ、Lchild はノードの左側の子を指します
  • Ltag は 1 としてマークされ、Lchild はノードの直前の先行ノードを指します
3.4.2 スレッド二分木の構築

最初にトラバーサル シーケンスを書き出してから、サブツリーがあるかどうかに応じて、左右のサブツリーへのポインター、または直接の先行および直接の後続への手がかりを追加します。
以下は、スレッド化されたバイナリ ツリーを構築する例です。
スレッド二分木

4. 木、森

4.1 木と二分木の変換

4.1.1 木を二分木に変換する

一般的な木の場合、それに対応する一意の二分木に簡単に変換できます。詳細な手順は次のとおりです (子兄弟表記)。

  • ①点線を追加します。ツリーの各レベルで左から右の順に兄弟ノード間に点線を追加します。
  • ②接続に進みます。最初の左端の子ノードを除いて、親ノードと他のすべての子ノードの間の接続が削除されます。
  • ③回す。ツリーを時計回りに 45 度回転させると、元の実線が左に傾いています。
  • ④整数。回転したツリーのすべての点線を実線に変更し、右に傾けます。

以下に例を示します。
コンバージョン グラフ

4.1.2 二分木から木へ

二分木を木に変換する手順は次のとおりです。

  • ①点線を追加します。ノード i がその親ノードの左側のサブツリーのルート ノードである場合、ノード i の右側の子ノードとすべての右側の子ノードが右側の子チェーンに沿って検索され、i ノードの親ノードが点線で接続されます。 .
  • ②接続に進みます。バイナリ ツリー内の親ノードとその右側の子ノードの間のすべてのリンクを削除します。
  • ③正則化。グラフ内のノードを階層的に配置し、すべての点線を実線に変更します。反時計回りに 45 度回します。

例を挙げると、次のようになります。
二分木から木へ

4.2 ツリー格納構造

4.3 フォレストとバイナリ ツリーの変換

4.3.1 フォレストからバイナリ ツリーへ

フォレストからバイナリ ツリーへの変換手順は次のとおりです。

  • ①F={T,T2,…,T}の各木を二分木に変換する。
  • ②指定されたフォレスト内のツリーの順序に従って、最後のバイナリ ツリーから開始して、各バイナリ ツリーは前のバイナリ ツリーのルート ノードの右側のサブツリーとして使用され、その後、最初のツリーのルート ノードが使用されます。変換された 生成されたバイナリ ツリーのルート ノードです。

以下は、森を木に変えた例です。
フォレストからバイナリ ツリーへ

4.3.2 二分木から森へ

二分木をフォレストに変換する手順は次のとおりです。
①接続に移動します。バイナリ ツリーのルート ノードとその右側の子ノードの間のすべての接続、および右側の子ノード チェーンの方向に沿ったすべての右側の子ノードを削除して、いくつかの分離されたバイナリ ツリーを取得します。各ツリーは、二分木の順に元の森。
②二分木を復元する。二分木を木に復元する方法で、孤立した各二分木を一般的な木に復元します。
二分木をフォレストに変換する例は次のとおりです。
二分木から森へ

4.4 木と森の横断

ツリー トラバーサルには、プレオーダー トラバーサルとポストオーダー トラバーサルの 2 種類があります。

  • Preorder traversal: 最初にルート ノードにアクセスし、次に各サブツリーを順番に走査します。ツリーの事前順序トラバーサルは、ツリーをバイナリ ツリーに変換した後のバイナリ ツリーの事前順序トラバーサルと同じです。
  • ポスト オーダー トラバーサル: 最初に各サブツリーをポスト オーダーでトラバースし、次にルート ノードにアクセスします。ツリーのポストオーダートラバーサルは、ツリーをバイナリツリーに変換した後のバイナリツリーのインオーダートラバーサルと同じです。

5. 木と二分木の応用

5.1 二分ソートツリー

5.2 平衡二分木

5.3 ハフマン木とハフマン符号化

5.3.1 関連概念
  • ノード パス: ツリー内の 1 つのノードから別のノードへの分岐は、それら 2 つのノード間のパスを構成します。
  • パスの長さ: ノードへのパス上の分岐の数は、パスの長さと呼ばれます。
  • ツリーのパスの長さ: ツリーのルートから各ノードまでのパスの長さの合計。
  • ノードの加重パス長: ノードからツリーのルート ノードまでのパス長とノードの重み (値) の積。その中でも重さ(価値)とは、さまざまな費用やコスト、頻度などの抽象的な名称です。
  • ツリーの加重パス長: ツリー内のすべてのリーフ ノードの加重パス長 (WTL) の合計で、
    WPL = ω 1 ι 1 + ω 2 ι 2 + . . . + ω n ι n = ∑として記録されます。 i = 1 n ω i ι i WPL = \omega_1\iota_1+\omega_2\iota_2+...+\omega_n\iota_n=\sum_{i=1}^{n}\omega_i\iota_iW P L=おお11+おお22+...+おおnn=私は= 1nおお
  • ハフマン (ハフマン) 木: n 個の葉ノード (各ノードの重みはω 1 \omega_1おお1) には複数の二分木があります。しかし、これらすべてのバイナリ ツリーの中に、ハフマン ツリー (または最適ツリー) と呼ばれる最小の WPL 値を持つツリーが存在する必要があります。

以下は、WPL の計算例です。
WPL計算

5.3.2 ハフマン木の構築

構築ポイント:エンコーディングが長いパスの長さを長くし、エンコーディングが短いパスの長さを短くする必要があります。
与えられた n 個の重みはw 1 w_1ですw1w 2 w_2w2,…, wn w_nwnノード、ハフマン木を構築するためのアルゴリズムは次のように記述されます。

  1. n 個のノードはそれぞれ、1 つのノードのみを含む n 個の二分木と見なされ、フォレスト F を形成します。
  2. 新しいノードを構築し、F からルート ノードの重みが最も小さい 2 つのツリーを新しいノードの左右のサブツリーとして選択し、左右のサブツリーのルート ノードとして新しいノードの重みを設定します。重みの合計の 。
  3. F から選択した 2 つのツリーを削除し、新しく取得したツリーを F に同時に追加します。
  4. F にツリーが 1 つだけ残るまで、手順 2 と 3 を繰り返します。

例:
w={8,3,4,6,5,5} をハフマン木に構築する場合、手順は次のとおりです。
ハフマン木 1

ハフマン木 2
その WPL 値は次のとおりです。

WPL = 6 * 2 + 3 * 3 + 4 * 3 + 8 * 2 + 5 * 3 + 5 * 3 = 79 WPL=6*2+3*3+4*3+8*2+5*3+5 ※3=79W P L=62+33+43+82+53+53=79

5.3.3 ハフマン符号化

どのエンコーディングも別のエンコーディングのプレフィックスでない場合、そのようなエンコーディングはプレフィックス エンコーディングと呼ばれます。
ハフマン木からハフマン コードを取得するのは自然なプロセスです。まず、出現する各文字を独立したノードと見なし、その重みを出現頻度 (または回数) とし、対応するハフマン木を構築します。明らかに、すべての文字ノードはリーフ ノードに表示されます。文字のエンコーディングは、ルートからその文字までのパス上の一連のエッジ ラベルとして解釈できます。ここで、エッジ ラベルは「左の子に向かう」の場合は 0、「右の子に向かう」場合は 1 です。

ハフマン符号化かどうかを判断するには?

  • 方法 1: プレフィックスエンコーディングの定義に準拠しているかどうかによる
  • 方法 2: ハフマン木を描いて適合するかどうかを確認する
5.3.4 ハフマン木の特徴
  • ハフマン木には次数 0 のノードと次数 2 のノードしかありません。
  • ハフマン木は最小の WPL 値を持ちます。
  • ハフマン ツリーの左右のサブツリーは交換できるため、ハフマン ツリーは一意ではありませんが、W 値は一意です。
  • ハフマン木は本質的に二分木ではありませんが、通常、ハフマン木は二分木と考えられています。
  • ハフマン木の上位ノードの重みは、下位ノードの重み以上です。
  • ハフマン コーディングでは、葉のコーディングについてのみ説明します。

5.4 赤黒木

5.5 ユニオン検索とその応用

ユニオン チェック セットは、非常にデリケートで実用的なデータ構造であり、主に一部のばらばらなセットのマージおよびクエリの問題を処理するために使用されます。一般的な用途には、接続されたサブグラフの検索、最小スパニング ツリーを検索するためのクラスカルのアルゴリズム、および最も近い共通の祖先の検索が含まれます。
ユニオン検索を使用する場合、最初に互いに素な動的セット S={S1,s2,...,Sn} のセットが格納され、通常、セット内の要素を表すために整数が使用されます。

ユニオン チェックは、次の 3 つの操作をサポートする単純なコレクション表現です。

  1. Initial(S): コレクション S の各要素を、1 つの要素のみを持つサブコレクションとして初期化します。
  2. Union(S, Root1, Root2): セット S のサブセット Root2 をサブセット Root1 にマージします。Root1 と Root2 が相互に素であることが必要です。そうでない場合、マージは実行されません。
  3. Find(S,x): セット S 内の単一要素 x が配置されているサブセットを検索し、サブセットのルート ノードを返します。

通常、ツリー (フォレスト) の親表現は共用体検索セットの格納構造として使用され、各サブセットはツリーで表されます。サブコレクションを表すすべてのツリーは、完全なコレクションを表すフォレストを形成し、親表現配列に格納されます。通常、配列要素の添え字は要素名を表すために使用され、ルート ノードの添え字はサブコレクション名を表すために使用され、ルート ノードの親ノードは負の数です。

おすすめ

転載: blog.csdn.net/qq_41780234/article/details/127295174