树与二叉树的奥秘全解析

目录

定义

基本性质

性质一

性质二

性质三

二叉树

定义

基本形态

性质

性质一

性质二

性质三

真题

存储结构

顺序结构

链式结构

遍历

前序遍历

中序遍历

后序遍历

性质

真题

特殊二叉树

满二叉树

完全二叉树

性质

线索二叉树

存储结构

中序遍历线索化

真题

哈夫曼树

计算WPL

定义

构造哈夫曼树

哈夫曼编码

真题

树与二叉树的转换

树转换成二叉树

二叉树转换成树

森林与二叉树的转换

二叉树转换为森林

森林转换为二叉树

遍历

树的遍历

森林的遍历

树、森林与二叉树的遍历关系


定义

树是一个或多个结点的有限集合

存在一个称为根的特定结点其余的结点被分为 n 个互不相交的集合 T1, T2, ..., Tn, 其中的每个集合都是一棵树。T1, T2, ..., Tn称为根节点的子树。

结点:树中的一个独立单元。

结点的度:结点拥有的子树数称为结点的度。

树的度:树内各结点度的最大值。

叶子:度为 0 的结点或终端结点。

非终端结点:度不为 0 的结点。

双亲和孩子:结点的子树的根称为该结点的孩子,相应地,该结点称为孩子的双亲。

层次:结点的层次从根开始定义,根为第一层,根的孩子为第二层,以此类推。

基本性质

性质一

树中所有结点数等于所有结点的度数之和加 1。


例题:

解析:

答案:

B


性质二

对于度为 m 的树,第 i 层上最多有 m^ i-1个结点。

性质三

对于高度为 h,度为 m 的树,最多有 (m ^ h - 1) / (m - 1) 个结点。

二叉树

定义

二叉树(Binary Tree)是 n(n ⩾ 0 )个结点所构成的集合,它或为空树(n=0),或为非空树。

对于非空树T:

(1)有且仅有一个 称为根的结点

(2)除根结点以外的其余结点分为两个互不相交的子集 T1 和 T2 ,分别称为 T 的左子树和右子树,且 T1 和 T2 本身又都是二叉树。

(3)二叉树每个结点至多只有两棵子树。

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


基本形态


性质

性质一

二叉树的第 i 层最多有2 ^ (i-1)(i ⩾ 1 )个结点。

性质二

深度为 k 的二叉树最多有2 ^ k - 1(k ⩾ 1)个结点。

性质三

对于任何非空的二叉树T,如果叶子结点的个数为n0,而度为2的结点数为n2,则 n0=n2+1

真题

题1

解析

答案

C


题2

解析:

答案:

C


题3

解析:

答案:

A

存储结构

顺序结构

链式结构

复制代码
typedef char ElemType; 

typedef struct TreeNode 
{ 
    ElemType data; 
    TreeNode *lchild; 
    TreeNode *rchild; 
}TreeNode; 

typedef TreeNode* BiTree;

遍历

前序遍历

先访问根结点,然后访问左分支上遇到的每一个结点,持续这一过程,直到遇到空结点为止。这时,返回到最近的有右孩子的祖先结点,并从该结点的右孩子开始继续遍历。

复制代码
void preOrder(BiTree T) 
{ 
    if (T == NULL) 
    { 
        return; 
    } 
    printf("%c ", T->data); 
    preOrder(T->lchild); 
    preOrder(T->rchild); 
}

输出内容: A B D H K E C F I G J

中序遍历

先访问根结点,向树的左下方移动,直到遇到空结点为止,然后访问空结点的父结点。接着继续遍历该结点的右子树,如果右子树没的子树可以遍历,那么继续遍历上一层最后一个未被访问的结点

复制代码
void inOrder(BiTree T) 
{ 
    if (T == NULL) 
    { 
        return; 
    } 
    inOrder(T->lchild); 
    printf("%c ", T->data); 
    inOrder(T->rchild); 
}

输出内容: H K D B E A I F C G J

后序遍历

从根结点开始先访问结点的左右儿子,再对该结点进行访问。这就意味着结点的儿子将在该结点之前输出。

复制代码
void postOrder(BiTree T) 
{ 
    if (T == NULL) 
    { 
        return; 
    } 
    postOrder(T->lchild); 
    postOrder(T->rchild); 
    printf("%c ", T->data); 
}

输出内容:K H D E B I F J G C A


下列树的遍历结果是?

前序:A B D H E I C F G J K

中序:H D B E I A F C J G K

后序:H D I E B F J K G C A


下列树的遍历结果是?

前序:A B D E G H C F I

中序:D B G E H A F I C

后序:D G H E B I F C A


性质

已知前序遍历和中序遍历,可以唯一确定一棵二叉树。

已知中序遍历和后序遍历,可以唯一确定一棵二叉树。

已知前序遍历和后序遍历,是不能确定一棵二叉树的。

例:前序序列是ABC,后序序列是CBA

真题

题1

解析:

D. A. B.

答案:

C


题2

答案:

B


题3

解析:


答案:
B

特殊二叉树

满二叉树

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

所有的叶子结点只能出现在最后一层

对于同样深度的二叉树,满二叉树的结点个数最多,叶子结点的数量也是最多的。

如果对满二叉树进行编号,根结点从1开始,从上到,下从左到右,对于编号为i的结点,若存在左孩子, 则左孩子的编号为2i,右孩子为2i+1。

完全二叉树

深度为 k 的、有 n 个结点的二叉树,当且仅当其每一个结点都与深度为 k 的满二叉树中编号从 1 至 n 的结点一一对应时,称之为完全二叉树。

完全二叉树的特点是:

(1)叶子结点只可能在层次最大的两层上出现;

(2)对任一结点,若其右分支下的子孙的最大层次为 l,则其左分支下的子孙的最大层次必为 l 或 l+1。

没有左子树,不能有右子树,上一层没有铺满,不能有下一层

性质

性质四:具有 n 个结点的完全二叉树的深度为 log2n+1(向下取整)

性质五:如果对一棵有 n 个结点的完全二叉树(其深度为 log2n+1(向下取整))的结点按层序编号(从第 1 层到第 log2n+1 (向下取整层),每层左到右),则对任一结点 i (1 ⩽ in),以下结论成立。

(1)如果 i=1,则结点 i 是二叉树的根,无双亲;如果结点 i>1则其双亲是结点 i/2(向下取整)

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

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

线索二叉树

通过遍历可以得到二叉树的线性排列 ,但这样线性序列只有在遍历时才能得到

将二叉树线索化得到线索二叉树可以解决这个问题

线索化:利用叶节点的空余空间记录前驱、后继

存储结构

复制代码
typedef char ElemType; 

typedef struct ThreadNode 
{ 
    ElemType data; 
    struct ThreadNode *lchild; 
    struct ThreadNode *rchild; 
    int ltag; 
    int rtag; 
}ThreadNode; 

typedef ThreadNode* ThreadTree;

ltag为 0 时,指向该结点左孩子,为 1 时,指向该结点的前驱

rtag为 0 时,指向该结点右孩子,为 1 时,指向该结点的后继

中序遍历线索化


中序遍历: H D I B J E A F C G

  1. 头结点的 lchild 指向二叉树的根
  1. 头结点的 rchild 指向遍历的最后一个结点(指中序遍历的最后一个节点)


3. 第一个结点的 lchild 指向头结点

4. 最后一个结点的 rchild 指向头结点

真题

题1

解析:

d b c a

答案:

D


题2

解析:
d e b x a c

答案:

D

哈夫曼树

路径:从树中一个结点到另一个结点之间的分支构成这两个结点之间的路径

路径长度:路径上的分支数目

树的路径长度:从树根到每一个结点的路径长度之和

结点的权:在实际应用中,给树中的结点赋予代表某种含义的数值

结点的带权路径长度:从该节点到树根之间的路径长度与该结点权的乘积

树的带权路径长度(WPL):树中所有叶节点的带权路径长度之和

计算WPL

WPL = 5 * 1 + 15 * 2 + 40 * 3 + 30 * 4 + 10 * 4 = 315

定义

带权路径长度 WPL 最小的二叉树称为哈夫曼树。

构造哈夫曼树

  1. 先把有权值的叶子结点按照从小到大的顺序排列成一个有序序列
  2. 取两个最小权值的结点作为一个新结点 N 1 的子结点
  3. 将结点 N 1 替换刚刚取出的两个结点,并加入到有序序列中
  4. 重复步骤 2 ,将 N 1 和 "及格"作为一个新结点 N 2 的子结点
  5. 将结点 N 2 替换刚刚取出的两个结点,并加入到有序序列中
  6. 重复步骤 2 ,将 N 2 和 "良好"作为一个新结点 N 3 的子结点
  7. 将结点 N 3 替换刚刚取出的两个结点,并加入到有序序列中
  8. 重复步骤 2 ,将 N 3 和 "中等"作为一个新结点 N 4 的子结点


WPL = 40 * 1 + 30 * 2 + 15 * 3 + 5 * 4 + 10 * 4 = 205

哈夫曼编码

对 BADCADFEED 进行编码

哈夫曼编码进制串:1001 01 00 101 01 00 1000 11 11 00

原编码二进制串:001 000 011 010 000 011 101 100 100 011

真题

题1


题2


题3


题4

解析:

答案:

A

树与二叉树的转换

树转换成二叉树


  1. 加线,在所有兄弟结点之间加一条线

  1. 去线,对树中的每一个结点,只保留它与第一个孩子结点的连线,删除它与其它孩子结点之间的连线。

  1. 层次调整,以树的根结点为轴心,将整棵数顺时针旋转一定角度,使之层次分明。注意第一个

孩子是二叉树结点的左孩子。兄弟转过来的孩子是结点的右孩子。

二叉树转换成树


  1. 加线,若某个结点的左孩子存在,则将这个左孩子的所有右孩子结点都作为此结点的孩子,将该结点与这些右孩子结点用线连起来。

  1. 去线,删除二叉树中所有结点与其右孩子结点的连线

  1. 调整,转一下

森林与二叉树的转换

二叉树转换为森林

第一棵二叉树不动,从第二棵树开始,依次把后一棵二叉树的根结点作为前一棵二 叉树的根结点的右孩子,然后用线连起来

森林转换为二叉树

从根结点开始,若右孩子存在,则把右孩子结点的连线删除。

二叉树转换成树

遍历

树的遍历

森林的遍历

树、森林与二叉树的遍历关系

相关推荐
风筝在晴天搁浅3 小时前
代码随想录 617.合并二叉树
数据结构·算法
微露清风3 小时前
系统性学习C++-第七讲-string类
java·c++·学习
艾莉丝努力练剑3 小时前
【C++:继承】C++面向对象继承全面解析:派生类构造、多继承、菱形虚拟继承与设计模式实践
linux·开发语言·c++·人工智能·stl·1024程序员节
ezreal_pan3 小时前
架构权衡与实践:基于“约束大于规范”的缓存组件封装
redis·cache·1024程序员节
少年码客3 小时前
英文 PDF 文档翻译成中文的优质应用
人工智能·1024程序员节
hweiyu004 小时前
Gradle 增量构建与构建缓存:自定义 Task 如何实现 “只构建变化内容”?
gradle·1024程序员节
报错小能手4 小时前
项目——基于C/S架构的预约系统平台(3)
linux·开发语言·笔记·学习·架构·1024程序员节
cxr8284 小时前
涌现的架构:集体智能框架构建解析
人工智能·语言模型·架构·1024程序员节·ai智能体·ai赋能
AICodeThunder4 小时前
【S组篇】C++知识点总结(1):并查集基础
c语言·数据结构·c++·算法·图论