树,二叉树,森林
王卓老师的数据结构课程笔记
树和二叉树
定义
-
结点之间有分支,具有层次关系
-
是n个结点的有限集。
- 若n = 0,称为空树;
- 若n > 0,则它满足如下两个条件:
- 有且仅有一个特定的称为根的结点;
- 其余结点可分为m个互不相交的有限集T1,T2,T3,…,Tm,其中每一个集合本身又是一棵树,并称为根的子树。
- 显然,树的定义是一个递归的定义。
-
树的其他表示方式:
- 嵌套集合
- 广义表
- 凹入表示
树的基本术语
-
根结点:非空树中无前驱结点的结点
-
结点的度:结点拥有的子树数
-
树的度:树内各结点的度的最大值
-
度不等于0的结点被称为分支结点,非终端结点,根结点意外的分支结点称为内部结点
-
度等于0的结点被称为叶子结点,也被称为终端结点
-
结点的子树的根称为该结点的孩子,该结点称为孩子的双亲。
-
一些结点有共同的双亲,这些结点被称为兄弟结点。
-
双亲位于同一层的结点,被称为堂兄弟。
-
结点的祖先:从根到该结点所经分支上的所有结点。
-
结点的子孙:以某结点为根的子树中的任一结点。
-
树的深度:树中结点的最大层次。
-
有序树:树中结点的各子树从左至右有次序(最左边的为第一个孩子)。
-
无序树:树中结点的各子树无次序。
-
森林:是m(m>=0)棵互不相交的树的集合。
- 一棵树可以看成是一个特殊的森林。
- 给森林中的各子树加上一个双亲结点,森林就变成了树。
- 树一定是森林,森林不一定是树。
二叉树的定义
- 一个结点最多只有两个子节点;
- 二叉树的结构最简单,规律性最强;
- 所有的树都能转化成唯一对应的二叉树,不失一般性。
- 普通树(多叉树)若不转化为二叉树,则运算很难实现;
- 这样可以解决树的存储结构辑器运算中存在的复杂性。
- 二叉树是n(n>=0)个结点的有限集,它或者是空集(n = 0),或者由一个根结点及两棵不相交的分别称作这个根的左子树和右子树的二叉树组成。
- 特点:
- 每个结点最多有两个孩子(二叉树中不存在度大于2的结点)。
- 子树有左右之分,其次序不能颠倒。
- 二叉树可以是空集合,根可以有空的左子树或空的右子树。
二叉树不是树的特殊情况,它们是两个概念。
- 二叉树结点的子树要区分左子树和右子树,即使只有一棵子树也要进行区分,说明它是左子树,还是右子树。
- 树当结点只有一个孩子时,就无需区分它时左还是右的次序。因此二者是不同的。这是二叉树与树的最主要的差别。
- 具有两个结点的二叉树有两种状态(双亲结点和左孩子结点以及双亲结点和右孩子结点)
- 也就是说,二叉树每个结点的位置或者说次序都是固定的,可以是空的,但是不可以说它没有位置,而树的结点位置是相对与别的结点来说的,没有别的结点时,它就无所谓左右了。
案例
- 数据压缩问题:
- 将数据文件转换成由0、1组成的二进制串,称之为编码。
- 等长编码方案
- 不等长编码方案1
- 不等长编码方案2
- 将数据文件转换成由0、1组成的二进制串,称之为编码。
- 利用二叉树求解表达式的值:
- 以二叉树表示表达式的递归定义如下:
- 若表达式为数或简单变量,则相应二叉树中仅有一个根结点,其数据域存放该表达式信息;
- 若表达式为“第一操作数 运算符 第二操作数”的形式,则相应的二叉树中以左子树表示第一操作数,右子树表示第二操作数,根结点的数据域存放运算符(若为一元运算符,则左子树为空),其中,操作数本身又为表达式。
- 以二叉树表示表达式的递归定义如下:
抽象数据类型定义
-
具有相同特性的数据元素的集合
-
二叉树的建立CreateBiTree(&T,definition)
- 初始条件:definition给出二叉树T的定义。
- 操作结果:按照definition构造二叉树T。
-
先序遍历PreOrderTraverse(T)
- 初始条件:二叉树T存在。
- 操作结果:先序遍历T,对每个结点访问一次。
-
中序遍历InOrderTraverse(T)
- 初始条件:二叉树T存在。
- 操作结果:中序遍历T,对每个结点访问一次。
-
后序遍历PostOrderTraverse(T)
- 初始条件:二叉树T存在
- 操作结果:后序遍历T,对每个结点访问一次。
二叉树的性质和存储结构
- 在二叉树的第i层上至多又2^(i-1)个结点(i>=1).
- 深度为k的二叉树至多有2^k-1个结点。(k>=1).
- 对任何一棵二叉树T,如果其叶子数为n0,度为2的结点数为n2,则n0 = n2 +1;
完全二叉树和满二叉树的性质
-
满二叉树:一棵深度为k且有2^k-1个结点的二叉树称为满二叉树。
- 特点:1.每一层上的结点数都是最大结点数(即每层都满)
- 叶子节点全部在最底层
-
对满二叉树节点位置进行编号
- 编号规则:从根结点开始,自上而下,自左而右
- 每一个结点位置都有元素
-
满二叉树在同样深度的二叉树中结点个数最多
-
满二叉树在同样深度的二叉树中叶子结点个数最多
-
完全二叉树
- 深度为k的具有n个结点的二叉树,当且仅当其每一个结点都与深度为k的满二叉树中编号为1~n的结点一一对应时,称之为完全二叉树。
- 在满二叉树中从最后一个结点开始连续去掉任意个结点,即是一棵完全二叉树
-
完全二叉树的叶子只可能分布在层次最大的两层上。
-
对于完全二叉树:对任一结点,如果其右子树的最大层次为i,则其左子树的最大层次必为i或i+1。
-
具有n个结点的完全二叉树的深度为**[log2n]+1**.表明了完全二叉树的结点数n与完全二叉树深度k之间的关系。
- 如果有一棵n个结点的完全二叉树(双亲结点编号和孩子结点编号之间的关系)
- 如果i = 1,则结点i是二叉树的根,无双亲;如果i>1,则其双亲是结点[i/2].
- 如果2i>n,则结点i为叶子结点,无左孩子;否则,其左孩子是结点2i。
- 如果2i + 1 > n,则结点i无右孩子;否则,其右孩子是结点 2i+1。
二叉树的存储结构
-
顺序存储结构
按照满二叉树的结点层次编号,依次存放二叉树中的数据元素(从上到下一层一层的记录存储)
#define MAXTSIZE 100 Typedef TElemType SqBiTree[MAXTSIZE] SqBiTree bt;
- 缺点:数据变动以后不好处理
- 空间浪费严重。
-
链式存储结构(二叉链表)
typedef struct BiNode
{
TElemType data;
struct BiNode *lchild,*rchild;//左右孩子指针
}BiNode,*BiTree;
- 有一个头指针指向根结点。
-
链式存储结构(三叉链表)
typedef struct TriTNode { TelemType data; struct TriTNode *lchild,*parent,*rchild; }TriTNode,*TriTree;
- 包含双亲结点的指针。
遍历二叉树
- 遍历:顺着某一条路径寻访二叉树中的结点,是的每个结点均被访问依次,而且仅被访问依次(又称为周游)。
- “访问”的含义很广,可以是对结点作各种处理,如:输出结点的信息、修改结点的数据值等,单要求这种访问不破坏原来的数据结构。
- 遍历目的:得到树中所有结点的一个线性排列。
- 用途:是各类二叉树算法的前提
- 遍历方法:
- 先序:先根结点,后左节点,再右节点
- 中序:先左结点,后根结点,再右结点
- 后序:先左结点,后右节点,再根结点
- 由二叉树的递归定义可得,遍历左子树和遍历右子树可如同遍历二叉树一样“递归”进行。
- 遍历的时候递归进行遍历。将大树分为:左,根,右;