1、习题六 树和二叉树一、单项选择题1 以下说法错误的是 ( A )A树形结构的特点是一个结点可以有多个直接前趋B线性结构中的一个结点至多只有一个直接后继C树形结构可以表达(组织)更复杂的数据D树(及一切树形结构)是一种“分支层次“结构E任何只含一个结点的集合是一棵树2下列说法中正确的是 ( D )A任何一棵二叉树中至少有一个结点的度为 2B任何一棵二叉树中每个结点的度都为 2C任何一棵二叉树中的度肯定等于 2D任何一棵二叉树中的度可以小于 23讨论树、森林和二叉树的关系,目的是为了( A )A借助二叉树上的运算方法去实现对树的一些运算B将树、森林按二叉树的存储方式进行存储C将树、森林转换成二叉树
2、D体现一种技巧,没有什么实际意义4树最适合用来表示 ( C )A有序数据元素 B无序数据元素C元素之间具有分支层次关系的数据 D元素之间无联系的数据5若一棵二叉树具有 10 个度为 2 的结点,5 个度为 1 的结点,则度为 0 的结点个数是( B )A9 B11 C15 D不确定 6设森林 F 中有三棵树,第一,第二,第三棵树的结点个数分别为 M1,M2 和 M3。与森林F 对应的二叉树根结点的右子树上的结点个数是( D ) 。AM1 BM1+M2 CM3 DM2+M37一棵完全二叉树上有 1001 个结点,其中叶子结点的个数是( E )A 250 B 500 C254 D505 E以上答案
3、都不对 8二叉树的第 I 层上最多含有结点数为( C )A2 I B 2 I-1-1 C 2 I-1 D2 I -110一棵二叉树高度为 h,所有结点的度或为 0,或为 2,则这棵二叉树最少有( B )结点A2h B2h-1 C2h+1 Dh+1 11. 利用二叉链表存储树,则根结点的右指针是( B ) 。A指向最左孩子 B指向最右孩子 C空 D非空12已知一棵二叉树的前序遍历结果为 ABCDEF,中序遍历结果为 CBAEDF,则后序遍历的结果为( A ) 。ACBEFDA B FEDCBA C CBEDFA D不定 13已知某二叉树的后序遍历序列是 dabec, 中序遍历序列是 debac
4、, 它的前序遍历是( D ) 。Aacbed Bdecab Cdeabc Dcedba 14在二叉树结点的先序序列,中序序列和后序序列中,所有叶子结点的先后顺序( B )A都不相同 B完全相同 C先序和中序相同,而与后序不同 D中序和后序相同,而与先序不同 15在完全二叉树中,若一个结点是叶结点,则它没( C ) 。A左子结点 B右子结点 C左子结点和右子结点 D左子结点,右子结点和兄弟结点20由 3 个结点可以构造出多少种不同的二叉树?( D )A2 B3 C4 D5 22. 一棵有 n 个结点的二叉树,按层次从上到下,同一层从左到右顺序存储在一维数组A1n中,则二叉树中第 i 个结点(i
5、从 1 开始用上述方法编号)的右孩子在数组 A 中的位置是( D )AA2i(2ilchild=null 中根序列:B C D A F E H J I G;后根序列:D C B F J I H G E A。4(1)k h-1(h 为层数)(2)因为该树每层上均有 Kh-1个结点,从根开始编号为 1,则结点 i 的从右向左数第个孩子的结点编号为 ki。设 n 为结点 i 的子女,则关系式(i-1)k+21)的前一结点编号为 n-1(其最右边子女编号是(n-1)*k+1) ,故结点 n的第 i 个孩子的编号是(n-1)*k+1+i。(4) 根据以上分析,结点 n 有右兄弟的条件是,它不是双亲的从右
6、数的第一子女,即 (n-1)%k!=0,其右兄弟编号是 n+1。5.6 (l)图略;(2) 前序序列:J中序序列: E C B H F D J I G A后序序列: (3)图略。7字符 A,B,C,D 出现的次数为 9,1,5,3。其哈夫曼编码如下 A:1,B:000,C:01,D:001五、算法设计题1题目分析二叉树是递归定义的,以递归方式建立最简单。判定是否是完全二叉树,可以使用队列,在遍历中利用完全二叉树“若某结点无左子女就不应有右子女”的原则进行判断。BiTree Creat() /建立二叉树的二叉链表形式的存储结构ElemType x;BiTree bt;scanf(“%d”, /本
7、题假定结点数据域为整型if(x=0) bt=null;else if(x0)bt=(BiNode *)malloc(sizeof(BiNode);bt-data=x; bt-lchild=creat(); bt-rchild=creat(); HGDACJIBFEMPONKOL1 359000111else error(“输入错误”);return(bt);/结束 BiTreeint JudgeComplete(BiTree bt) /判断二叉树是否是完全二叉树,如是,返回 1,否则,返回 0int tag=0; BiTree p=bt, Q; / Q 是队列,元素是二叉树结点指针,容量足够大
8、if(p=null) return (1);QueueInit(Q); QueueIn(Q,p); /初始化队列,根结点指针入队while (!QueueEmpty(Q)p=QueueOut(Q); /出队if (p-lchild /左子女入队else if (p-lchild) return 0; /前边已有结点为空,本结点不空else tag=1; /首次出现结点为空if (p-rchild /右子女入队else if (p-rchild) return 0; else tag=1; /whilereturn 1; /JudgeComplete算法讨论完全二叉树证明还有其它方法。判断时易犯
9、的错误是证明其左子树和右子数都是完全二叉树,由此推出整棵二叉树必是完全二叉树的错误结论。2.题目分析后序遍历最后访问根结点,即在递归算法中,根是压在栈底的。采用后序非递归算法,栈中存放二叉树结点的指针,当访问到某结点时,栈中所有元素均为该结点的祖先。本题要找 p 和 q 的最近共同祖先结点 r ,不失一般性,设 p 在 q 的左边。后序遍历必然先遍历到结点 p,栈中元素均为 p 的祖先。将栈拷入另一辅助栈中。再继续遍历到结点 q 时,将栈中元素从栈顶开始逐个到辅助栈中去匹配,第一个匹配(即相等)的元素就是结点 p 和 q 的最近公共祖先。typedef structBiTree t;int t
10、ag;/tag=0 表示结点的左子女已被访问,tag=1 表示结点的右子女已被访问stack;stack s,s1;/栈,容量够大BiTree Ancestor(BiTree ROOT,p,q,r)/求二叉树上结点 p 和 q 的最近的共同祖先结点r。top=0; bt=ROOT; while(bt!=null |top0)while(bt!=null stop.tag=0; bt=bt-lchild; /沿左分枝向下if(bt=p) /不失一般性,假定 p 在 q 的左侧,遇结点 p 时,栈中元素均为 p 的祖先结点for(i=1;i0;i-)/;将栈中元素的树结点到 s1 去匹配pp=si
11、.t;for (j=top1;j0;j-)if(s1j.t=pp) printf(“p 和 q 的最近共同的祖先已找到”);return (pp);while(top!=0 /退栈if (top!=0)stop.tag=1;bt=stop.t-rchild; /沿右分枝向下遍历/结束 while(bt!=null |top0)return(null);/、p 无公共祖先/结束 Ancestor3解答:本算法要借用队列来完成,其基本思想是,只要队列不为空,就出队列,然后判断该结点是否有左孩子和右孩子,如有就依次输出左、右孩子的值,然后让左、右孩子进队列。void layorder (bitrep
12、tr T)initqueue (q) /*队列初始化*/if(T!=NULL)printf(“%f”, T-data);enqueue (q, T); /*入队列*/while (not emptyqueue (q) ) /*若队列非空*/outqueue (q, p) ; /*出队*/if (p-lchild!=NULL)printf(“%f”, p-lchild-data);enqueue (q, p-lchild); /*入队列*/if (p-rchild!=NULL)printf(“%”, p-rchild-data);enqueue (q, p-rchild); /*入队列*/4 【
13、解答】Void PreOrder(BiTree root) /*先序遍历二叉树的非递归算法*/InitStack(p=root;while(p!=NULL | !IsEmpty(S) ) if(p!=NULL)Visit(p-data);push(p=p-Lchild;elsePop( p=p-RChild;5 void InOrder(BiTree bt)BiTree s,p=bt; /s 是元素为二叉树结点指针的栈,容量足够大int top=0;while(p | top0)while(p) s+top=p; bt=p-lchild; /中序遍历左子树if(top0)p=stop-; pr
14、intf(p-data); p=p-rchild; /退栈,访问,转右子树 6BiTree Copy(BiTree t)/复制二叉树 tBiTree bt;if (t=null) bt=null;elsebt=(BiTree)malloc(sizeof(BiNode); bt-data=t-data;bt-lchild=Copy(t-lchild);bt-rchild=Copy(t-rchild); return(bt); /结束 Copy7.题目分析叶子结点只有在遍历中才能知道,这里使用中序递归遍历。设置前驱结点指针 pre,初始为空。第一个叶子结点由指针 head 指向,遍历到叶子结点时,
15、就将它前驱的rchild 指针指向它,最后叶子结点的 rchild 为空。LinkedList head,pre=null; /全局变量LinkedList InOrder(BiTree bt)/中序遍历二叉树 bt,将叶子结点从左到右链成一个单链表,表头指针为 headif(bt)InOrder(bt-lchild); /中序遍历左子树if(bt-lchild=null pre=bt; /处理第一个叶子结点elsepre-rchild=bt; pre=bt; /将叶子结点链入链表InOrder(bt-rchild); /中序遍历左子树pre-rchild=null; /设置链表尾return
16、(head); /InOrder时间复杂度为 O(n),辅助变量使用 head 和 pre,栈空间复杂度 O(n)8.题目分析 删除以元素值 x 为根的子树,只要能删除其左右子树,就可以释放值为 x 的根结点,因此宜采用后序遍历。删除值为 x 结点,意味着应将其父结点的左(右)子女指针置空,用层次遍历易于找到某结点的父结点。本题要求删除树中每一个元素值为 x 的结点的子树,因此要遍历完整棵二叉树。void DeleteXTree(BiTree bt) /删除以 bt 为根的子树DeleteXTree(bt-lchild); DeleteXTree(bt-rchild);/删除 bt 的左子树、
17、右子树free(bt); / DeleteXTree /释放被删结点所占的存储空间void Search(B:Tree bt,ElemType x)/在二叉树上查找所有以 x 为元素值的结点,并删除以其为根的子树BiTree Q;/Q 是存放二叉树结点指针的队列,容量足够大if(bt)if(bt-data=x) DeleteXTree(bt); exit(0);/若根结点的值为 x,则删除整棵树QueueInit(Q); QueueIn(Q,bt);while(!QueueEmpty(Q)p=QueueOut(Q);if(p-lchild) / 若左子女非空if(p-lchild-data=x
18、) /左子女结点值为 x,应删除当前结点的左子树DeleteXTree(p-lchild); p-lchild=null; /父结点的左子女置空else Enqueue (Q,p-lchild);/ 左子女入队列if(p-rchild) / 若右子女非空if(p-rchild-data=x) /右子女结点值为 x,应删除当前结点的右子树DeleteXTree(p-rchild); p-rchild=null; /父结点的右子女置空else Enqueue (Q,p-rchild);/ 右子女入队列/while/if(bt) /search9int BTLC(BiTree T,int *c)/对
19、二叉树 T 的结点计数if(T)*c+;BTLC(T-lchild, /统计左子树结点BTLC(T-rchild, /统计右子树结点 /结束 Count,调用时*c=010(1)找结点的中序前驱结点BiTNode *InPre (BiTNode *p)/*在中序线索二叉树中查找 p 的中序前驱结点,并用 pre 指针返回结果*/ if (p-Ltag= =1) pre = p-LChild; /*直接利用线索*/else/*在 p 的左子树中查找“最右下端”结点*/ for ( q=p-LChild; q-Rtag= =0; q=q-RChild);pre = q;return (pre);
20、(2)找结点的中序后继结点BiTNode *InSucc (BiTNode *p)/*在中序线索二叉树中查找 p 的中序后继结点,并用 succ 指针返回结果*/ if (p-Rtag= =1) succ = p-RChild; /*直接利用线索*/else/*在 p 的右子树中查找“最左下端”结点*/ for ( q=p-RChild; q-Ltag= =0; q=q-LChild);succ= q;return (succ); (3) 找结点的先序后继结点BiTNode *PreSucc (BiTNode *p)/*在先序线索二叉树中查找 p 的先序后继结点,并用 succ 指针返回结果*/ if (p-Ltag= =0) succ = p-LChild; else succ= p-RChild;return (succ); (4) 找结点的后序前驱结点BiTNode *SuccPre (BiTNode *p)/*在后序线索二叉树中查找 p 的后序前驱结点,并用 pre 指针返回结果*/ if (p-Ltag= =1) pre = p-LChild; else pre= p-RChild;return (pre);