1、1第六章 树及二叉树一、下面是有关二叉树的叙述,请判断正误()1. 若二叉树用二叉链表作存贮结构,则在 n 个结点的二叉树链表中只有 n1 个非空指针域。()2.二叉树中每个结点的两棵子树的高度差等于 1。 ()3.二叉树中每个结点的两棵子树是有序的。 ()4.二叉树中每个结点有两棵非空子树或有两棵空子树。 ()5.二叉树中每个结点的关键字值大于其左非空子树(若存在的话)所有结点的关键字值,且小于其右非空子树(若存在的话)所有结点的关键字值。 (应当是二叉排序树的特点)()6.二叉树中所有结点个数是 2k-1-1,其中 k 是树的深度。 (应 2i-1) ()7.二叉树中所有结点,如果不存在非
2、空左子树,则不存在非空右子树。 ()8.对于一棵非空二叉树,它的根结点作为第一层,则它的第 i 层上最多能有 2i1 个结点。 (应 2i-1)()9.用二叉链表法(link-rlink)存储包含 n 个结点的二叉树,结点的 2n 个指针区域中有 n+1 个为空指针。(正确。用二叉链表存储包含 n 个结点的二叉树,结点共有 2n 个链域。由于二叉树中,除根结点外,每一个结点有且仅有一个双亲,所以只有 n-1 个结点的链域存放指向非空子女结点的指针,还有 n+1 个空指针。 )即有后继链接的指针仅 n-1 个。()10.具有 12 个结点的完全二叉树有 5 个度为 2 的结点。最快方法:用叶子数
3、n/26,再求 n2=n0-1=5 ( ) 11、哈夫曼树中没有度为 1 的结点,所以必为满二叉树。( )12、在哈夫曼树中,权值最小的结点离根结点最近。( )13、线索二叉树是一种逻辑结构。( )14、深度为 K 的完全二叉树至少有 2K-1个结点。( )15、具有 n 个结点的满二叉树,其叶结点的个数为(n+1)/2。( )16、前序和中序遍历用线索树方式存储的二叉树,不必使用栈。( )17、哈夫曼树是带权路径长度最短的树,路径上权值较大的点离根较远。二、填空1 由个结点所构成的二叉树有 5 种形态。 2. 一棵深度为 6 的满二叉树有 n 1+n2=0+ n2= n0-1=31 个分支结
4、点和 2 6-1 =32 个叶子。注:满二叉树没有度为 1 的结点,所以分支结点数就是二度结点数。3 一棵具有个结点的完全二叉树,它的深度为 9 。( 注:用 log2(n) +1= 8.xx +1=94. 设一棵完全二叉树有 700 个结点,则共有 350 个叶子结点。答:最快方法:用叶子数n/2350 25. 设一棵完全二叉树具有 1000 个结点,则此完全二叉树有 500 个叶子结点,有 499 个度为 2 的结点,有 1 个结点只有非空左子树,有 0 个结点只有非空右子树。答:最快方法:用叶子数n/2500 ,n 2=n0-1=499。 另外,最后一结点为 2i 属于左叶子,右叶子是空
5、的,所以有 1 个非空左子树。完全二叉树的特点决定不可能有左空右不空的情况,所以非空右子树数0.6. 一棵含有 n 个结点的 k 叉树,可能达到的最大深度为 n ,最小深度为 2 。答:当 k=1(单叉树)时应该最深,深度n(层) ;当 k=n-1(n-1 叉树)时应该最浅,深度2(层) ,但不包括 n=0 或 1 时的特例情况。教材答案是“完全 k 叉树” ,未定量。)7. 二叉树的基本组成部分是:根(N) 、左子树(L)和右子树(R) 。因而二叉树的遍历次序有六种。最常用的是三种:前序法(即按 N L R 次序) ,后序法(即按 L R N 次序)和中序法(也称对称序法,即按 L N R
6、次序) 。这三种方法相互之间有关联。若已知一棵二叉树的前序序列是 BEFCGDH,中序序列是 FEBGCHD,则它的后序序列必是 F E G H D C B 。 解:法 1:先由已知条件画图,再后序遍历得到结果;法 2:不画图也能快速得出后序序列,只要找到根的位置特征。由前序先确定 root,由中序先确定左子树。例如,前序遍历 BEFCGDH 中,根结点在最前面,是 B;则后序遍历中 B 一定在最后面。法 3:递归计算。如 B 在前序序列中第一,中序中在中间(可知左右子树上有哪些元素) ,则在后序中必为最后。如法对 B 的左右子树同样处理,则问题得解。8.中序遍历的递归算法平均空间复杂度为 O
7、(n) 。答:即递归最大嵌套层数,即栈的占用单元数。精确值应为树的深度 k+1,包括叶子的空域也递归了一次。9. 用 5 个权值3, 2, 4, 5, 1构造的哈夫曼(Huffman)树的带权路径长度是 33 。解:先构造哈夫曼树,得到各叶子的路径长度之后便可求出 WPL(453)2(12)3=33(15) (9) (6) 注:两个合并值先后不同会导致编码不同,即哈夫曼编码不唯一)4 5 3 (3) (注:合并值应排在叶子值之后)1 2(注:原题为选择题:32 33 34 15)10、N 个结点的二叉树采用二叉链表存放,共有空链域个数为 n+111、深度为 6(根层次为 1)的二叉树至多有 2
8、6 1 个结点。12、 已知一棵完全二叉树的第 5 层有 3 个结点,其叶子结点数是 9 。3三、单项选择题( C )1 不含任何结点的空树 。()是一棵树; ()是一棵二叉树; ()是一棵树也是一棵二叉树; ()既不是树也不是二叉树答:以前的标答是 B,因为那时树的定义是 n1( C )2二叉树是非线性数据结构,所以 。、它不能用顺序存储结构存储; 、它不能用链式存储结构存储; 、顺序存储结构和链式存储结构都能存储; 、顺序存储结构和链式存储结构都不能使用 ( C )3.具有 n(n0)个结点的完全二叉树的深度为 。() log 2(n) () log 2(n) () log 2(n) +1
9、 () log2(n)+1注 1:x 表示不小于 x 的最小整数; x表示不大于 x 的最大整数,它们与 含义不同!注 2:选(A)是错误的。例如当 n 为 2 的整数幂时就会少算一层。似乎 log2(n) +1是对的?( A )4把一棵树转换为二叉树后,这棵二叉树的形态是 。()唯一的 ()有多种()有多种,但根结点都没有左孩子 ()有多种,但根结点都没有右孩子5. 从供选择的答案中,选出应填入下面叙述 ? 内的最确切的解答,把相应编号写在答卷的对应栏内。树是结点的有限集合,它 A 根结点,记为 T。其余的结点分成为 m(m0)个 B 的集合 T1,T2,Tm,每个集合又都是树,此时结点 T
10、 称为 Ti的父结点,T i称为 T 的子结点(1im ) 。一个结点的子结点个数为该结点的 C 。供选择的答案A: 有 0 个或 1 个 有 0 个或多个 有且只有 1 个 有 1 个或 1 个以上 B: 互不相交 允许相交 允许叶结点相交 允许树枝结点相交C: 权 维数 次数(或度) 序答案:ABC1,1,36.从供选择的答案中,选出应填入下面叙述 ? 内的最确切的解答,把相应编号写在答卷的对应栏内。二叉树 A 。在完全的二叉树中,若一个结点没有 B ,则它必定是叶结点。每棵树都能惟一地转换成与它对应的二叉树。由树转换成的二叉树里,一个结点 N 的左子女是 N在原树里对应结点的 C ,而
11、N 的右子女是它在原树里对应结点的 D 。供选择的答案A: 是特殊的树 不是树的特殊形式 是两棵树的总称 有是只有二个根结点的树形结构 B: 左子结点 右子结点 左子结点或者没有右子结点 兄弟4CD: 最左子结点 最右子结点 最邻近的右兄弟 最邻近的左兄弟 最左的兄弟 最右的兄弟答案:A= B= C= D 7、将一棵有 100 个结点的完全二叉树从根这一层开始,每一层从左到右依次对结点进行编号,根结点编号为 1,则编号为 49 的结点的左孩子的编号为( A) A、98 B、99 C、50 D、48答案:ABCDE2,1,1,38、设森林 F 中有三棵树,第一、第二和第三棵树的结点个数分别为 M
12、1、M2 和 M3。与森林F 对应的二叉树根结点的右子树上的结点个数是(D)A)M1 B)M1+M2 C)M3 D)M2+M39、将一棵有 100 个结点的完全二叉树从根这一层开始,每一层从左到右依次对结点进行编号,根结点编号为 1,则编号最大的非叶结点的编号为(C)A、48 B、49 C、50 D、5110、某二叉树结点的中序序列为 A、B、C、D、E、F、G,后序序列为B、D、C、 A、F、G、E,则其左子树中结点数目为(C)A)3 B)2 C)4 D)5四、简答题(每小题 4 分,共 20 分)1. 一棵度为 2 的树与一棵二叉树有何区别?答:度为 2 的树从形式上看与二叉树很相似,但它
13、的子树是无序的,而二叉树是有序的。即,在一般树中若某结点只有一个孩子,就无需区分其左右次序,而在二叉树中即使是一个孩子也有左右之分。2.设如下图所示的二叉树 B 的存储结构为二叉链表,root 为根指针,结点结构为:(lchild,data,rchild) 。其中 lchild,rchild 分别为指向左右孩子的指针,data 为字符型,root 为根指针,试回答下列问题:1. 对下列二叉树 B,执行下列算法 traversal(root),试指出其输出结果;2. 假定二叉树 B 共有 n 个结点,试分析算法traversal(root)的时间复杂度。 (共 8 分)二叉树 B解:这是“先根再
14、左再根再右” ,比前序遍历多打印各结点一次,输出结果为:A B C C E E B A D F F D G G特点:每个结点肯定都会被打印两次;但出现的顺序不同,其规律是:凡是有左子树的AB DC F GEC 的结点类型定义如下:struct nodechar data;struct node *lchild, rchild;C 算法如下:void traversal(struct node *root)if (root)printf(“%c”, root-data);traversal(root-lchild);printf(“%c”, root-data);traversal(root-r
15、child);5结点,必间隔左子树的全部结点后再重复出现;如 A,B,D 等结点。反之马上就会重复出现。如 C,E,F,G 等结点。时间复杂度以访问结点的次数为主,精确值为 2*n,时间渐近度为 O(n).3.给定二叉树的两种遍历序列,分别是:前序遍历序列:D,A,C,E,B,H,F,G,I; 中序遍历序列:D,C,B,E,H,A,G,I,F,试画出二叉树 B,并简述由任意二叉树 B 的前序遍历序列和中序遍历序列求二叉树 B 的思想方法。解:方法是:由前序先确定 root,由中序可确定 root 的左、右子树。然后由其左子树的元素集合和右子树的集合对应前序遍历序列中的元素集合,可继续确定 ro
16、ot 的左右孩子。将他们分别作为新的 root,不断递归,则所有元素都将被唯一确定,问题得解。DAC FE GB H I4.给定如图所示二叉树 T,请画出与其对应的中序线索二叉树。解:要遵循中序遍历的轨迹来画出每个前驱和后继。中序遍历序列:55 40 25 60 28 08 33 542825 3340 60 08 54555、已知一棵二叉树,其中序序列 DBCAFGE,后序序列 DCBGFEA,构造该二叉树。解:6、已知叶子结点值 2,3,5,6,9,11,构造哈夫曼树,计算其带权路径长度。2825 3340 60 08 54 55282540555560330854NILNIL6解:7、给
17、定权值8,12,4, 5,26,16,9 ,构造一棵带权路径长度最短的二叉树,并计算其带权路径长度。解:或:WPL=83+44+54+162+93+123+262=207注:哈夫曼树的左右子树可以互换。8. (P60 4-26)试写出如图所示的二叉树分别按先序、中序、后序遍历时得到的结点序列。答:DLR:A B D F J G K C E H I L MLDR: B F J D G K A C H E L I MLRD:J F K G D B H L M I E C A79、 (把如图所示的树转化成二叉树。答:注意全部兄弟之间都要连线(包括度为 2 的兄弟),并注意原有连线结点一律归入左子树,
18、新添连线结点一律归入右子树。ABE CK F H DL G IM J10 画出和下列二叉树相应的森林。答:注意根右边的子树肯定是森林,而孩子结点的右子树均为兄弟。6.11、假设用于通信的电文仅由 8 个字母组成,字母在电文中出现的频率分别为0.07,0.19,0.02,0.06,0.32,0.03,0.21,0.10。试为这 8 个字母设计哈夫曼编码。使用 07 的二进制表示形式是另一种编码方案。对于上述实例,比较两种方案的优缺点。解:方案 1;哈夫曼编码先将概率放大 100 倍,以方便构造哈夫曼树。w=7,19,2,6,32,3,21,10,按哈夫曼规则:【(2,3) ,6, (7,10)】
19、, 19, 21, 32(100)(40) (60)19 21 32 (28)(17) (11)7 10 6 (5)2 3方案比较:0 1 0 1 0 119 21 32 0 10 1 0 17 10 6 0 12 3字母编号 对应编码 出现频率1 000 0.072 001 0.193 010 0.024 011 0.065 100 0.326 101 0.037 110 0.218 111 0.10字母编号 对应编码 出现频率1 1100 0.072 00 0.193 11110 0.024 1110 0.065 10 0.326 11111 0.037 01 0.218 1101 0.1
20、08方案 1 的 WPL2(0.19+0.32+0.21)+4(0.07+0.06+0.10)+5(0.02+0.03)=1.44+0.92+0.25=2.61方案 2 的 WPL3(0.19+0.32+0.21+0.07+0.06+0.10+0.02+0.03)=3结论:哈夫曼编码优于等长二进制编码六、算法设计题1.编写递归算法,计算二叉树中叶子结点的数目。解:思路:输出叶子结点比较简单,用任何一种遍历递归算法,凡是左右指针均空者,则为叶子,将其打印出来。法一:核心部分为:DLR(liuyu *root) /*中序遍历 递归函数*/if(root!=NULL)if(root-lchild=N
21、ULL) printf(“%dn“,root-data);DLR(root-lchild);DLR(root-rchild); return(0);法二:int LeafCount_BiTree(Bitree T)/求二叉树中叶子结点的数目 if(!T) return 0; /空树没有叶子 else if(!T-lchild /叶子结点 else return Leaf_Count(T-lchild)+Leaf_Count(T-rchild);/左子树的叶子数加 上右子树的叶子数 /LeafCount_BiTree 2.写出求二叉树深度的算法,先定义二叉树的抽象数据类型。解:法一:int de
22、pth(liuyu*root) /*统计层数*/int d,p; /*注意每一层的局部变量 d,p 都是各自独立的*/p=0;9if(root=NULL)return(p); /*找到叶子之后才开始统计*/else d=depth(root-lchild);if(dp) p=d; /*向上回朔时,要挑出左右子树中的相对大的那个深度值*/d=depth(root-rchild);if(dp)p=d;p=p+1;return(p);法二:int Get_Depth(Bitree T)/求子树深度的递归算法 if(!T) return 0; else m=Get_Depth(T-lchild); n
23、=Get_Depth(T-rchild); return (mn?m:n)+1; /Get_Depth 3、求二叉树中以元素值为 x 的结点为根的子树的深度。解:int Get_Sub_Depth(Bitree T,int x)/求二叉树中以值为 x 的结点为根的子树深度 if(T-data=x) printf(“%dn“,Get_Depth(T); /找到了值为 x 的结点,求其深度 exit 1; else if(T-lchild) Get_Sub_Depth(T-lchild,x); if(T-rchild) Get_Sub_Depth(T-rchild,x); /在左右子树中继续寻找
24、/Get_Sub_Depth 4.编写按层次顺序(同一层自左至右)遍历二叉树的算法。解:思路:既然要求从上到下,从左到右,则利用队列存放各子树结点的指针是个好办法。这是一个循环算法,用 while 语句不断循环,直到队空之后自然退出该函数。技巧之处:当根结点入队后,会自然使得左、右孩子结点入队,而左孩子出队时又会立即使10得它的左右孩子结点入队,以此产生了按层次输出的效果。level(liuyu*T)/* liuyu *T,*p,*q100; 假设 max 已知*/int f,r;f=0; r=0; /*置空队*/r=(r+1)%max;qr=T; /*根结点进队*/while(f!=r) /
25、*队列不空*/f=(f+1%max);p=qf; /*出队*/printf(“%d“,p-data); /*打印根结点*/if(p-lchild)r=(r+1)%max; qr=p-lchild; /*若左子树不空,则左子树进队*/ if(p-rchild)r=(r+1)%max; qr=p-rchild; /*若右子树不空,则右子树进队*/ return(0);法二:void LayerOrder(Bitree T)/层序遍历二叉树 InitQueue(Q); /建立工作队列 EnQueue(Q,T); while(!QueueEmpty(Q) DeQueue(Q,p); visit(p);
26、 if(p-lchild) EnQueue(Q,p-lchild); if(p-rchild) EnQueue(Q,p-rchild); /LayerOrder 5.编写算法判别给定二叉树是否为完全二叉树。答:int IsFull_Bitree(Bitree T)/判断二叉树是否完全二叉树,是则返回 1,否则返回 0 InitQueue(Q); flag=0; EnQueue(Q,T); /建立工作队列 while(!QueueEmpty(Q) 11 DeQueue(Q,p); if(!p) flag=1; else if(flag) return 0; else EnQueue(Q,p-lc
27、hild); EnQueue(Q,p-rchild); /不管孩子是否为空,都入队列 /while return 1; /IsFull_Bitree 分析:该问题可以通过层序遍历的方法来解决.与 6.47 相比,作了一个修改,不管当前结点 是否有左右孩子,都入队列.这样当树为完全二叉树时,遍历时得到是一个连续的不包含空 指针的序列.反之,则序列中会含有空指针. 6、阅读下列算法,若有错,改正之。7、阅读下面程序,并回答有关问题。其中 BSTree 为用二叉链表表示的二叉排序树类型。(1) 简要说明程序功能。 (5 分)(2) n 个结点的满二叉树的深度 h 是多少?(3 分)(3) 假设二叉排
28、序树*bst 是有 n 个结点的满二叉树,给出算法的时间复杂度。 (2 分)int Proc (BSTree *bst, KeyType K) BSTree f, q, s;s=(BSTree)malloc(sizeof(BSTNode);s- key = K; s- lchild = NULL; s- rchild = NULL;if ( *bst = NULL ) *bst = s; return 1; f = NULL; q = *bst;while( q != NULL ) if ( K key ) f = q; q = q - lchild; else f = q; q = q -
29、rchild; BiTree InSucc(BiTree q)/已知 q 是指向中序线索二叉树上某个结点的指针,/本函数返回指向*q 的后继的指针。r=q-rchild; /应改为 r=q;if(!r-rtag) while(!r-rtag)r=r-rchild; /应改为 while(!r-Ltag) r=r-Lchild;return r; /应改为 return r-rchild;/ISucc答:这是找结点后继的程序。共有 3 处错误。注:当 rtag1 时说明内装后继指针,可直接返回,第一句无错。当 rtag 0 时说明内装右孩子指针,但孩子未必是后继,需要计算。中序遍历应当先左再根再
30、右,所以应当找左子树直到叶子处。r=r-lchild; 直到 LTag=1; 应改为:while(!r-Ltag)r=r-L child;12if ( K key ) f - lchild = s; else f - rchild = s; return 1; 解:(1) 在二叉排序树中插入关键字为 K 的结点(2) h = log2 ( n+1 ) 或 h = log2 n + 1 (方括号表示向下取整)(3) O ( log2 ( n+1 ) ) 或 O ( log2 n )8、试设计算法计算一棵给定二叉树上所有结点数目。假设二叉树的存储结构描述如下:typedef struct BiTN
31、odeTElemType data;struct BiTNode *lchild;*rchild; /*左右孩子指针*/BiTNode,*BiTree;解:int CountNode(BinTree bt) if (bt=Null)return(0);else num1=CountNode(root-lchild);num2=CountNode(root-rchild);return(num1+num2+1); 9、计算二叉树上单分支结点数目。假设二叉树的存储结构描述如下:typedef struct BiTNodeTElemType data;struct BiTNode *lchild;*rchild; /*左右孩子指针*/ BiTNode,*BiTree;解:FUNC nodes1(t:bitre):integer;if t=Null then nodes1:=0else if (t-lchild= Null) and (t-rchild= Null) then nodes1:=0else if (t-lchild= Null) or (t-rchild= Null)then nodes1:=1+nodes1(t-lchild)+nodes1(t-rchild) 13else nodes1:=nodes1(t-lchild)+nodes1(t-rchild) ENDF;