()7.二叉树中所有结点,如果不存在非空左子树,则不存在非空右子树。
()&对于一棵非空二叉树,它的根结点作为第一层,则它的第i层上最多能有2*-1个结点。
()9•用二叉链表法(link-rlink)存储包含n个结点的二叉树,结点的2n个指针区域中有n+1个为空指针。
(V)10.具有12个结点的完全二叉树有5个度为2的结点。
二、填空
1.由3个结点所构成的二叉树有_种形态。
2.一棵深度为6的满二叉树有个分支结点和个叶子。
3.一棵具有257个结点的完全二叉树,它的深度为o
4.设一棵完全二叉树有700个结点,则共有个叶子结点。
5.设一棵完全二叉树具有1000个结点,则此完全二叉树有个叶子结点,有个
度为2的结点,有个结点只有非空左子树,有个结点只有非空右子树。
6•—棵含有n个结点的k叉树,可能达到的最大深度为,最小深度为—。
7.二叉树的基本组成部分是:
根(N)、左子树(L)和右子树(R)。
因而二叉树的遍历次
序有六种。
最常用的是三种:
前序法(即按NLR次序),后序法(即按次序)和
中序法(也称对称序法,即按LNR次序)。
这三种方法相互之间有关联。
若已知一棵二叉树的前序序列是BEFCGDH,中序序列是FEBGCHD,则它的后序序列必是。
8.中序遍历的递归算法平均空间复杂度为o
9.用5个权值{3.2,4.5,1}构造的哈夫曼(Huffman)树的带权路径长度是。
三、单项选择题
()1.不含任何结点的空树°
(A)是一棵树;(B)是一棵二叉树;
(C)是一棵树也是一棵二叉树;(D)既不是树也不是二叉树
()2.二叉树是非线性数据结构,所以°
(A)它不能用顺序存储结构存储;(B)它不能用链式存储结构存储;
(C)顺序存储结构和链式存储结构都能存储;
(D)顺序存储结构和链式存储结构都不能使用
()3.具有n(n>0)个结点的完全二叉树的深度为。
(A)「log2(n)1(B)Llog2(n)J(C)Llog2(n)J+l(I))「log2(n)+1]
()4.把一棵树转换为二叉树后,这棵二叉树的形态是o
(A)唯一的(B)有多种
(C)有多种,但根结点都没有左孩子(D)有多种,但根结点都没有右孩子
四、算法设计题
1•编写递归算法,计算二叉树中叶子结点的数目。
2.写出求二叉树深度的算法,先泄义二叉树的抽象数据类型。
或编写递归算法,求二叉树中以元素值为x的结点为根的子树的深度。
3.编写按层次顺序(同一层自左至右)遍历二叉树的算法。
或:
按层次输岀二叉树中所有结点:
4.已知一棵具有n个结点的完全二叉树被顺序存储于一维数组A中,试编写一个算法打印出编号为i的结点的双亲和所有的孩子。
5.编写算法判别给建二叉树是否为完全二叉树。
6.假设用于通信的电文仅由8个字母组成,字母在电文中出现的频率分别为0.07,0.19,0.02,0.06,0.32,0.03,0.21,0.10。
试为这8个字母设计哈夫曼编码。
使用0〜7的二进制表示形式是另一种编码方案。
对于上述实例,比较两种方案的优缺点。
习题精选答案
1.(V)2.(X)3.(J)4.(X)5.(X)
6.(X)7.(X)&(X)9.(V).10.(V)
1.52.31323.94.3505.50049910
6.n27.LRNFEGHDCB8.O(n)9.33
三、单项选择题
1.(C)2・(C)3.(C)4・(A)
四、1.解:
思路:
输岀叶子结点比较简单,用任何一种遍历递归算法,凡是左右指针均空者,则为叶子,将其打印出来。
法一:
核心部分为:
DLR(liuyu*root)严中序遍历递归函数*/
{if(root!
=NULL)
{if((root->lchild==NULL)&&(root->rchild==NULL)){sum++;printf(M%d\n,\root->data);}
DLR(root->lchild);
DLR(root->rchild);}
return(O);
}
法二:
intLcafCount_BiTree(BitreeT)〃求二叉树中叶子结点的数目
{
if(!
T)return0;//空树没有叶子
elseif(!
T->lchi!
d&&!
T->rchild)return1;〃叶子结点
elsereturnLeaf_Count(T->lchild)+Lcaf_Count(T->rchiId)y/左子树的叶子数加
上右子树的叶子数
)//LeafCount_BiTree
①打印叶子结点值(并求总数)
思路:
先建树,再从遍历过程中打印结点值并统计。
步骤1键盘输入序列12,8,17,11,16,2,13,9,21,4,构成一棵二叉排序树。
叶子结点值应该是4,9,13,21,总数应该是4.
111621
//
4913
编程:
生成二叉树排序树之后,再中序遍历排序查找结点的完整程序如下:
说明部分为:
#includc
#includc
typcdefstnictliuyufintdata;stnictliuyu*lchild,*rchild;}test;
liuyu*root;
intsum=O;intm=sizeof(test);
voidinsert_data(intx)/*如何生成二叉排序树?
参见教材P43C程序*/
{liuyu*p,*q,*s;
s=(test*)malloc(m);
s->data=x;
s->lchild=NULL;
s->rchild=NULL;
if(!
root){root=s;return;}
p=root;
while(p)/*如何接入二叉排序树的适当位巻*/
{q=p;
if(p->data=x){printf("dataalreadyexist!
\nH);return;}
elseif(xdata)p=p->lchild;elsep=p->rchild;
}
if(xdata)q->lchild=s;
elseq->rchild=s;
}
DLR(liuyu*root)/*中序遍历递归函数*/
{if(root!
=NULL)
{if((root->lchild==NULL)&&(root->rchild==NULL)){sum++;printf(H%d\n'\root->data);}
DLR(root->lchild);
DLR(root->rchild);}
return(O);
}
mainO/*先生成二叉排序树,再调用中序遍历递归函数进行排序输出*/
{inti.x;
i=l;
root=NULL;/*千万别忘了赋初值给root!
*/
do{printf(HpIeasei叩utdata%d:
uj);
i++;
scanf(規d”,&x);八从键盘采集数据,以4999表示输入结朿引
if(x==-9999){
DLR(root);
printf(M\nNowoutputcountvalue:
%d\n,\sum);
return(O);}
elseinsert_data(x);}/*调用插入数据元素的函数*/
while(x!
=-9999);
return(O):
}
2.答:
设计思路:
只査后继链表指针,若左或右孩子的左或右指针非空,则层次数加1:
否则函数返回。
但注意,递归时应当从叶子开始向上计数,否则不易确左层数。
intdepth(liuyu*root)/*统计层数*/
{intd.p;戶注意每一层的局部变量d.p都是各自独立的*/
p=o:
if(root=NULL)return(p);/*找到叶子之后才开始统讣
else{
d=dcpth(root->lchild);
if(d>p)p=d;/*向上回朔时,要挑出左右子树中的相对大的那个深度值*/
d=depth(root->rchild);
if(d>p)p=d;
}
p=p+l;
rcturn(p);
}
法二:
intGet_Sub_Dcpth(BitreeT.intx)〃求二叉树中以值为x的结点为根的子树深度
{
if(T->data=x)
{
printf(H%d\ir\GeLDepth(T));//找到了值为x的结点,求其深度
exit1;
else
{
if(T->lchild)Get_Sub_Depth(T->lchild?
x);
if(T->rchild)Get_Sub_Depth(T->rchild.x);〃在左右子树中继续寻找
}
)//Get_Sub_Depth
intGet_Depth(BitreeT)〃求子树深度的递归算法
if(!
T)return0;
else
{
m=Get_Depth(T->lchild);
n=Get_Depth(T->rchild);
return(m>n?
m:
n)+l;
}
)//Get_Depth
步骤2:
执行求深度的函数,并打印统汁岀来的深度值。
完整程序如下:
#includc
#includc
typcdefstnictliuyu{intdata;stnictliuyu*lchild,*rchild;}test;
liuyu*root;
intsum=0;intm=sizeof(test);
voidinsert_data(intx)/*如何生成二叉排序树?
参见教材P43C程序*/
{liuyu*p,*q,*s;
s=(test*)malloc(m);
s->data=x;
s->lchild=NULL;
s->rchild=NULL;
if(!
root){root=s;return;}
p=root;
while(p)/*如何接入二叉排序树的适当位巻*/
{q=p;
if(p->data=x){printf("dataalreadyexist!
\nH);return;}
elseif(xdata)p=p->lchild;elsep=p->rchild;
}
if(xdata)q->lchild=s;
elseq->rchild=s;
}
intdepth(liuyu*root)/*统计层数*/
{intd.p;戶注意每一层的局部变量d.p都是各自独立的*/
p=0;
if(root=NULL)return(p);/*找到叶子之后才开始统讣*/
else{
d=depth(root->lchild);
if(d>p)p=d;/*向上回朔时,要挑岀左右子树中的相对大的那个深度值*/
d=dcpth(root->rchild);
if(d>p)p=d;
)
p=p+l;
return(p);
)
voidmain()八先生成二叉排序树,再调用深度遍历递归函数进行统计并输岀
*/
{inti,x;
i=l;
root=NULL;/*千万别忘了赋初值给root!
*/
do{printf(npleasei叩utdata%d:
Hj);
i++;
scanf(H%d\&x);戶从键盘采集数据,以・9999表示输入结束旬
if(x==-9999){
printf(M\iiNowoutputdepthvalue=%d\n,\depth(root));return:
}elseinsert_data(x);}/*调用插入数据元素的函数*/
while(x!
=-9999);
return;}
3.解:
思路:
既然要求从上到下,从左到右,则利用队列存放各子树结点的指针是个好办法。
这是一个循环算法,用while语句不断循环,直到队空之后自然退岀该函数。
技巧之处:
当根结点入队后,会自然使得左、右孩子结点入队,而左孩子出队时又会立即使得它的左右孩子结点入队,……以此产生了按层次输出的效果。
level(liuyu*T)
/*liuyu*T,*p,*q[100];假设max已知*/
{intfj;
f=0;r=0;/*置空队引
r=(r+l)%max;
q[r]=T;/*根结点进队*/
while(f!
=r)/*队列不空*/
{f=(f+l%max);
p=q[f];/*出队引
primf(”%d”p>data);八打印根结点牝/
if(p->lchild){r=(r+1)%max:
q[r]=p->lchild:
}/*若左子树不空,则左子树进队*/
if(p->rchild){r=(r+1)%max;q[r]=p->rchild;}/*若右子树不空,则右子树进队*/
}return(O);
}
法二:
voidLayerOrder(BitreeT)〃层序遍历二叉树
{
InitQueue(Q);//建立工作队列
EnQueue(Q,T);
while(!
QueueEnipty(Q))
{
DeQucue(Q,p);
visit(p);
if(p->lchild)EnQueue(Q.p->lchild);
if(p->rchild)EnQueue(Q,p->rchild);
}
}//LayerOrder
可以用前而的函数建树,然后调用这个函数来输出。
完整程序如下(已上机通过)
#includc
#includc
#dcfinemax50
typcdefstnictliuyufintdata;stnictliuyu*lchild,*rchild;}test;
liuyu*root,*p,*q[max];
intsum=0;intm=sizeof(test);
voidinsert_data(intx)/*如何生成二叉排序树?
参见教材P43C程序*/
{liuyu*p,*q,*s;
s=(test*)malloc(m);
s->data=x;s->lchild=NULL;s->rchild=NULL;
if(!
root){root=s;return;}
p=root;
while(p)/*如何接入二叉排序树的适当位巻*/
{q=p;
if(p->data=x){printf("dataalreadyexist!
\nH);return;}
elseif(xdata)p=p->lchild;elsep=p->rchild;
}
if(xdata)q->lchild=s;
elseq->rchild=s;
}
level(liuyu*T)
/*liuyu*T,*p,*q[100];假设max已知*/
{intf,r;
f=0;r=0;/*置空队引
r=(r+l)%max:
q[r]=T;/*根结点进队*/
while(f!
=r)/*队列不空*/
{f=(f+l%max);
p=q[f];/*出队勺
primf(”%d”p>data);八打印根结点*/
if(p->lchild){r=(r+1)%max;q[r]=p->lchild:
}/*若左子树不空,则左子树进队*/
if(p->rchild){r=(r+1)%max;q[r]=p->rchild:
}/*若右子树不空,则右子树进队*/
)
return(O);
}
voidmain()严先生成二叉排序树,再调用深度遍历递归函数进行统计并输出
*/
{inti,x;
i=l;
root=NULL;/*千万别忘了赋初值给root!
*/
do{printf(npleasei叩utdata%d:
\i);
i++;
scanf(H%d\&x);戶从键盘采集数据,以・9999表示输入结束旬
if(x==-9999){
printf(M\iiNowoutputdatavalue:
\nM,level(root));return;}
elseinsert_data(x);}/*调用插入数据元素的函数*/
while(x!
=-9999);
return;}
4.答:
首先,由于是完全二叉树,不必担心中途会出现孩子为null的情况。
其次分析:
结点i的左孩子为2i,右孩子为2i+l;直接打印即可。
Printf(fcfcLeft_cliild=\%d,v[2*i].data;"Right_child=二%d,v[2*卄1].data;);
但苴双亲是i/2,需先判断i为奇数还是偶数。
若i为奇数,则应当先i-,然后再除以
2。
If(i/2!
=0)i-;
PrmtfC€Parents=\%d,v[i/2].data;);
5.答:
imIsFull_Bitrcc(BitrccT)〃判断二叉树是否完全二叉树,是则返回1,否则返回0
{
InitQueue(Q);
flag=O;
EnQueue(Q,T);〃建立工作队列
while(!
QueueEmpty(Q))
{
{
DeQueue(Q.p);
if(!
p)flag=l;
elseif(flag)return0;
else
{
EnQueue(Q.p->lchild);
EnQucuc(Q,p->rchild);〃不管孩子是否为空,都入队列
}
}//while
return1;
}//IsFulLBitree
分析:
该问题可以通过层序遍历的方法来解决.与6.47相比,作了一个修改,不管当前结点是否有左右孩子,都入队列•这样当树为完全二叉树时,遍历时得到是一个连续的不包含空指针的序列•反之,则序列中会含有空指针.
6、解:
方案1:
哈夫曼编码先将槪率放大100倍,以方便构造哈夫曼树。
w二{7,1926,32,3,21,10},按哈夫曼规则:
【[(2,3〉,
方案比较:
方案1的WPL=2(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的WPL=3(0.19+0.32+0.21+0.07+0.06+0.10+0.02+0.03)=3
结论:
哈夫曼编码优于等长二进制编码