度:某一节点子树(孩子、子节点)的个数。
树的度:树中结点的最大度数。
“结点”和“节点”这两种叫法意思是一样的,都可以用。
根节点:一棵(非空)树有且仅有一个的节点。
叶子结点:度为 0 的结点,也称为终端结点。
分支结点:度不为 0 的结点,也称为非叶子节点或非终端结点(根节点也可能属于分支节点)。
层数:根节点在第一层,根节点的子节点在第2层,以此类推。
深度:是从根结点开始自顶向下逐层累加的。
高度:是从叶结点开始自底向上逐层累加的。
有序树:树中任意节点的子节点之间有顺序关系。
无序树:树中任意节点的子节点之间无顺序关系。
树中两个结点之间的:
路径:由这两个结点之间所经过的结点序列构成。
路径长度:路径上所经过的边的个数。
孩子:节点的子树(子节点)。
双亲:节点是子树的双亲。
兄弟:同一个双亲的孩子之间称为兄弟。
堂兄弟:双亲在同一层的节点互为堂兄弟。
祖先:从根到此节点所经分支上的所有节点都称为此节点的祖先。
子孙:以某节点为根的子树中的任一节点都称为此节点的子孙。
二叉树的特点是每个结点至多只有两棵子树,即二叉树中不存在度大于2的结点。
二叉树的子树有左右之分,其次序不能颠倒。
满二叉树 | 完全二叉树 |
---|---|
树的高度为 n n n,且含有 2 n − 1 2^n-1 2n−1个结点的二叉树; 树的每层都含有最多的结点; 叶子结点都集中在满二叉树的最下层; 除叶子结点外的其他结点度数均为2; | 每个结点都与和它高度相同的满二叉树结点的编号对应; 叶子结点只可能在最下面两层出现; 度为1的结点最多只能有一个,且该节点只有左孩子; 若某结点只有左孩子,或者是叶子结点,那么只要存在编号大于该结点的结点一定是叶子结点; 结点个数为奇数时每个分支结点都含有左右孩子; 结点个数为偶数时只有编号最大的分支节点没有右孩子; |
满二叉树也属于完全二叉树的一种,生成一个完全二叉树的口诀就是:“从上到下,从左到右”。
(1)顺序存储结构:
(2)链式存储结构:
(1)树中的结点数等于所有结点的度数加 1 。若树中结点总数为 n,则:n = 分支数(即度数之和)+1。
【2010统考真题】在一棵度为4的树T中,若有20个度为4的结点,10个度为3的结点,1个度为2的结点,10个度为1的结点,则树T的叶子结点个数是(B)。
A.41
B.82
C.113
D.122
【分析】设叶子结点有k个,总结点数为n,则有: n = ( 20 + 10 + 1 + 10 ) + k = ( 20 ∗ 4 + 10 ∗ 3 + 1 ∗ 2 + 10 ∗ 1 ) + 1 n=(20+10+1+10)+k=(20*4+10*3+1*2+10*1)+1 n=(20+10+1+10)+k=(20∗4+10∗3+1∗2+10∗1)+1,得到 41 + k = 123 41+k=123 41+k=123,即 k = 82 k=82 k=82。
【训练例题】已知一棵度为4的树中,度为 0、1、2、3 的结点数分别为 14、4、3、2,求该树的结点总数 n 和度为 4 的结点个数。
【分析】设度为4的结点个数为k,则有: n = ( 14 + 4 + 3 + 2 ) + k = ( k ∗ 4 + 2 ∗ 3 + 3 ∗ 2 + 4 ∗ 1 ) + 1 n=(14+4+3+2)+k=(k*4+2*3+3*2+4*1)+1 n=(14+4+3+2)+k=(k∗4+2∗3+3∗2+4∗1)+1,得到 23 + k = 4 k + 17 23+k=4k+17 23+k=4k+17,即 k = 2 , n = 25 k=2,n=25 k=2,n=25。
上面第一条性质适用于所有树,而下面将介绍二叉树专有的五个性质。
(2)非空二叉树上的叶子结点数等于度为 2 的结点数加 1,即: n 0 = n 2 + 1 n_0 = n_2 +1 n0=n2+1。
由性质一“结点总数 = 结点度数+1”,有: n = n 0 + n 1 + n 2 = ( n 0 ∗ 0 + n 1 ∗ 1 + n 2 ∗ 2 ) + 1 n = n_0 + n_1 + n_2 = (n_0*0+n_1*1+n_2*2)+1 n=n0+n1+n2=(n0∗0+n1∗1+n2∗2)+1,左右消去后得上式。
(3)完全二叉树中结点 i 的左孩子编号为 2 i 2i 2i。
设结点 i 是第k行的第x个,则它属于第 ( 2 k − 1 − 1 ) + x = 2 k − 1 + ( x − 1 ) (2^{k-1}-1)+x=2^{k-1}+(x-1) (2k−1−1)+x=2k−1+(x−1) 个结点;
而它的左子节点是第k+1行的,属于第 ( 2 ( k + 1 ) − 1 − 1 ) + 2 ∗ ( x − 1 ) + 1 = 2 k + 2 ∗ ( x − 1 ) = 2 ∗ [ 2 k − 1 + ( x − 1 ) ] (2^{(k+1)-1}-1)+2*(x-1)+1=2^k+2*(x-1)=2*[2^{k-1}+(x-1)] (2(k+1)−1−1)+2∗(x−1)+1=2k+2∗(x−1)=2∗[2k−1+(x−1)] 个结点;
(4)非空二叉树上第 k 层上至多有 2 k − 1 2^{k-1} 2k−1 个结点(k≥1) 。
等比数列的通项。
(5)高度为 h 的二叉树至多有 2 h − 1 2^h-1 2h−1 个结点(h≥1)。
由等比数列求和公式: a 1 − a n ∗ q 1 − q \frac{a_1-a_n*q}{1-q} 1−qa1−an∗q,有: 1 − 2 h − 1 ∗ 2 1 − 2 = 2 h − 1 \frac{1-2^{h-1}*2}{1-2}=2^h-1 1−21−2h−1∗2=2h−1。
(6)具有 n 个结点(n>0)的完全二叉树的高度为: ⌊ l o g 2 n ⌋ + 1 \lfloor log_2n \rfloor+1 ⌊log2n⌋+1。
由公式五可推知: 2 h − 1 ≤ n ≤ 2 h − 1 2^{h-1} \leq n \leq 2^h-1 2h−1≤n≤2h−1,因此 { h ≤ l o g 2 n + 1 = l o g 2 ( 2 n ) h ≥ l o g 2 ( n + 1 )
{h≤log2n+1=log2(2n)h≥log2(n+1)" role="presentation" style="position: relative;"> { h ≤ l o g 2 n + 1 = l o g 2 ( 2 n ) h ≥ l o g 2 ( n + 1 )
(1)先序遍历:若二叉树非空,则访问根结点,先根遍历左子树,后根遍历右子树——“根左右”。
(2)中序遍历:若二叉树非空,则中根遍历左子树,访问根结点,中根遍历右子树——“左根右”。
(3)后序遍历:若二叉树非空,则后根遍历左子树,后根遍历右子树,访问根结点——“左右根”。
(4)层序遍历:从二叉树的根结点开始,按自上而下,从左到右的顺序进行遍历。
对于一个表达式,可用一个二叉树来表示,例如对于表达式:
a
−
b
/
(
c
+
d
)
+
e
∗
f
a-b/(c+d)+e*f
a−b/(c+d)+e∗f
我们不难发现,先序遍历和后序遍历可以将中缀表达式转换成前缀表达式和后缀表达式。
【经典例题】例如,求先序序列(ABCDEFGHI)和中序序列(BCAEDGHFI)所确定的二叉树以及后序序列。
已知一棵二叉树的后序序列为DABEC,中序序列为DEBAC,则先序序列为?
下列序列中,不能唯一地确定一棵二叉树的是(D) 。
A.层次序列和中序序列
B.先序序列和中序序列
C.后序序列和中序序列
D.先序序列和后序序列(不能没有中序序列来确定根及其左右分布情况,否则会得到多个二叉树)
因为树这一节知识结构分布不太一样,每一部分都涉及大量知识点,所以单独小结。
线索二叉树是对二叉链表存储的改进。
为此还需要添加两个标识域,用于标识指针是指向其左/右孩子还是指向其遍历时的前驱/后继。
例如:构造下列二叉树的中序线索二叉树
前面介绍了二叉树的存储结构,树同样也可以采用顺序存储结构和链式存储结构,但无论是哪种存储结构都需要能唯一反映树中各结点之间的逻辑关系。
下面介绍三种表示法,对应了三种存储方式(包含一种或两种存储结构)。
双亲表示法就是采用一组连续空间(即静态链表)来存储每个结点。
采用顺序存储结构,在每个结点中增设一个伪指针(一个结点就是一个结构体对象),指示其双亲结点在数组中的位置下标。
将每个结点的孩子结点,按从左到右的顺序用单链表链接起来( n 个结点就有 n 个孩子链表)。
采用顺序表+链表的结构,结合层次遍历实现该方法。链表的每个结点存储了树中当前结点的孩子节点对应的数组下标,以及下一个链表结点的地址。
孩子兄弟表示法又叫做二叉树表示法。这种表示法是三者之中最重要的!
以二叉链表作为树的存储结构。孩子兄弟表示法使每个结点包括三部分内容:
孩子兄弟表示法可以将树转换成二叉树:
① 将一棵树的兄弟结点之间相连,然后双亲只保留和第一个孩子结点的连线,从而生成对应的二叉树;
② 将对应的二叉树用链表表示,每个结点左侧相连的结点用左指针指向,右侧相连的结点用右指针指向,形成二叉树表示法;
③ 为了更加直观反映每个结点左右指针的指向情况,将二叉树链表画成二叉树的形式。
除此之外,二叉树和森林之间也可以相互转换。(不介绍一般的树和森林直接转换)
将森林转换为二叉树比较简单:
① 将森林中的每棵树先转换为二叉树,此时各个二叉树的根节点必然是没有右孩子的;
② 将后一棵二叉树视为前一棵二叉树的右子树,依次拼接在一起,森林就转换成了一棵二叉树。
反过来执行上述操作,便可以将二叉树转换为森林:
① 若二叉树非空,将其根节点与根节点的左子树视为第一棵二叉树,与右子树断开;
② 对断开后的右子树重复操作①,直到某一结点没有右孩子(一个结点也可单独成树);
② 将所得的每棵二叉树都转换回树,就得到了森林。
树的遍历:
二叉排序树(BST),也称二叉查找树或者二叉判定树(口诀:左小右大)。原属于第七章查找部分的知识点。
(1)若左子树非空,则左子树上所有结点关键字值均小于根结点的关键字值。
(2)若右子树非空,则右子树上所有结点关键字值均大于根结点的关键字值。
(3)左、右子树本身也分别是一棵二叉排序树(满足上述两点)。
插入操作就是严格遵守“比当前结点小就进入左子树区域,比当前结点大就进入右子树区域——左小右大,直到自身成为叶子结点”。
请按顺序插入:53 17 78 9 45 65 94 81 88 23,生成的二叉排序树如图:
删除操作的实现过程按 3 种情况来处理:
① 若被删除结点 z 是叶子结点,则直接删除。
② 若结点 z 只有一棵左子树或右子树,则让 z 的子树顶上,替代 z 的位置。
③ 若结点 z 有左、右两棵子树,则先中序遍历得到序列,令 z 在序列中的直接后继(或直接前驱)替代 z,然后从二叉排序树中删去这个直接后继(或直接前驱),此时就转换成了第一或第二种情况。
第①小点例题:删除88;
第②小点例题:删除45;
第③小点例题:删除78。由于中序表达式:9 17 23 45 53 65 78 81 88 94,所以:
查找过程就是从根结点开始,将给定的值与结点的关键字对比。
查找所有给定值(给定值一般就是组成树的这些关键字的集合)需要比较的关键字总个数,除以给定值的总个数,所得的结果我们称为平均查找长度:ASL——A:average;S:search;L:len。
情况(a) | 情况(b) |
---|---|
查找成功/失败的平均查找长度ASL为: { A S L 成功 = ( 1 + 2 ∗ 2 + 3 ∗ 4 + 4 ∗ 3 ) / 10 = 2.9 A S L 失败 = ( 3 ∗ 5 + 4 ∗ 6 ) / 11 = 39 / 11 | 查找成功/失败的平均查找长度ASL为: { A S L 成功 = ( 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 ) / 10 = 2.9 A S L 失败 = ( 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 ∗ 2 ) / 11 = 65 / 11 |
给定值全部查找成功的情况下: 第一层1个元素各需要比较1次; 第二层2个元素各需要比较2次; 第三层4个元素各需要比较3次; 第四层3个元素各需要比较4次; | 给定值全部查找成功的情况下: 每层1个元素需要比较k次,k为所在层数(深度); |
给定值全部查找失败的情况下: 第三层12、53、60共5种失败可能,各需比较3次; 第四层28、40、70共6种失败可能,各需比较4次; | 给定值全部查找失败的情况下: 前九层各有1种失败可能,均需比较k次,k为所在层数(深度); 第十层有2种失败可能,均需比较10次; |
对于下列关键字序列,不可能构成某二叉排序树中一条查找路径的是(A)。
A.95,22,91,24,94,71(所构成的二叉排序树出现了度为2的分支结点,不可数是查找路径)
B.92,20,91,34,88,35
C.21,89,77,29,36,38
D.12,25,71,68,33,34
从空树开始,依次插入元素 52,26,14,32,71,60,93,58,24 和 41 后构成了一棵二叉排序树。在该树查找60 要进行比较的次数为(D)。
A.3(所构成的二叉排序树中,60所在的结点深度为3)
B.4
C.5
D.6
一棵二叉排序树按先序遍历得到的序列为(50,38,30,45,40,48,70,60,75,80),试画出该二叉排序树,并求出等概率下查找成功和查找失败的平均查找长度。
又称平衡树(AVL):任意结点的左、右子树高度差的绝对值不超过1,且子树都是平衡树。
定义每个结点左子树与右子树的高度差为该结点的平衡因子,平衡因子的值只可能是 -1、0 或 1。叶子结点的平衡因子一定为 0。
考试中一般不会让你计算平衡因子,知道这个概念是为了判断平衡二叉树。
最小平衡二叉树公式:F(n)=F(n-1)+F(n-2)+1,用于计算构建某一高度的平衡二叉树所需结点个数的最小值。其中:
例如:F(1)=1,F(2)=2,得:F(3)=2+1+1=4;{ F(h) | h = 1,2,3,… } = { 1,2,4,7,12,20,33,… }。
【例题】
含有20个结点的平衡二叉树的最大深度为(C)。
A.4
B.5
C.6( 列出数列:1、2、4、7、12、20,可知 F(6)=20,即最大深度 h=6)
D.7
下列二又排序树中,满足平衡二叉树定义的是(B)。
平衡二叉树实际上就是对二叉排序树引入了新的约束,用来限制二叉排序树的高度,从而加快查找速度(减少平均查找路径)。因此平衡二叉树的插入操作同样遵循“左小右大”的方式,并且还需要时刻注意保证每个结点的平衡因子的绝对值不能超过1。
当插入后出现了平衡因子的绝对值大于1的结点,就需要对当前的树做出调整,使它变回平衡二叉树。调整需要遵循两个原则:① 降低高度;② 保持二叉排序树性质。
(1)LL型:右旋转
(2)RR型:左旋转
(3)LR型:先左旋转后右旋转
(4)RL型:先右旋转后左旋转
关于上面四类情况,有几点需要补充:
平衡二叉树的删除操作同样与二叉排序树一样,依据删除结点的不同情况要进行不同的变换。如果在变换过程中也破坏了树的“平衡性”,就像插入操作中一样找出“最小不平衡子树”,然后依据从“根节点”到达被删除结点的路径的前两段确定所属类型,再对“最小不平衡子树”进行相应变换。
【分析】本次插入是 RL型,进行相应变换可得 37 的子节点为 24 和 53 。
赋予树的结点权值,所得到的就是带权树。
【经典例题】二叉树有 4 个叶子结点 a、b、c、d,分别带权 7、5、2、4,该二叉树的最小带权路径长度为:
哈夫曼树的构造在课本有详细描述,这里简单概括并附上口诀:
(1)构造森林全是根;
(2)选用两小造新人;
(3)删除两小添新人;
(4)重复2、3剩单根。
简单概括,就是用单个结点存储权值,每次选择权值最小的两个结点作为左右子节点构造一棵树,然后删除这两个结点并将它们的权值之和存储在根节点中,再把根节点添加进可选结点,然后重新选择两个新的最小结点重复上面步骤,直到可选的结点只剩一个。
哈夫曼编码的目的是获取最优的前缀编码。
哈夫曼编码的实现:(口诀:左邻右里)
5个字符有如下4种编码方案,不是前缀编码的是(D)。
A.01,0000,0001,001,1
B.011,000,001,010,1
C.000,001,010,011,100
D.0,100,110,1110,1100(明显110是1100的前缀。当然我们也可以通过画左0右1的二叉树,假设五个字符分别是A、B、C、D、E,画完后发现C是E的双亲结点,不是叶子结点。)
设给定权集 w = [ 5,7,2,3,6,8,9 ],试构造关于w的一根哈夫曼树,并求其加权路径长度WPL。