数据结构-树(基本概念整合)

    xiaoxiao2021-04-17  39

    树是最优美的形状(世间万物皆有其树结构)

    ①树的基本概念

    (Tree)是n(n>=0)个结点的有限集。

    在任意一棵非空树中:

    (1)有且仅有一个特定的称为(Root)的结点;

    (2)当n>1时,其余结点可分为m(m>0)个互不相交的有限集T1,T2,...,Tn,其中每个集合本身又是一棵树,并称为根的子树(SubTree)

    层次                     树

      1                         A

                             /    |     \

      2                 B      C      D

                       /   \      |     /   |   \

      3            E     F   G   H   I   J

                   /  \                |

      4         K   L              M

    ★树的结点包含一个数据元素及若干指向其子树的分支。

    (1)度

    结点拥有的子树树称为结点的度(Degree)如:上图A的度为3,C的度为1,F的度为0。

    度为0的结点称为叶子(Leaf)或终端结点,如:上图K,L,F,G,M,I,J都是树的叶子

    度不为0的结点称为非终端结点分支结点,如:上图A,B,C,D,E,H

    树的度是树内各节点的度的最大值,如:上图的树的度为 3 。

    (2)结点(家谱图)

    结点的子树的根称为该结点的孩子(Child),相应地,该结点称为孩子的双亲(Parent)如:上图D是A的孩子,A是D的双亲。

    同一个双亲的孩子叫兄弟(Sibling)如:上图H,I,J为互为兄弟

    其双亲在同一层的结点互为堂兄弟。如上图G与E、F、H、I、J互为堂兄弟

    结点的祖先是从根到该结点所经分支上的所有结点。如:上图M的祖先为A、D、H

    (3)层次,深度(你家几代同堂啊?)

    结点的层次(Level)从根开始定义起,根为第一层,根的孩子为第二层,

    树中结点的最大层次成为树的深度(Depth)或高度。如:上图树的深度为4

    (4)有序树与无序树(长子,次子。。)

    树中结点的各子树看成从左至右是有次序的(即不能互换),则称该树为有序树,否则成为无序树

    有序树中最左的子树的根称为第一个孩子,最右边的称为最后一个孩子。(毕竟有序,排好了谁是老大,谁是老二,长兄有序嘛)

    森林(Forest)是m(m>=0)棵互不相交的树的集合。对树中每个结点而言,其子树的集合即为森林。

    由此也可以森林和树相互递归的定义来描述树。

    ②二叉树

    1、二叉树的定义及其主要特征

    二叉树(Binary Tree)是另一种树形结构,

    ★二叉树的特点是:(1)每个结点至多只有两棵子树(即二叉树中不存在度大于2的结点),

                          (2)二叉树的子树有左右之分,其次序不能随意颠倒。

    二叉树要么为空,要么是由一个根结点加上两棵分别称为左子树和右子树的,互不相交的二叉树组成。

    ★二叉树的五种形态

                |                  |            A      |            A          |           A

       Φ      |        A        |      B            |       B      C      |               B

    (1)   |    (2)     |        (3)   |        (4)       |       (5)

    ★二叉树的性质

    (性质一)在二叉树的第 i 层上至多有 2^(i-1)个结点 (i >=1)

                      第层 :1(2^0)         第层:2(2^1)          第层:4(2^2)        第层:8(2^3)

    (性质二)深度为 k 的二叉树至多有2^k -1个结点(k>=1)

                       由性质一得:第k层,2^k - 1 个结点

                        1+2+2^2+....+2^(k-1) = ( 1 - 2^k)/(1-2) = 2^k - 1 

    (性质三)★对任何一棵二叉树 T ,如果其终端结点数位n0,度为2的结点数位n2,则n0=n2+1。

                       式一:  n = n0 + n1 + n2 (结点总数   等于   度为0   加   度为1   加   度为2)

                       式二:  n = n0 + 2*n2 +1(n = 分支总数+1  ;分支总数 = n1+n2 (分支是由度为1,度为2的结点射出的))

                        式二 - 式一得: n0 = n2 + 1

    完全二叉树

    一棵深度为 k 且有2^k - 1个结点的二叉树为满二叉树

    (性质一)具有n个结点的完全二叉树的深度为 」 log 2 n」+1 

                       由普通的二叉树性质二得:深度为k,结点数最多2^k - 1个,那么log一下就出来了

    (性质二)如果对一棵有n个结点的完全二叉树(其深度为」 log 2 n」+1 )的结点按层序编号(从第一层到最后一层,每层从左到右),对任一结点i(1<= i <=n)有

               (1)如果 i =1,则结点 i 是二叉树的根,无双亲;

                        如果 i >1,则其双亲Parent( i ) 是结点  」i / 2」

               (2)如果2*i >n,则结点 i 无左孩子(结点 i 为叶子节点) ;否则其左孩子LChild( i )是结点2*i。

               (3)如果2*i+1>n,则结点 i 无右孩子;否则其右孩子RChild( i )是结点2*i+1。

               【这个性质,对于建立二叉树,有着非凡的意义】

                 且看下面这个完全二叉树:

                                       1

                               /                   \

                           2                         3                                                            [ i / 2 ]

                       /      \                  /           \                                                 /                  \

                   4          5              6             7                                         i                           i+1

                 /  \         /    \         /     \         /    \                                /        \                     /         \

                8   9     10   11    12    13    14   15                           2 i     2 i+1            2 i+2    2 i+3

    【性质二通俗版】从下往上看:结点1是根,没双亲;其他点的双亲是   i / 2 取下值

                                 从上往下看:左孩子 是根节点的两倍(偶数),右孩子是根节点的两倍+1(奇数)

    2、二叉树的顺序存储结构和链式存储结构

    顺序存储结构:

    #define MAX_TREE_SIZE 100 //二叉树最大结点数 typedef TElemTyle SqBiTree[MAX_TREE_SIZE]; //0号单元存储根节点 SqBiTree bt;[1]  [2]  [3]  [4]  [5]  [6]  [7]  [8]  [9]  [10] [11] [12]

    [1]  [2]  [3]  [4]  [5]  [0]  [0]  [0]  [0]  [6]   [7]    [8]

    链式存储结构

    typedef struct BiTNode{ TElemType date; struct BiTNode *lchild,*rchild;//左右孩子指针 }BiTNode,*BiTree;                               [ ] [A] [^]                               /           

                            [ ] [B] [ ]

                          /             \

                  [^] [C] [^]      [ ] [D] [ ]    

                                    /             \

                          [^] [E] [ ]       [^] [F] [^]

                                     \

                                [^] [G] [^]

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

    不同的建二叉树技巧:

    【链式建树】【树形选择排序】【线段树——简单类型建树(int,char)】【线段树——结构体建树(详解推荐)】

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

    3、二叉树的遍历:传送门(建树,前序,中序,后序,中序非递归,深度,叶子数,节点数)

    (1)先序遍历二叉树   (根>左>右)

    (2)中序遍历二叉树   (左>根>右)

    (3)后序遍历二叉树   (左>右>根)

    (4)层次遍历二叉树

    4、线索二叉树的基本概念和构造(传送门)

                     [ lchild ] [ LTag ] [ data ] [ RTag ] [ rchild ] 

    LTag  = { 0 : lchild 域指示结点的左孩子 1 : lchild 域指示结点的前驱 } 

    RTag = { 0 : rchild 域指示结点的右孩子 2 : rchild 域指示结点的后继 }

    以这种结点结构构成的二叉链表作为二叉树的存储结构,叫做线索链表,其中,指向结点前驱和后继的指针,叫做线索

    加上线索的二叉树叫做线索二叉树(Threaded Binary Tree)

    对二叉树以某种次序遍历使其变成线索二叉树的过程叫做线索化

    (1)前序线索二叉树

    (2)中序线索二叉树

    (3)后序线索二叉树

    ③树、森林

    1、树的存储结构

                                                     A

                                               ↙      ↘

                                           B                 C

                                       ↙               ↙       ↘

                                     D               E               F

                                ↙  ↓  ↘           ↘

                             G     H      I              J

         一、双亲表示法(自下而上

        我们假设以一组连续空间存储树的结点,同时在每个结点中,附设一个指示器指示其双亲结点在数组中的位置。

    每个结点:【data】【parent】 

    其中data:数据域,存储结点的数据信息。parent:指针域,存储该结点的双亲在数组找那个的下标。

    #define MAX_TREE_SIZE 100 typedef int TElemType; //树节点的数据类型 typedef struct PTNode{ TElemType data; //结点数据 int parent; //双亲位置 }PTNode; typedef struct{ PTNode nodes[MAX_TREE_SIZE]; //结点数组 int r,n; //根的位置和结点数 }PTree;链式: typedef struct PTrNode{ TElemType data; //结点数据 struct PTrNode *parent; //指向双亲的指针 }PTrNode; 下标0123456789dataABCDEFGHIJparent-1001223334

         二、孩子表示法

        树中每个结点可能有多课子树,可以考虑用多重链表,即每个结点有多个指针域,其中每个指针指向一棵子树的根节点,这种方法叫做多重链表表示法

    方案一:

    每个结点【data】【child1】【child2】【child3】....【childd】

    其中data是数据域,child1~childd是指针域,用来指向该结点的孩子结点。指针个数为树的度数(每个结点的孩子结点个数为树的度,即最多大的孩子结点个数)

    【缺点:这样很浪费空间】

    方案二:

    每个结点【data】【degree】【child1】【child2】【child3】....【childd】

    其中data是数据域,degree是度域,child1~childd是指针域,用来指向该结点的孩子结点。

    【这样就能减少空指针的浪费】

    方案三:

    用两种结点结构:

    表头数组的表头节点:【data】【firstchild】其中:data是数据域,存储某结点的数据信息。firstchild是头指针域,存储孩子链表的头指针。

    孩子链表的孩子结点:【child】【next】其中:child是数据域,存储某个节点在表头数组中的下标,next是指针域,指向下一个孩子结点的指针。

    #define MAX_TREE_SIZE 100 typedef int TElemType; //树节点的数据类型 typedef struct CTNode{ //孩子结点 int child; struct CTNode *next; }*ChildPrt; typedef struct{ //表头结构 TElemType data; ChildPtr firstchild; }CTBox; typedef struct{ //树结构 CTBox nodes[MAX_TREE_SIZE]; //结点数组 int r,n; //根的位置和结点数 }CTree; 下标   data    firstchild             child     next   

       0        【A】      【   】    →   →   →   【 1 】     【    】    →   →  → 【 2 】 【 ^ 】

       1        【B】      【   】    →   →   →    【 3 】    【 ^ 】    

       2        【C】      【   】    →   →   →    【 4 】    【    】    →   →  → 【 5 】 【 ^ 】

       3        【D】      【   】    →   →   →    【 3 】    【    】    →   →  →  【 7 】 【   】   →   →  【 8 】 【 ^ 】

       4        【E】      【   】    →   →   →    【 9 】    【 ^ 】

       5        【F】      【 * 】  

       6        【G】      【 * 】

       7        【H】      【 * 】    

       8        【I 】      【 * 】

       9        【J】      【 * 】

          双亲孩子表示法:(结合上面两种表示法)

    下标    data   parent   firstchild             child     next   

       0        【A】    【 -1 】  【   】    →   →   →   【 1 】     【    】    →   →  → 【 2 】 【 ^ 】

       1        【B】    【 0  】  【   】    →   →   →    【 3 】    【 ^ 】    

       2        【C】    【 0  】  【   】   →   →   →    【 4 】    【    】    →   →  → 【 5 】 【 ^ 】

       3        【D】    【 1  】 【   】    →   →   →    【 3 】    【    】    →   →  →  【 7 】 【   】   →   →  【 8 】 【 ^ 】

       4        【E】     【 2 】  【   】    →   →   →    【 9 】    【 ^ 】

       5        【F】     【 2 】 【 * 】  

       6        【G】     【 3 】 【 * 】

       7        【H】     【 3 】 【 * 】    

       8        【I 】     【 3 】 【 * 】

       9        【J】     【 4 】 【 * 】

         三、孩子兄弟表示法

    结点结构:【data】【firstchild】【rightsib】

    其中data为数据域,firstchild为指针域,存储该节点的第一个孩子结点存储地址,rightsub是指针域,存储该节点的右兄弟结点的存储地址。

    typedef int TElemType; //树节点的数据类型 typedef struct CSNode{ TElemType data; struct CSNode * firstchild,*rightsib; }CSNode , *CSTree;【 A 】【  】【 ^ 】                 ↓

    【 B 】【  】【 ^ 】  →      →     →     →     →     →     →     →     →     →    【 C 】【  】【 ^ 】

                    ↓                                                                                                                      ↓

    【 D 】【  】【 ^ 】                                                                                        【 E 】【  】【   】 →  【 F 】【 ^ 】【 ^ 】

                    ↓                                                                                                                      ↓

    【 G 】【 ^ 】【  】 →  【 H 】【 ^ 】【  】  →  【 I 】【 ^ 】【 ^ 】          【 J 】【 ^ 】【 ^ 】

    斜着看,他其实是一棵二叉树。(根据代码也可以看出来)

    根据二叉树的特性来处理树

    2、森林和二叉树的转换

         一、森林转换成二叉树

              如果F={ T1,T2,....,Tm }是森林,则可按如下规则转换成一棵二叉树B=(root,LB,RB)

            (1)若F为空,即m=0,则B为空树。

            (2)若F非空,即m≠0,则B的根root即为森林中第一棵树的根ROOT(T1);

                  B的左子树LB是从T1中根结点的子树森林F1= { T11,T12,...,T1m1} 转换而成的二叉树

                  B的右子树RB是从森林F‘ = { T2,T3,.....,Tm}转换而来的二叉树。

         二、二叉树转换成森林

              如果B=(root,LB,RB)是一棵二叉树,则可按如下规则转换成森林F={ T1,T2,....,Tm }

            (1)若B为空,则F为空。

            (2)若B非空,则F中第一棵树T1的根root即为二叉树B的根root;

                  T1中根结点的子树森林F1是由B的左子树LB转换而成的森林;

                  F中除T1之外其余树组成的森林F‘ = { T2,T3,.....,Tm}是由B的右子树RB转换而成的森林。

    3、树与森林的遍历

           一、先序遍历森林

           若森林非空,则可按下述规则遍历之;

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

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

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

           二、中序遍历森林

           若森林非空,则可按下述规律遍历之;

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

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

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

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

    特别的建树技巧

    【并查集建树(利用数组的一对多特性)双亲表示法

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

    ④树与二叉树的应用

    1、二叉排序树

    2、平衡二叉树

    3、哈夫曼树和哈夫曼编码。

    转载请注明原文地址: https://ju.6miu.com/read-674034.html

    最新回复(0)