データ構造レビューファイブ:ツリーとバイナリツリー

定義ツリーとバイナリツリー

木の定義

ツリーは空のツリーは、ツリーが空のために有している場合、n個のノードの有限集合であり、n = 0:

1、唯一つのノードがルートと呼ばれる存在です。

、ルートノード以外の残りのノードは、m個の互いに素の有限に分割することができる2 T1、T2を設定...、Tmは、前記各セットは、ツリー自体で、ルートとするサブツリーになっ。

ツリーの基本的な用語

図1に示すように、ノード:ツリー内の別のユニット、データエレメント、およびそのサブツリーの分岐点の複数を含みます。

図2に示すように、ノードの:結節点の数がサブツリーを有します。

図3に示すように、木の:ツリー内のノードの最大値。

4、:0度ノード(末端ノード)。

5、非終端ノード:ノードの次数は(ブランチノード)が0ではありません。

6:親と子:サブツリーのノードのルートはそれに応じて、親ノードの子で、ノードの子と呼ばれています。

7、兄弟:お互いの兄弟と呼ばれる子を持つ親の間。

8、祖先:分岐上のすべてのノードへのルートからのノードです。

9、子孫:ノードの任意の子孫となるノードをルートとするサブツリー内のノード。

10、レベル:ルートから開始し、第一層のルートは、任意のレベルで階層ツリーのノードがその親ノードプラス1に等しいです。

11、いとこ:互いにいとこと同じ層に親ノード。

図12に示すように、ツリーの深さ:ツリーノードの高さと呼ばれるツリーまたは階層の最大深度。

図13は、順序付きおよび順序なしツリーツリー:サブツリーノードの各々は、順序がから(すなわち、互換性がない)左から右にされる場合、ツリーは、さもなければ非として知られ、順序木と呼ばれていますオーダーツリー。左のサブツリーのルートでは順序木は、最初の子、第二子と呼ばれ、右端と呼ばれています。

図14は、森林 ;森のセットであるサブツリーの各ノードのツリーmが互いに素木木の集合です。

バイナリツリーの定義

バイナリツリーは、ツリーTのための空の木が空を有する場合、Nノードからなる集合であり、n = 0:

1、唯一つのノードがルートと呼ばれる存在です。

図2に示すように、ルートノード以外の残りのノードは、2つの互いに素の有限のセットT1、T2に分割することができ、T及び右サブツリー、およびT1とT2の左の部分木と呼ばれている、今度は、二分木。

二進木

バイナリツリーの性質

1、バイナリツリーにおけるi番目の層は、最大で有する2 ^ {I-1}ノード(I> = 1)。

2、最大で深さkのバイナリツリー2 ^ {K} -1-のノード(K> = 1)。

それは、ノードN0の端末番号であれば3、いずれかの二分木Tは、次数2のノードの数がN0 = N2 + 1、N 2です。

同時N0 = N2 + 1を得るために、= N 1 + 2×N 2 + 1(サブツリー及び処理のルート)、N = N0 + N1 + N2(各総和ノード次数)およびN;:簡単には証明します

図4は、完全なバイナリツリーは、n個のノードでの深さを有しています\ lfloor LOG_ {2}のn \ rfloor + 1 \右を左\

図5は、n個のノードのノードとシーケンス番号によって完全二分木場合、任意のノードのために、あります。

(1)I = 1の場合、iはバイナリツリーない親のルートノードであり、I> 1は、それが親ノードである場合lfloor I / 2 \左\ \右\ rfloorを

2Iは、Iない左側の子ノードNよりも大きい場合(2)それ以外の場合は、左の子ノード2Iです。

(3)2I + 1の場合> N、次にない右の子ノードiが、それ以外の場合は、右の子ノード2I + 1です。

完全なバイナリ:深さkとを含む2 ^ {K} -1-ノードのバイナリツリーを、

完全二分木:深さKと呼ばれるノードが1からnまでのノードから番号深さk個の対応の完全なバイナリツリーに関連付けられている場合に限り、それぞれがバイナリツリー内のNノードは、ありますバイナリツリーを完成。

バイナリストレージ構造

ストレージの二つのタイプ:順次ストレージ、ストレージチェーン。

単にストレージ配列とルートから開始することによって、完全二分木に順に格納されているときに、制御しなければならない一般的なバイナリツリー、バイナリツリー上のノードのために、それは、一次元アレイに対応する位置に存在しないことを記憶されています標識されたノード0。

チェーン店:

typedef struct BiTNode{
    char data;
    struct BiTNode *lchild,*rchild;
}BiTNode,*BiTree;

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

シーケンスの先行順走査に基づいて分けることができるプレオーダープレオーダー後順トラバーストラバーサル方法の三種類

次のように先行順走査で動作します。

バイナリツリーが空の場合、そうでない場合は、空:

1、ルートノードにアクセスします。

2、先行順は、サブツリーを残しました。

3、行きがけ右のサブツリー。

他のように。

予約限定:

予約限定再帰アルゴリズム

void PreOrderTraverse(BiTree T){
    if(T){
        cout<<T->data;
        PreOrderTraverse(T->lchild);
        PreOrderTraverse(T->rchild);
    }
    return;
} 

予約限定!

順トラバーサル、再帰的なアルゴリズムでは

void InOrderTraverse(BiTree T){
    if(T){
        PreOrderTraverse(T->lchild);
        cout<<T->data;
        PreOrderTraverse(T->rchild);
    }
    return;
} 

後順

後順再帰的なアルゴリズム

void PostOrderTraverse(BiTree T){
    if(T){
        PreOrderTraverse(T->lchild);
        PreOrderTraverse(T->rchild);
        cout<<T->data;
    }
    return;
} 

アプリケーションバイナリツリートラバーサル

ビルドのバイナリリスト(例えば前順に)

アルゴリズムのステップ:

図1に示すように、文字の走査シーケンスは、文字がCHを読み取ります。

chは二分木である空の木であることを示す「/」文字である場合2、Tは、そうでなければ、次の操作を実行し、NULLです。

(1)空間におけるT接合点を適用します。

(2)CH T->データに割り当てられました。

(3)Tの再帰左サブツリーを作成します。

(4)が再帰的にTの右サブツリーを作成します。

コードの場合:

void CreateTree(BiTree &T){//建树
    char elem;
    cin>>elem;
    if(elem=='/') T=NULL;
    else{
        T=new BiTNode;
        T->data=elem;
        CreateTree(T->lchild);
        CreateTree(T->rchild);
    }
    return;
}

バイナリツリーは、そのような深さ、同じことを考え、言っても過言ではないとして、バイナリツリーの計算アルゴリズムが存在コピー。

スレッド・バイナリ

スレッドバイナリツリー特定のルールノードに基づいて、線形配列でバイナリツリー、バイナリツリー内のノードのシーケンスを取得する第一の配列、およびそれに続く配列の配列順に配置されています。

バイナリツリーノード構造を変更し、左の子ノードへRTAG 2つのフラグフィールド、LTAGが0である、lchildポインティングLTAG増加は、LTAGは、lchildポイントノード前駆1であり; RTAGの時間0、rchild接合点右の子は、RTAGはrchild後続ノードに向け、1です。

木と森

一般的なツリー格納構造:

1、両親表記

記憶部に記憶された隣接ノードツリーのセット、また、その親ノードの位置を示すために、親ドメインに取り付けられたデータフィールドのデータに加えて、各ノード。

このメモリ構造、ツリーのルートノードを見つけるのは簡単、と両親が、評価されたときに、子ノードの全体構造を通過する必要があります

2、子供表記

ツリー内の各ノードはサブツリーのルートに複数のサブツリー、複数の利用可能なリスト、各ポインタ・フィールド点を有することができるので

下に示すように、子と親の表記法と組み合わせること:

3、子供の兄弟表記

2つの最初のノードの子ノードの鎖ドメイン、および次の兄弟ノード、およびドメインという名前のfirstChildドメインnextsiblingへのストレージ構造などのバイナリツリーリストで、リンクリストのノードポイント

次のように図蓄積の結果は以下のとおりです。

森とバイナリ変換

図1は、二分木の森に変換しました

もしF = {T1、T2、...、Tmを}森林、次の規則は、バイナリツリーB =(根、LB、RB)に変換することができます。

Fが空の場合(1)、即ち、M = 0の場合、Bは空の木です。

(2)Fが空でない場合は、即ち、M = 0、ルートは、ルート(T1)の森の木のルートのルートBである;! LB Bは、サブツリーが森林T1内のサブツリーのルートノードである左F1 = {T11、T12、...は、T1M}は二分木から変換され、森林F「の右の部分木からRB = {T2、T3、...、Tmは}バイナリツリーから変換されました。

図2は、二分木の森に変換しました

B =(根、LB、RB)は二分木である場合、次の規則が= {T1、T2、...、Tmを} Fに変換することができます。

Bが空の場合(1)は、その後Fは空です。

森林F1におけるT1のサブツリーのルートノードLBにBの左の部分木によって変換される;(2)Bが空でない場合、ROOT FルートツリーT1は、最初の(T1)Bは、二分木のルートのルートであります森林、木F「」=の森の他の部分に加えて、F. T1 {T2、T3、...、T mは}一緒にRB Bの右の部分木から変換されました。

木と森を横断

1、ツリートラバーサル

図の例上記で。

まずトラバーサル:RADEBCFGHK

后根遍历:D E A B G H K F C R

2、森林的遍历

先序遍历森林(非空):

(1)访问森林中第一棵树的根结点

(2)先序遍历第一棵树的根结点的子树森林

(3)先序遍历除去第一棵树之后剩余的树构成的森林

中序遍历森林(非空):

(1)中序遍历森林中第一棵树的根结点的子树森林

(2)访问第一棵树的根结点

(3)中序遍历除去第一棵树之后剩余的树构成的森林

哈夫曼树及其应用

哈夫曼树又称最优树,是一种带权路径长度最短的树。

结点的带权路径长度:从该结点到树根之间的路径长度与节点上权的乘积;

树的带权路径长度:树中所有叶子节点的带全路径长度之和。

构造过程:

1、根据给定的n个权值{w1,w2,…,wn},构造n棵只有根结点的二叉树,这n棵二叉树构成一个森林F;

2、在森林中选区两棵根结点的权值最小的树作为左右子树构造一棵新的二叉树,且置新的二叉树的根结点的权值为左右子树上根结点的权值之和;

3、在森林F中删除这两棵树,同时将新得到的二叉树加入F中;

4、重复(2)和(3),直到F只含一棵树为止,这棵树便是哈夫曼树。

哈夫曼树存储结构:

typedef struct HTNode{
    int weight;
    int lchild,rchild,parent;
}HTNode,*HTree;

构造哈夫曼树算法:

1、初始化:首先动态申请2n个单元;然后循环2n-1次,从1号单元开始,依次将1至2n-1所有单元中的双亲、左孩子、右孩子的下表都初始化为0;最后再循环n次,输入前n个单元中叶子节点的权值;

2、创建树:循环n-1次,通过n-1次的选择、删除与合并来创建哈夫曼树。选择是从当前森林中选择双亲为0且权值最小的两个数根节点s1和s2,删除是将s1和s2的双亲改为非0;合并就是将s1和s2的权值和作为一个新结点的权值依次存入到数组的第n+1之后的单元中,同时记录着个新结点左孩子的下标为s1,右孩子的下标为s2。

void CreateHMT(HTree &HT,int n){
    int m=2*n-1;
    HT=new HTNode[m+1];
    for(int i=1;i<=m;++i){
        HT[i].lchild=HT[i].rchild=HT[i].parent=0;
    }
    for(int i=1;i<=n;i++){
        cin>>HT[i].weight;
    }
    for(int i=n+1;i<=m;++i){
        int s1,s2;
        SelectMin(HT,i,s1,s2);
        HT[s1].parent=HT[s2].parent=i;
        HT[i].weight=HT[s1].weight+HT[s2].weight;
        HT[i].lchild=s1;HT[i].rchild=s2;
    }
    return;
}

哈夫曼编码

前缀编码:如果一个编码方案中人一个编码都不是其他任何编码的前缀,则称编码是前缀码;

哈夫曼编码:对一棵具有n个叶子的哈夫曼树,若对树中的每个做分支赋予0,右分支赋予1,则从根到每个叶子的路径上,各分支的赋值分别构成一个二进制串,该二进制串就称为哈夫曼编码

哈夫曼编码是最优前缀编码

哈夫曼编码存储表示:

typedef char **HuffmanCode; 

算法步骤:

1、分配n个字符编码的编码表空间HC,长度为n-1;分配临时存储每个字符编码的动态数组空间cd,cd[n-1]置为’\0‘;

2、逐个求解n个字符的编码,循环n次,执行以下操作:

(1)设置变量start用于记录编码在cd中存放的位置,start初始时指向最后,即编码结束符位置n-1;

(2)设置变量c用于记录从叶子结点向上回溯至根结点所经过的结点下标,c初始时为当前待编码字符的下标i,f用于记录双亲结点的下标;

(3)从叶子节点向上回溯至根结点,求得字符i的编码,当f没有到达根结点时,循环执行以下操作:

回溯依次start向前指一个位置,即--start;

若结点c是f的左孩子,则生成编码0,否则生成编码1,保存在cd[start]中;

继续向上回溯,改变c和f的值。

(4)根据数组cd的字符串长度为第i个字符编码分配空间HC[i],然后将数组cd中的编码复制到HC[i]中;

3、释放临时空间cd。

代码实现;

void CreatHFC(HTree HT,HFMCode &hc,int n){
    hc=new char*[n+1];//开n+1个1维数组空间
    char* cd=new char[n];//临时数组
    cd[n-1]='\0';
    for(int i=1;i<=n;++i){
        int start=n-1;  
        int c=i;
        int f=HT[i].parent;
        while(f!=0){
            start--;
            if(HT[f].lchild==c)
            cd[start]='0';
            else cd[start]='1';
            c=f;
            f=HT[f].parent;
        }
        hc[i]=new char[n-start];
        strcpy(hc[i],&cd[start]);
    }
    delete cd;
    return;
}

附上一条龙完整代码:

#include<bits/stdc++.h>
using namespace std;
typedef struct HTNode{
    char name;
    int weight;
    int lchild,rchild,parent;
}HTNode,*HTree;
typedef char** HFMCode;
void SelectMin(HTree HT,int n,int &s1,int &s2){
    s1=s2=0;
    int i;
    for(i=0;i<n;++i){//先将s1,s2赋上值,
        if(HT[i].parent==0){//从未被选过的元素,即双亲为0的元素中挑选
            if(s1==0){
                s1=i;
            }
            else {
                s2=i;
                break;//不要忘了!!此时s1、s2都已赋值成功,直接跳出
            }
        }
    }
    if(HT[s1].weight>HT[s2].weight){//把s1作为较小的元素
        int t=s1;
        s1=s2;
        s2=t;
    }
    for(;i<n;++i){          //接着开始比较
        if(HT[i].parent==0){
            if(HT[i].weight<HT[s1].weight){
                s2=s1;
                s1=i;
            }
            else if(HT[i].weight<HT[s2].weight){
                s2=i;
            }
        }
    }
    return;
}
void CreateHMT(HTree &HT,int n){
    int m=2*n-1;
    HT=new HTNode[m+1];
    for(int i=1;i<=m;++i){
        HT[i].lchild=HT[i].rchild=HT[i].parent=0;
    }
    cout<<"请输入各个元素字母及权重:\n";
    for(int i=1;i<=n;i++){
        cin>>HT[i].name>>HT[i].weight;
    }
    for(int i=n+1;i<=m;++i){
        int s1,s2;
        SelectMin(HT,i,s1,s2);
        HT[s1].parent=HT[s2].parent=i;
        HT[i].weight=HT[s1].weight+HT[s2].weight;
        HT[i].lchild=s1;HT[i].rchild=s2;
    }
    return;
}
void CreatHFC(HTree HT,HFMCode &hc,int n){
    hc=new char*[n+1];//开n+1个1维数组空间
    char* cd=new char[n];//临时数组
    cd[n-1]='\0';
    for(int i=1;i<=n;++i){
        int start=n-1;  
        int c=i;
        int f=HT[i].parent;
        while(f!=0){
            start--;
            if(HT[f].lchild==c)
            cd[start]='0';
            else cd[start]='1';
            c=f;
            f=HT[f].parent;
        }
        hc[i]=new char[n-start];
        strcpy(hc[i],&cd[start]);
    }
    delete cd;
    return;
}
void Print(HTree HT,HFMCode HC,int n){
    for(int i=1;i<=n;++i){
        cout<<HT[i].name<<":"<<HC[i]<<endl;
    }
    return;
}
int main(){
    HTree HT;
    int n;
    cout<<"请输入元素个数:";
    cin>>n;
    CreateHMT(HT,n);
    HFMCode HC;
    CreatHFC(HT,HC,n);
    Print(HT,HC,n);
    return 0;
}

测试样例:

————————————————————————————————————————————————————————

出典:http://data.biancheng.net/view/30.html

https://www.cnblogs.com/ciyeer/p/9045746.html

https://blog.csdn.net/qq_25775935/article/details/88647758

注:この記事はすべてのコンテンツは、「データ構造(C言語第二版)」(ヤン魏ミン教師A)に由来しています

リリース6元記事 ウォンの賞賛0 ビュー123

おすすめ

転載: blog.csdn.net/Roy_F_M/article/details/103839968