提示:积善之家,必有余庆;积不善之家,必有余殃
一、树的基本概念
1.1树的定义
- 树是n个结点的有限集,当n=0 时称为空树
- 在任意一个非空树中因该满足 有且仅有一个特定的称为根的结点
- 树是一种逻辑结构 也是一种分层结构 树的根节点没有前驱 除根结点之外的所有结点有且只有一个前驱,树的所有结点可以有零个或者多个后继
1.2基本术语
- 示意图
- K的祖先:从根A到结点K的唯一路径上的任意结点
- 子孙:结点B是结点K的祖先,而结点K是结点B的子树
- 双亲与孩子:路径上最接近结点K的结点E称为K的双亲 而K为结点e的孩子
- 兄弟:拥有相同双亲的结点称为兄弟 如结点K与结点L
- 结点的度:结点中每一个结点孩个数,树中结点最大的度数称为树的度
- 分支节点:度大于0 的结点
- 叶子结点:度为0 的结点
- 结点的层次:从树根开始定义 根结点为第一层 他的子结点为第二层
- 树的深度:从根节点开始自顶向下逐层累加
- 结点的高度:从结点子底向上逐层累加的
- 树的高度(深度):树中结点的最大层数
- 有序和无序树:树中结点的各个子树从左往右是有次序的 不能互换 称为树的有序树 否则称为无序树
- 路径与路径长度:树中结点的之间的路径是由这两个结点之间的结点序列构成 而路径长度是路径上所经过的边的个数
- 森林:森林是有n课互不相交的树的集合 只要把树的结点删除就成了森林 把n 棵树加上一个结点 并把m棵树作为该结点的子树 则森林变成了树
1.3、树的性质(重点概念)
- 树中的结点数等于所有结点的度数加一(一度就相当于树中的一个线 加一是因为多了一个根节点)
- 度为m的树中第i层最多为m的(i-1)次方个结点
- 高度为h的二叉树最少有h 个结点(相当于一个线性表一样的二叉树)
- 高度为h度为m的树至少有h+m-1个结点(至少有一个结点的度为m若此时其他结点都是1,则此时结点数为h+m-1)
- 高度为h,度为m的树,最多(m的h次方减一)/(m-1)个结点( 第一层m的0 次方 第二层 m的一次方 第三层m的二次方 是一个等比数列 可以用等比数列公式)
. 具有n个结点的m 叉树的最小高度为logm(n(m-1)+1)(每一个结点尽可能大的时候高度才会底这个可以通过上一个来反解出k=***) - m叉树 每一个结点最多为m个子树 所以可以是空树
- 度为m的树 一定不为空树 至少有m+1个结点
二、二叉树概念
2.1、二叉树的主要性质
2.1.1、二叉树的定义
- 二叉树是另外一种树型结构 其特点是每一个结点最多有两个子树 并且二叉树的子树还有左右之分 其次序不能颠倒
2.1.2、二叉树与度为2的有序树的区别
- 度为2的树最少要有三个结点 而二叉树可能为空
- 孩子结点 :度为二的有序树的孩子的左右次序是相对于另外一个孩子而言的,若是某个结点只有一个孩子,则这个孩子就无法分其左右次序 二叉树无论其孩子是否为2 均需确定其左右次序
2.1.3、特殊的二叉树
2.1.3.1、满二叉树
一颗高度为h 且含有2的h次方-1个结点的二叉树称为满二叉树
对于编号为i的结点 双亲为i/2(往下取整) 若是有左孩子,则左孩子为2i 若是有右孩子则为2i+1
2.1.3.2、完全二叉树
1,高度为h有n个结点的二叉树 当且仅当其中每一个结点都与高度为h 的满二叉树中编号为1-n 个结点一一对应称为完全二叉树
2.特点:i<n/2(向下取整) 则结点为分支结点 否则为叶子结点
3.叶子结点只可能在其层次最大的两层上出现,对于最大层次中的叶子结点,都依次排列在该层最左边位置
4.若是有度为一的结点 则只可能有一个这样的结点 并且此结点只有左孩子而无右孩
5.按层编号之后 一旦出现某个结点(编号为i)为叶子结点或只有左孩子 则大于编号i的都是叶子结点
6 若n为奇数 则每一个分支结点都有左右孩子(因为有一个根结点)
7 若是n为偶数 则编号最大的分支结点(n/2)只有左孩子 没有右孩子 其余分支节点左右孩子都有
2.1.3.4、二叉排序树
左子树上的所有结点的关键字均小于根结点的关键字 :右子树上的所有结点的关键字均大于根结点的关键字,左子树和右子树各是一个二叉排序树
2.1.3.5、 平衡二叉树
树上任意一个结点的左子树和右子树的深度之差不超过1
2.1.4、二叉树的性质
1 非空二叉树上叶子结点数等于度为2的结点数加1
我们来设二叉树其中的线一共有b条 叶子结点为n0 ,度为一的为n1,度为二的为n2
总的结点数为n
则可得到下面几个关系
n=n0+n1+n2
b=n0+n1+n2-1(因为根结点是没有线的,所以需要减一)
b=n2*2+n1;(这个关系最容易忽略 )
直接让2与3相等就可以解出 n0-1=n2
2.非空二叉树上第K层上最多为2的k-1次方个结点 原因根是从2的0 次方开始的
3 高度为h的二叉树最多有2的h次方-1个结点 使用等比数列求解公式即可求出
4 对完全二叉树从上到下 从左到右依次进行编号1.2…n 则有下列关系
(1)当i>1时,即当i为偶数时,其双亲结点的编号为i/2,它是双亲结点的左孩子;当i为奇数时,其双亲结点的编号为(i-1)/2,它是双亲结点的右孩子。
(2)当2i≤n时,结点i的左孩子编号为2i,否则无左孩子
(3)当2i+1≤n时,结点i的右孩子编号为2i+1,否则无右孩子
(4)结点i所在层次(深度)为log2(i)向下取整+1(深度是从1开始的,根结点的深度为1)
(5)具有n个(n>0) 结点的完全二叉树的高度为log2(n+1)向上取整或log2(n)向下取整+1 (高度同样是从1 开始的)
2.2、 二叉树的存储结构
2.2.1、顺序存储结构:
(1)用一组相同的地址连续的存储单元依次自上而下 自左而右存储完全二叉树的结点元素 即将完全二叉树上编号为i的结点元素存储在一维数组下标为i-1的分量中
(2)完全二叉树和满二叉树采用顺序存储是比较合适的
(3)一般二叉树为了能反映二叉树中结点之间的逻辑关系 只能添加并不存在的空结点,然而若是使用数组来存储完全二叉树,完全二叉树上的结点可以与数组上的元素一一对应,再存储到一维数组的相应分量中。
2.2.2、链式存储
由于顺序存储空间利用率底 所以二叉树一般使用链式存储结构 用链式结点来存储二叉树中的每一个结点
含有n个结点的二叉链表中 含有n+1 个空链域(n个结点也就意味着有n*2个指针域 根结点不需要指针来指向 也就意味着只有n-1 个结点需要指针来指向 两者相减就是n+1)
三、 二叉树的遍历和线索二叉树
3.1、二叉树的遍历(代码以更新)
代码传从带
先序遍历 :访问根结点 先序遍历左子树 先序遍历右子树
中序遍历:中序遍历左子树 访问根结点 中序遍历右子树
后序遍历:后序遍历左子树 后续遍历右子树 访问根结点
使用栈可以实现非递归方式
3.2、层序遍历(代码已更新)
代码传从带
利用队列实现
实现:先将二叉树根结点入队 然后出队 访问出队结点 若是他有左子树则将左子树根结点入队,若是他又右结点则将右子树根节点入队,然后出队 如此反复直到队列为空
3.3、构建二叉树(代码以更新)
代码传送带
二叉树的先序(后续)序列和中序序列都可以唯一确定一个二叉树、层序遍历和中序(后续)遍历可以确定一个二叉树
3.4、线索二叉树(代码以更新)
3.4.1、基本概念
3.4.1.1、线索二叉树的基本概念:
遍历二叉树就是以一定的规则将二叉树中的结点排列成一个线性序列,从而得到几种遍历序列,使用该序列中的每一个结点(第一个与最后一个除外)都有一个直接前驱和直接后继
3.4.1.2、规定
若是无左孩子 则Lchild指向其前驱结点
若是无右孩子 则rchild指向其后继节点
3.4.1.3、结点结构
3.4.1.4、标识域的含义
ltag 0 lchild指示结点左孩子
ltag 1 lchild指示结点前驱
rtag 0 rchild指示结点右孩子
rtag 1 lchild指示结点后继
3.4.2、中序线索二叉树
3.4.2.1、概念:
(1)二叉树的线索化是将二叉链表中的空指针改为指向其前驱或后继的线索
(2)而前去或者后继的信息只有遍历时才能得到 因此线索化的实质是遍历一次二叉树
3.4.2.2、中序线索二叉树的建立
(1)加设一个指针pre指向刚刚访问过的结点 指针p指向此时正在访问的结点,即pre指向p的前驱
(2)在中序遍历的过程中检查p的左指针是否为空 若是为空 则让他指向它的pre 检查pre的右孩子 若是为空则将它指向p
3.4.2.3、中序线索二叉树的遍历
若是其右标记为’1‘ 则右链为线索 指向其后继 否则遍历右子树的第一个访问的结点(右子树的最左下)为其后继
3.4.3、先序线索二叉树
3.4.4、后序线索二叉树
四、树、森林
4.1树的存储结构
4.1.1、双亲表示法
(1)采用一组连续空间来存储每一个结点 同时在每一个节点中增加一个伪指针 指示其双亲在数组中的位置 根结点下标为0 其伪指针域为-1
(2)优点 利用了每一个结点(根节点除外) 只有唯一双亲的性质,可以同时很快得到每一个结点的双亲结点
(3)缺点:但是求结点的孩子时需要遍历整个结构
4.1.2、孩子表示法
(1)将每一个结点的孩子结点都用简单链表链接起来形成一个线性结构,此时n个结点就有n个孩子链表 叶子节点的孩子链表为空
(2) 优点:寻找子女非常直接
(3) 缺点:寻找双亲需要遍历n个结点中孩子链表指针域所指向的孩子链表
4.1.3、孩子兄弟表示法
(1)以二叉链表为树的存储结构,孩子兄弟表示法使得每一个结点包含三个内容,节点值,指向结点第一个孩子结点的指针,以及指向结点下一个兄弟结点的指针
(2) 优点:这种存储结构比较灵活 最大优点就是可以方便实现树转化为二叉树的操作,易于查找孩子等
(3)缺点:从当前结点找双亲比较麻烦
4.2、树、森林与二叉树的转化
4.2.1、树转化为二叉树的规则
每一个结点左指针指向它的第一个孩子,右指针指向它在树中的相邻右兄弟
4.2.2、树转化为二叉树的画法
在兄弟结点之间加一条线,对每一个结点只保留它与第一个孩子的连线 而将其他的孩子的连线全部抹掉,以树根为轴心 顺序针旋转45*
4.2.3、森林转化为二叉树的画法
将森林中的每棵树转化换成相应的二叉树
每一棵树的根也可视为兄弟关系,在每一颗树根之间加一根连线
以第一棵树的根为轴心顺时针旋转45*
4.2.4、二叉树转化为树林的规则
若是二叉树非空,则将二叉树根及其左子树为第一颗二叉树形式,故将根的右链断开
4.3 树和森林的遍历
4.3.1、先根遍历
若是非空,先访问根结点 再依次遍历根结点的每一个子树 遍历子树时仍然是先根后子树的规则 其遍历序列与这棵树相应的二叉树的先序序列相同
4.3.2、后根遍历
若树非空,先依次遍历根结点的每一个子树,再访问根结点,遍历子树时仍然遵循先子树后根的规则 其遍历序列与这棵树相应的二叉树中序遍历相同
4.3.3、先序遍历森林
访问森林中第一课树的根结点
先序遍历第一个树中的根节点的子树森林
先序遍历除去第一颗树之后剩余树构成的森林
4.3.4、中序遍历森林
中序遍历森林中第一个棵树的根结点的子树森林 访问第一课树 的根结点 中序遍历除去第一颗树之后剩余的树构成的森林
4.4、 树的应用——并查集
(1)Initial(S):
将集合S中的每个元素都初始化为只有一个单元素的子集合。
(2)Union(S, Root1, Root2):
把集合S中的子集合(互不相交)Root2并入子集合Root1。
这就是所谓的“并”的操作。
这里要注意的是两个集合是不相交的,如果它们有相交的部分,就不予执行。
(3)Find(S, x):
查找集合S中单元素x所在的子集合,并返回该子集合的名字。
这就是所谓的“查”的操作。
返回的就是根结点的标号。
五、树与二叉树的应用
5.1、二叉排序树的定义
(1)若左子树非空,则左子树上所有结点的值均小于根结点的值
(2)若是右子树非空 则右子树上所有结点的值都大于根结点的值
(3)左右子树也分别是一个二叉排序树
左子树的结点值<根结点的值<右子树结点值
5.2、二叉排序树的查找
二叉排序树的查找时从根结点开始,沿某个分支逐层往下比较的过程,若是二叉排序树非空,先将给定值与根结点的关键字进行比较
(1)若是相等则查找成功 若是不等
(2)若是小于根结点的关键字,则在根节点的左子树上查
(3)否则则在右子树上查找
5.3、 二叉排序树的插入
(1)插入结点的过程,若原二叉排序树为空,则直接插入结点
(2)若是关键字k小于根结点值 则插入到左子树,
(3)若是关键字大于根结点 则插入到右子树中 ,插入的结点一定是一个新添加的叶子结点 且查找失败时查找路径上访问的最后一个结点的左孩子或者右孩子
5.4、二叉排序树的构造
第一个结点作为根 新加入的值若是大于则右子树 若是小于则是左子树 记住不一样的添加顺序导致的 构造的树是 不一样的
5.5、二叉排序树的删除
(1)若是删除的结点是叶子结点 直接删除即可 ,不会破坏二叉排序树的性质
(2) 若是结点z 只有一颗左子树 或者右子树 则让z的子树成为z父节点 的子树 代替z的位置
(3) 若是z既有左子树 又有右子树,则令z的中序遍历过程中的直接后继(或者直接前驱)代替z 然后从二叉排序树中删除这个直接后继(或者直接前驱) 这样就转化为第一种或者第二种情况
哈夫曼树(难点)
关于哈夫曼树树的创建,编码 解码,有兴趣的可以看看这个数据结构专栏中的哈夫曼树
感觉不错的小伙伴点个赞呗 你的鼓励是作者的动力