《大话数据结构》第六章 树


第六章 树

树的定义

树的定义:树(Tree)是n个结点的有限集,n=0时称为空树。在任意一棵非空树中:

  1. 有且仅有一个特定的根(Root)的结点
  2. 当n>1时,其余的结点可分为m个互不相交的有限集 T 1 , T 2 , . . . T m T_1,T_2,...T_m T1,T2,...Tm,其中每一个集合本身优势一棵树,并且称为根的子树(SubTree)


错误的树
子树相交了


结点的分类

结点的度:结点拥有的子树树被称为结点的度(Degree)。

叶结点/终端结点:度为0的结点称为叶结点(Leaf)或终端结点。

非终端结点/分支结点:度不0的结点。

内部结点:除了根结点之外,分支结点也称为内部结点。

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

结点间关系

  1. 结点的子树的根称为该结点的孩子(Child),相应地,该节点称为孩子的双亲(Parent)。
  2. 同一个双亲的孩子之间互称兄弟(Sibling)。
  3. 结点的祖先是从根到该结点所经分支上的所有结点。
  4. 以某结点为根的子树中的任意结点都称为该节点的子孙。

树的其他相关概念

  • 结点的层次(Level)从根开始定义起,根为第一层,根的孩子为第二层。
  • 双亲在同一层的结点互为堂兄弟
  • 树中结点的最大层次称为树的深度(Depth)或高度。深度是绝对的,从根结点开始算,高度是相对的,看自己的子树有多高。
  • 如果将树中结点的各子树看成从左至右是有次序的,不能互换的,则称该树为有序树,否则为无序树
  • 森林(Forest)是m棵互不相交的树的集合,对树中的每个结点而言,其子树的集合即为森林



树的抽象数据类型


树的存储结构

利用顺序存储和链式存储结构的特点,完全可以实现对树的存储结构的表示

双亲表示法

思路:在每个结点中,附设一个指示器指示其双亲结点到链表中的位置,知道爹妈是谁,不知道孩子是谁(段正淳?)。

结点结构定义

#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;    // 根的位置和结点数
} PTreee;

根节点没有双亲,所以约定根结点的位置域设置为-1。

可以根据需求,再增加一个结点最左边孩子的域,叫长子域,如果没有孩子结点,长子域设置为-1:

还可以增加右兄弟域,如果存在右兄弟,就记录下右兄弟的小标,不存在就设置为-1:

那么左右是怎么确定的呢?


孩子表示法

多重链表表示法:由于树中每个结点可能有多棵子树,可以考虑用多重链表,即每个结点有多个指针域,其中每个指针指向一棵子树的根结点,我们把这张方法叫做多重链表表示法。

然而树的每个结点的度是不同的,有两种解决方案。

方案一:
让指针域的个数就等于树的度:

其中data是数据域,child1到childd是指针域,用来指向该结点的孩子结点。

缺点:但是这样很浪费空间,除非说各个树的结点度相差很小的时候,才有优势。

方案二
每个指针结点域的个数等于该结点的度,专门取一个位置来存储结点指针域的个数:

其中data为数据域,degree为度域,也就是存储该结点的孩子结点的个数,child1到childd为指针域,指向该结点的各个孩子的结点。

缺点:虽然对空间的利用率提高了,但是由于各个结点的链表是不相同的结构,加上要维护结点的度的数值,在运算上就会带来时间上的损耗。

孩子表示法:把每个结点的孩子结点排列起来,以单链表作存储结构,则n个结点有n个孩子链表,如果是叶子结点则此单链表为空,然后n个头指针又组成一个线性表,采用顺序存储结构,存放进一个一维数组中

分而治之?

孩子链表的孩子结点

其中child是数据域,用来存放结点在表头数组中的下标;next是指针域,存放指向某结点的下一个孩子结点的指针。

表头数组的表头结点


其中data是数据域,存储某结点的数据信息;firstchild是头指针域,存储该结点的孩子链表的头指针

孩子表示法结构定义

#define MAX_TREE_SIZE 100
typedef struct CTNode
{
   
    int child;    // 表头下标
    struct CTNode *next;
} *ChildPtr;
typedef struct CTBox   // 表头结构
{
   
    TElemType data;
    ChildPtr firstchild;
};
typedef struct CTree   // 树结构
{
   
    CTBox nodes[MAX_TREE_SIZE];    // 表头结构数组,就是结点数组
    int r, n;    // 根的位置和结点数
};

优点:对于查找某个结点的某个孩子,或者找某个结点的兄弟,遍历等比较方便

感觉兄弟不好找呀,比如找A的兄弟,要遍历找到包含A的子链表,才能找到A的兄弟

缺点:无法找到双亲,找双亲需要遍历。

所以可以把双亲表示法和孩子表示法综合一下,得到双亲孩子表示法


孩子兄弟表示法

理论基础:任意一棵树,它的结点的第一个孩子如果存在就是唯一的,它的右兄弟如果存在也是唯一的,因此,设置两个指针,分别指向该结点的第一个孩子和此结点的右兄弟。

其中data是数据域;firstchild为指针域,存第一个孩子的地址;righsib也是指针域,存储右兄弟结点的地址。

结构定义代码

typedef struct CSNode
{
   
    TElemType data;
    struct CSNode *firstchild, *rightsib;
} CSNode, *CSTree;

如果有需要可以加parent指针域。

好处:给查找某个结点的孩子带来了方便,同时把一棵复杂的树变成了一棵二叉树


二叉树

定义:二叉树是n个结点的有限集合,该集合或者为空集(称为空二叉树),或者由一个根结点和两棵互不相交的、分别称为根结点的左子树和右子树的二叉树组成

二叉树特点

  • 每个结点最多两棵子树,所以二叉树中不存在度大于2的结点。
  • 左子树和右子树是有顺序的,次序不能任意颠倒。
  • 即使某结点只有一棵子树,也要区分他是左子树还是右子树

二叉树的五种基本形态

  1. 空二叉树
  2. 只有一个根结点
  3. 根结点只有左子树
  4. 根结点只有右子树
  5. 根结点既有左子树又有右子树

三个结点的二叉树有五种形态


特殊二叉树

斜树

定义:所有的结点都只有左子树的二叉树叫左斜树,只有右子树叫右斜树,这两者统称为斜树。

左斜树

右斜树

特点:每一层都只有一个结点,结点的个数与二叉树的深度相同。


满二叉树

定义:在一棵二叉树中,如果所有的分支结点都存在左子树和右子树,并且所有叶子都在同一层上,这样的二叉树被称为满二叉树

特点

  1. 叶子只能出现在最下层
  2. 非叶子结点的度一定是2
  3. 在同深度的二叉树中,满二叉树的节点个数最多,叶子数最多

完全二叉树

定义:对一棵具有n个结点的二叉树按层序编号,如果编号为i(1<=i<=n)的结点的位置,和同深度的满二叉树中 i 结点的位置相同,那这颗树就叫完全二叉树。

没满,但是存在的结点结构和满二叉树相同

按层序编号后的结点应连贯,满二叉树一定是完全二叉树,完全二叉树不一定是满二叉树,下面是非完全二叉树

特点

  1. 叶子结点只能出现在最下两层
  2. 最下层的叶子一定集中在左部连续位置
  3. 倒数二层,若有叶子结点,一定都在右部连续位置
  4. 如果结点度为1,则该结点只有左孩子,不存在只有右孩子的情况
  5. 同样结点树的二叉树,完全二叉树的深度最小

子树的生成以左边为主,但是不可只有左边,倒数第二层必须有右边,倒数第二层开始网上必须都完全填满,所以深度会最小


二叉树的性质

性质1:在二叉树的第 i 层上至多有 2 i − 1 2^{i-1} 2

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
大话处理器:处理器基础知识读本的真正完整本,全部八章,手动呕血扫描加书签,非网上那种6.33MB的太监版~!以全家人性命为誓~! 作者简介   万木杨,网名木兮清扬,华为公司服务近6年,曾任软件工程师、算法工程师、系统工程师,擅长多媒体算法设计和编写高效代码。 作者自2004年起开始研究多媒体算法,从语音识别,到人脸动画,再到视频编解码,足迹遍布语音、图像、视频、3D。自2006年在DSP上编写程序,从此开始深入研究处理器内部结构,后来接触过大量的半导体公司和处理器芯片,对处理器技术和产品有着深刻的理解。 闲暇之余,作者喜爱读书,多年来保持平均两周一本的速度。 ·查看全部>>目录 第1章 漫游计算机世界 1.1 计算机的前世、今生、来世 1.2 计算机分门别类 1.3 PC机结构探秘 第2章 初识处理器——掀起你的盖头来 2.1 处理器是怎样工作的——处理器的硬件模型 2.2 怎样来使用处理器——处理器的编程模型 2.3 处理器的分层模型 2.4 选什么样的处理器——适合的才是最好的 第3章 指令集体系结构——处理器的外表 3.1 指令集是什么 3.2 指令集发展的来龙去脉 3.3 指令集的五朵金花 3.4 地盘之争 3.5 汇编语言格式——没有规矩不成方圆 第4章 微架构——处理器的内心世界 4.1 跟着顺溜学流水线 4.2 从子弹射击到指令执行 4.3 从顺序执行到乱序执行——因时制宜 4.4 处理器并行设计——并行,提高性能的不二法门 4.5 指令并行(Instruction Level Parallelism) 4.6 数据并行(Data Level Parallelism) 4.7 线程并行(Thread Level Parallelism) 4.8 并行总结 4.9 微架构总结 第5章 Cache——处理器的“肚量” 5.1 什么是Cache——探索既熟悉又陌生的领域 5.2 处理器的Cache结构——探索那些鲜为人知的秘密 5.3 Cache一致性 5.4 片内可寻址存储器——软件管理的Cache 第6章 编写高效代码——时间就是生命 6.1 软件效率——21世纪什么最重要?效率! 6.2 减少指令数——勤俭持家 6.3 减少处理器不擅长的操作——不要逼我做我不喜欢的事情 6.4 优化内存访问——别让包袱拖垮了你 6.5 充分利用编译器进行优化——编译器:我才是优化第一高手 6.6 利用多核来加速程序——人多力量大 第7章 SOC——吸星大法 7.1 SOC大一统时代 7.2 IP核 第8章 “芯”路历程——明明白白我的“芯” 8.1 逻辑电路基础——计算机的基本构成 8.2 芯片设计——芯者,国之大事,不可不察也 8.3 芯片制造——点沙成金

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值