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

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

①树的基本概念

(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;
下标0123456789
dataABCDEFGHIJ
parent-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、哈夫曼树和哈夫曼编码。


  • 23
    点赞
  • 72
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值