数据结构与算法:二叉树和树

* 树形结构:也是由结点(结点中的逻辑单元,可用于保存数据)和结点之间的连接关系(一种后继关系)构成,但其结构和线性结构不同,最重要的特征包括:结构不为空时,都有且仅有一个起始结点,叫做树根;按结点间的连接关系,树根外的结点都有且仅有一个前驱,但可以有0个或者多个后继(不同点)。
* 树根:树形结构中,除结构为空外,都存在着的有且仅有一个的起始结点,叫做树根。
* 前驱:存在前一个结点。
* 后继: 存在后一个结点。
* 二叉树: 最简单的树形结构,特点是:树中每个结点最多关联到两个后继结点;关联的后继结点分左右,或为左关联结点,或为右关联结点。
* 左子树、右子树:左关联结点就是左子树;右~就是右~。
* 空树:树中结点个数为零。
* 单点树:只有一个结点—根。
* 父节点、子节点:一棵二叉树的根结点称为该树的子树根结点的父结点;子树的根结点称为二叉树树根结点的子结点。
* 左/右子结点:左/右后继结点
* 父子关系:父结点到子结点的边形成的一种单方向的父/子结点关系。
* 祖先/子孙结点:基于父子关系可以定义其传递关系,称为~。
* 树叶:两棵子树都空,没有子结点的结点叫做~。
* 分支节点:树中除树叶外的其余结点叫做~。
* 结点的度数:一个结点的子结点的个数就是它的度数。
* 路径:根据祖先/子孙关系,从一个祖先结点到它的任何子孙结点都存在一系列的边,形成了两者的联系。这一系列首尾相连的边称为树中的一条路径。每个结点到它的子结点的有一条长度为1路径;到它自身路径长度为0.
* 路径长度:一条路径包含的多少条数。
*  结点的层数:从根开始,根的层数为0,往下子结点为下一层,层数+1.
* 树的高度(深度):树中结点的最大层数就是高度。
* 满二叉树:所有分直结点的度数都是2.
* 扩充二叉树:加入足够多的新叶结点,使得原有结点度数都变为2.
* 内部结点、外部结点:扩充二叉树中,原有结点就是内部结点,新增的新叶结点就是外部结点。
* 内部路径长度、外部路径长度:从树根到树中各内部结点的路径长度的和;~到树中的外部结点的~和。
* 完全二叉树:对于一棵高度为h的二叉树,从0~h-1层的结点度数都为2(结点都满),如果最下的那一层结点不满,则所有结点都在最左边连续排列,空位在右边,这样的二叉树就叫做~。
* 二叉树的遍历:按照某种系统化的方式,访问二叉树的每个结点一次(可能会操作结点的数据)。
* 深度优先遍历:顺着二叉树的一条路径走,直到检查完一个叶结点,往下没有结点了,才回溯继续。
* 先根序(先序)遍历:按照DLR顺序,先访问根结点,再访问左子结点,最后右子结点。
* 中根序(对称序/中序)遍历:LDR
* 后根序(后序)遍历:LRD
* 先根序列:按照先根序遍历得到的结点访问序列。
* 对称(中根)序列:按照中根序~。
* 后根序列:按照后根序~。
* 宽度优先遍历(按层次顺序遍历):在所有路径上齐头并进。
* 层次序列:按照宽度优先遍历得到的结点序列。
* 表达式树:二叉树中结点与子树的关系可用于表示运算符和运算对象的作用关系。
* 二元表达式:只包含二元运算符的表达式。
* 算术表达式:二元表达式中的运算对象都是数。
* 表达式求值:对表达式化简,把能计算的都计算出来。
* 优先队列:基于队列,特点是存入其中的数据都附有一个数值,叫做优先级,用来表示这个项的优先程度。优先队列要保证任何时候,访问或者弹出的都是这个优先队列中元素的优先级最高的元素。
* 优先关系:集合中元素之间的关系为有序的,靠前的就是更优先的。
* 优先级:项的优先程度。
* 堆:结点里存储数据的完全二叉树就是堆,堆中的数据的存储要满足一种关系,任何一个结点里所存的数据,它按规定的优先关系都要先用或者等于其子结点里的数据。
* 堆序:任何一个结点里所存的数据按规定的优先关系都要先用或者等于其子结点里的数据。
* 小顶堆:数据小的优先构造出来的堆就是小顶堆。
* 大顶堆:数据大的优先构造出来的堆就是大顶堆。
* 筛选(向下筛选、向上筛选)(以小顶堆为例):把元素和它的父结点的数据相比较,小的话交换两个元素的位置,不断进行这样的操作,直到不小于父结点的数据,或者是到达根结点,才停止。
* 堆排序:如果一个连续表里存储的数据是一个小顶堆,按照优先队列的操作方式反复弹出元素,就能得到一个递增序列。
* 离散事件模拟:前面讲过。被模拟系统的行为可以抽象为一些离散事件的发生,所发生事件可以引发新的时间等。人们希望通过计算机模拟理解系统行为,评价和涉及真实世界中实际的或者所需的系统。
* 模拟框架:模拟按照事件的发生时间为顺序来处理,在模拟系统里用一个优先队列保存,已知在将来某些特定时刻发生的事件,系统的运行就是不断从优先队列里取出等待事件,一个个的处理,直到模拟结束(或者没有事件了,队列为空)。要注意的是,一些时间在处理时,可能会引发新的事件。
* 事件队列:一个保存模拟中缓存的事件的优先队列。
* 宿主系统:事件有一个host参数,用于表示该事件的发生所在的模拟系统。事件执行时,可能会访问宿主系统。
* 二叉树的链接实现:用一个数据单元表示一个二叉树结点,通过子结点的链接(指针)建立结点间的联系。(与连续表的链接实现类似)。
* 二叉树结点类:结点类的构造函数接受三个参数,分别为它的结点数据、左子结点链接、右子结点链接(默认值为None,使用默认值时构造的就是叶结点)。
* 非递归的二叉树遍历算法:循环、生成器
* 非递归后序遍历:这种遍历方法比较麻烦,因为一个根结点要经过三次,第一次经过去处理左子结点,第二次经过是从左子结点返回来去处理右子结点,最后返回来才去处理根结点。对非递归先根序遍历算法进行修改就能得到。
* 下行循环:循环体中的内层循环,目标是找到下一个应该访问的结点。
* 二叉树类:(为什么已经有了二叉树结点类,还要定义一个二叉树类呢?直接基于结点类构造的二叉树具有递归的结构,可以很方便地递归处理,但这样的"二叉树结构"有不统一之处:用None表示空树,但是None的类型并不是BinTNode。此外还有一个原因是,基于二叉树结点类构造的结构不是一种封装良好的抽象数据类型。)
* 带权扩充二叉树:给扩充二叉树的每个外部结点标一个数值,用来表示与该叶有关的某种性质,称为这个结点的权。进而得到的就是~,其外部路径长度是WPL=i从0到m,wili之和。
* 哈夫曼树(最优二叉树):设有实数集W={w0,w1,...,wm-1},T是一颗扩充二叉树,它的m个外部结点分别以wi(i=0,1,2...n-1)为权,而且T的带权外部路径长度WPL在所有的这样的扩充二叉树中达到最小,那么,T就被称为数据集W的最优二叉树或者哈夫曼树。
* 哈夫曼算法:就是构造哈夫曼树的算法 。算法的输入为实数集W={w0,w1,...,wm-1},F是一个包含k棵二叉树的集合,开始时k=m,F={T0,T1...Tm-1},其中的每个Ti都是一棵只包含权威Wi的根结点的单点二叉树。重复以下步骤,直到集合F中只剩下一棵树,这棵二叉树就是集合W上的哈夫曼树(注意最后得到的哈夫曼树不唯一):1.构造一个新二叉树,它的左右子二叉树是从集合F中选取的两棵权值最小的二叉树,构造出的这棵新二叉树的权值设置为那两棵二叉树的权值之和。2.将那两棵二叉树从集合F中删掉,把新构造出的这棵二叉树加入到集合F中。
* 哈夫曼编码:把哈夫曼算法中的集合F定义为需要编码的字符集合,实数集W定义为各个字符在实际信息传输或储存中出现的频率,构造出一棵哈夫曼树,树中的每个分支结点到它的左子结点的边上标0,到它的右子结点的边上标1,以从根节点到一个外部结点的路径上的0/1序列,作为这个外部结点的标记字符的编码,这样得到的就是哈夫曼编码。
* 最优编码:哈夫曼编码是给定字符集(出现频率确定)的最优编码。存储传输时平均开销最小;对任意一对字符Ci和Cj,字符Ci的编码不是Cj编码的前缀。
* 有序树和无序树:根据子树排列顺序有无意义,可把树分为有序树和无序树。由于计算机表示时存在自然的顺序,所以数据结构中主要考虑有序树,下面默认树是有序树。
* 树和树林:树和二叉树差不多,不同的地方有:树的分支结点的度数没有限制,可以有任意的子结点;二叉树的子结点有左右之分,而在树中没有这个概念,但是因为树的子结点是有序的,所以可以说第一个子树,下一个子树...;树林就是树的集合。
* 树根:非空树中,有且仅有一个的起始结点就是树根。
* 有序树林和无序树林:根据树林中的树排列顺序有无意义,可分为~~,有序树林可以说第一棵树,下一棵树...
* 搜索树:前面提过。在一般的状态搜索问题里,搜索过程中经历的状态(结点)和状态之间的转移关系(边)形成了一棵树,称为‘搜索树’。
* 子结点引用表示法:子指针表示法。用一个数据单元表示结点,通过结点间的链接表示树结构,可以用list表示每个结点。(由于树的结点的度数不定,所以要给结点引用域设置一个值,度数超过这个值的树不支持)(缺点:会出现大量空闲的节点引用域,造成浪费)
* 父结点引用表示法:为克服子结点引用表示法空间浪费的缺点,又提出了父结点引用表示法。在树中,除了根结点外,每个分支结点都有且仅有一个父结点。所以,每个结点(根结点除外)可以只用一个引用域来保存父结点的引用。方法:用一个顺序表表示一棵树,每个表元素对应于一个结点,记录结点数据和父结点引用。(优点:空间开销小。缺点:要访问一个结点的子结点,需要通过查找,复杂度是O(n))
* 长子-兄弟表示法:树的二叉树表示。二叉树的左子结点表示树结点的第一个子结点,右子结点表示结点的下一个子结点。(这样只适用于结点度数小于等于2的树。)

你可能感兴趣的:(基础)