数据结构之二叉树

6.1 树的基本概念

6.1.1 树的定义和基本术语

1. 树的定义

树(Tree)是 n n n ( n ≥ 0 n\ge0 n0) 个结点的有限集合 T T T,若 n = 0 n=0 n=0 时称为空树,否则:

⑴ 有且只有一个特殊的称为树的根(Root)结点;
⑵ 若 n > 1 n>1 n>1 时,其余的结点被分为 m ( m > 0 ) m(m>0) m(m>0) 个互不相交的子集 T 1 , T 2 , T 3 , … , T m T_1, T_2, T_3,…,T_m T1,T2,T3,,Tm,其中每个子集本身又是一棵树,称其为根的子树(Subtree)。

这是树的递归定义,即用树来定义树,而只有一个结点的树必定仅由根组成,如图6-1(a)所示。

在这里插入图片描述

2. 树的基本术语

结点(node):一个数据元素及其若干指向其子树的分支。

结点的度(degree) 、树的度:结点所拥有的子树的棵数称为结点的度。树中结点度的最大值称为树的度。

叶子(leaf)结点、非叶子结点:树中度为0的结点称为叶子结点(或终端结点)。相对应地,度不为0的结点称为非叶子结点(或非终端结点或分支结点)。除根结点外,分支结点又称为内部结点。

孩子结点、双亲结点、兄弟结点
一个结点的子树的根称为该结点的孩子结点(child)或子结点;相应地,该结点是其孩子结点的双亲结点(parent)或父结点。同一双亲结点的所有子结点互称为兄弟结点。

层次、堂兄弟结点
双亲结点在同一层上的所有结点互称为堂兄弟结点。如图6-1(b)中结点 E、F、G、H、I、J。

结点的层次路径、祖先、子孙

从根结点开始,到达某结点 p p p 所经过的所有结点成为结点 p p p 的层次路径(有且只有一条)。结点 p p p 的层次路径上的所有结点( p p p 除外)称为 p p p 的祖先(ancester) 。以某一结点为根的子树中的任意结点称为该结点的子孙结点(descendant)。

树的深度(depth):树中结点的最大层次值,又称为树的高度

有序树和无序树:对于一棵树,若其中每一个结点的子树(若有)具有一定的次序,则该树称为有序树,否则称为无序树。

森林(forest):是 m ( m ≥ 0 ) m(m\ge 0) m(m0) 棵互不相交的树的集合。显然,若将一棵树的根结点删除,剩余的子树就构成了森林。

3. 树的表示形式

⑴ 倒悬树。是最常用的表示形式,如图6-1(b)。

⑵ 嵌套集合。是一些集合的集体,对于任何两个集合,或者不相交,或者一个集合包含另一个集合。图6-2(a)是图6-1(b)树的嵌套集合形式。

在这里插入图片描述

⑶ 广义表形式。图6-2(b)

⑷ 凹入法表示形式。见P120

树的表示方法的多样化说明了树结构的重要性。



6.2 二叉树

6.2.1 二叉树的定义

二叉树(Binary tree)是 n ( n ≥ 0 ) n(n≥0) n(n0)个结点的有限集合。若n=0时称为空树,否则:
⑴ 有且只有一个特殊的称为树的根(Root)结点;
⑵ 若 n > 1 n>1 n>1时,其余的结点被分成为二个互不相交的子集 T 1 T_1 T1, T 2 T_2 T2,分别称之为左、右子树,并且左、右子树又都是二叉树。

由此可知,二叉树的定义是递归的。

二叉树在树结构中起着非常重要的作用。因为二叉树结构简单,存储效率高,树的操作算法相对简单,且任何树都很容易转化成二叉树结构。上节中引入的有关树的术语也都适用于二叉树。

6.2.2 二叉树的性质

性质1:在非空二叉树中,第 i i i 层上至多有 2 i − 1 2^{i-1} 2i1 个结点 ( i ≥ 1 ) (i\ge1) (i1)

可用数学归纳法证明,略。

性质2:深度为 k k k 的二叉树至多有 2 k − 1 2^k-1 2k1 个结点( k ≥ 1 k\ge 1 k1) 。

性质3:对任何一棵二叉树,若其叶子结点数为 n 0 n_0 n0,度为2的结点数为 n 2 n_2 n2,则 n 0 = n 2 + 1 n_0=n_2+1 n0=n2+1

证明:设二叉树中度为 1 的结点数为 n 1 n_1 n1,二叉树中总结点数为 N N N,因为二叉树中所有结点度均小于或等于 2,则有: N = n 0 + n 1 + n 2 N=n_0+n_1+n_2 N=n0+n1+n2

再看二叉树中的分支数:除根结点外,其余每个结点都有唯一的一个进入分支,而所有这些分支都是由度为 1 和 2 的结点射出的。设B为二叉树中的分支总数,则有: N = B + 1 N=B+1 N=B+1

∴ B = n 1 + 2 × n 2 ∴ N = B + 1 = n 1 + 2 × n 2 + 1 ∴ n 0 + n 1 + n 2 = n 1 + 2 × n 2 + 1 ∴ n 0 = n 2 + 1 \begin{array}{ll} \therefore & B=n_{1}+2 \times n_{2} \\ \therefore & N=B+1=n_{1}+2 \times n_{2}+1 \\ \therefore & n_{0}+n_{1}+n_{2}=n_{1}+2 \times n_{2}+1 \\ \therefore & n_{0}=n_{2}+1 \end{array} B=n1+2×n2N=B+1=n1+2×n2+1n0+n1+n2=n1+2×n2+1n0=n2+1

满二叉树和完全二叉树

  • 满二叉树

一棵深度为 k k k 且有 2 k − 1 2^k-1 2k1 个结点的二叉树称为满二叉树(Full Binary Tree)。

满二叉树的特点:
◆ 基本特点是每一层上的结点数总是最大结点数。
◆ 满二叉树的所有的支结点都有左、右子树。
◆ 可对满二叉树的结点进行连续编号,若规定从根结点开始,按“自上而下、自左至右”的原则进行。

  • 完全二叉树

完全二叉树(Complete Binary Tree):如果深度为 k k k,由 n n n 个结点的二叉树,当且仅当其每一个结点都与深度为 k k k 的满二叉树中编号从 1 1 1 n n n 的结点一一对应,该二叉树称为完全二叉树。

或深度为 k k k 的满二叉树中编号从 1 1 1 n n n 的前 n n n 个结点构成了一棵深度为 k k k 的完全二叉树。其中 2 k − 1 ≤ n ≤ 2 k − 1 2^{k-1} \le n\le 2^{k}-1 2k1n2k1

完全二叉树是满二叉树的一部分,而满二叉树是完全二叉树的特例。

完全二叉树的特点:

若完全二叉树的深度为 k k k ,则所有的叶子结点都出现在第 k k k 层或 k − 1 k-1 k1 层。对于任一结点,如果其右子树的最大层次为 l l l,则其左子树的最大层次为 l l l l + 1 l+1 l+1

性质4 n n n 个结点的完全二叉树深度为: ⌊ log ⁡ 2 n ⌋ + 1 \left\lfloor\log _{2} n\right\rfloor+1 log2n+1

k = ⌊ log ⁡ 2 n ⌋ + 1 k=\left\lfloor\log _{2} n\right\rfloor+1 k=log2n+1

性质5:若对一棵有 n n n 个结点的完全二叉树(深度为 ⌊ log ⁡ 2 n ⌋ + 1 \left\lfloor\log _{2} n\right\rfloor+1 log2n+1 的结点按层(从第 1 1 1 层到第 ⌊ log ⁡ 2 n ⌋ + 1 \left\lfloor\log _{2} n\right\rfloor+1 log2n+1 层)序自左至右进行编号,则对于编号为 i ( 1 ≤ i ≤ n ) i(1\le i\le n) i(1in) 的结点:

(1) 若 i = 1 i=1 i=1:则结点 i i i 是二叉树的根,无双亲结点;否则,若 i > 1 i>1 i>1,则其双亲结点编号是 ⌊ i / 2 ⌋ \left\lfloor i/2\right\rfloor i/2

(2)如果 2 i > n 2i>n 2i>n:则结点 i i i 为叶子结点,无左孩子;否则,其左孩子结点编号是 2 i 2i 2i

(3)如果 2 i + 1 > n 2i+1>n 2i+1>n:则结点 i i i 无右孩子;否则,其右孩子结点编号是 2 i + 1 2i+1 2i+1

可以用数学归纳法证明。

在这里插入图片描述

6.2.3 二叉树的存储结构

1. 顺序存储结构

二叉树存储结构的类型定义:

#define MAX_SIZE 100
typedef telemtype sqbitree[MAX_SIZE];

用一组地址连续的存储单元依次“自上而下、自左至右”存储完全二叉树的数据元素。

对于完全二叉树上编号为 i i i 的结点元素存储在一维数组的下标值为 i − 1 i-1 i1 的分量中,如图6-6(c)所示。

对于一般的二叉树,将其每个结点与完全二叉树上的结点相对照,存储在一维数组中,如图6-6(d)所示。

最坏的情况下,一个深度为 k k k 且只有 k k k 个结点的单支树需要长度为 2 k − 1 2^k-1 2k1 的一维数组。

在这里插入图片描述

2. 链式存储结构

设计不同的结点结构可构成不同的链式存储结构。
① 二叉链表结点。有三个域:一个数据域,两个分别指向左右子结点的指针域,如图6-7(a)所示。

typedef struct BTNode
{
    ElemType data;
    struct BTNode *Lchild, *Rchild;
} BTNode;

② 三叉链表结点。除二叉链表的三个域外,再增加一个指针域,用来指向结点的父结点,如图6-7(b)所示。

typedef struct BTNode_3
{
    ElemType data;
    struct BTNode_3 *Lchild, *Rchild, *parent;
} BTNode_3;

在这里插入图片描述






6.3 遍历二叉树及其应用

(考试必考,给你两种遍历方式来给出第三种遍历方式)

遍历二叉树(Traversing Binary Tree):是指按指定的规律对二叉树中的每个结点访问一次且仅访问一次。

对于二叉树的遍历,分别讨论递归遍历算法非递归遍历算法。递归遍历算法具有非常清晰的结构,但初学者往往难以接受或怀疑,不敢使用。实际上,递归算法是由系统通过使用堆栈来实现控制的。而非递归算法中的控制是由设计者定义和使用堆栈来实现的。

若以L、D、R分别表示遍历左子树、遍历根结点和遍历右子树,则有六种遍历方案:DLR、LDR、LRD、DRL、RDL、RLD。若规定先左后右,则只有前三种情况三种情况,分别是:

  • DLR——先(根)序遍历。
  • LDR——中(根)序遍历。
  • LRD——后(根)序遍历。

6.3.1 先序遍历二叉树

1. 递归算法

算法的递归定义是:
若二叉树为空,则遍历结束;否则
⑴ 访问根结点;
⑵ 先序遍历左子树(递归调用本算法);
⑶ 先序遍历右子树(递归调用本算法)。

void PreorderTraverse(BTNode *T)
{
    if (T != NULL)
    {
        visit(T->data); /*  访问根结点  */
        PreorderTraverse(T->Lchild);
        PreorderTraverse(T->Rchild);
    }
}

说明:visit()函数是访问结点的数据域,其要求视具体问题而定。树采用二叉链表的存储结构,用指针变量T来指向。

2. 非递归算法

T是指向二叉树根结点的指针变量,非递归算法是:
若二叉树为空,则返回;否则,令p=T
(1)访问p所指向的结点;
(2)q=p->Rchild ,若q不为空,则q进栈;
(3)p=p->Lchild ,若p不为空,转(1),否则转(4);
(4) 退栈到p ,转(1),直到栈空为止。

#define MAX_NODE 50
void PreorderTraverse(BTNode *T)
{
    BTNode *Stack[MAX_NODE], *p = T, *q;
    int top = 0;
    if (T == NULL)
        printf(“ Binary Tree is Empty !\n”);
    else
    {
        do
        {
            visit(p->data);
            q = p->Rchild;
            if (q != NULL)
                stack[++top] = q;
            p = p->Lchild;
            if (p == NULL)
            {
                p = stack[top];
                top--;
            }
        } while (p != NULL);
    }
}

6.3.2 中序遍历二叉树

1. 递归算法

算法的递归定义是:
若二叉树为空,则遍历结束;否则
⑴ 中序遍历左子树(递归调用本算法);
⑵ 访问根结点;
⑶ 中序遍历右子树(递归调用本算法)。

void InorderTraverse(BTNode *T)
{
    if (T != NULL)
    {
        InorderTraverse(T->Lchild);
        visit(T->data); /*   访问根结点   */
        InorderTraverse(T->Rchild);
    }
}
2. 非递归算法

设T是指向二叉树根结点的指针变量,非递归算法是:
若二叉树为空,则返回;否则,令p=T
(1) 若p不为空,p进栈, p=p->Lchild
(2)否则(即p为空),退栈到p,访问p所指向的结点;
(3) p=p->Rchild ,转(1);
直到栈空为止。

#define MAX_NODE 50
void InorderTraverse(BTNode *T)
{
    BTNode *Stack[MAX_NODE], *p = T;
    int top = 0, bool = 1;
    if (T == NULL)
        printf(“ Binary Tree is Empty !\n”);
    else
    {
        do
        {
            while (p != NULL)//一直入栈到最左侧的叶子结点
            {
                stack[++top] = p;
                p = p->Lchild;
            }
            if (top == 0)
                bool = 0;
            else
            {
                p = stack[top];
                top--;
                visit(p->data);
                p = p->Rchild;
            }
        } while (bool != 0);
    }
}

6.3.3 后序遍历二叉树

如图6-9所示的二叉树表示表达式: ( a + b ∗ ( c − d ) − e / f ) (a+b*(c-d)-e/f) (a+b(cd)e/f)
按不同的次序遍历此二叉树,将访问的结点按先后次序排列起来的次序是:

其先序序列为: − + a ∗ b − c d / e f -+a*b-cd/ef +abcd/ef
其中序序列为: a + b ∗ c − d − e / f a+b*c-d-e/f a+bcde/f
其后序序列为: a b c d − ∗ + e f / − abcd-*+ef/- abcd+ef/

在这里插入图片描述
在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

No_one-_-2022

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值