(1)理解定义:本章介绍的一般树与二叉树均为树结构,树结构是最重要的非线性结构。 注意树结构与线性结构的差别:在线性结构中前驱结点是惟一的,而树结构中结点的前驱只 有一个(除根结点无前驱外),后继个数可有 m(m≥0)个,其中二叉树是后继个数最多为 2 的树。
(2)存储结构:
①二叉树采用顺序存储与二叉链表存储,其孩子个数最大为 2,故采用二叉树的二叉链 表表示法实现存储。
②对一般树,由于结点的后继个数变化范围较大,常采用树的二叉链表表示法实现存储 (即孩子兄弟法);
③树与二叉树之间的转换方法,最简单的是通过树与二叉树各自的二叉链表存储方法实 现转换,只不过是解释不同而已。
(3)掌握二叉树遍历算法是本章的重点,原因有二:其一,通过遍历得到了二叉树中结点 访问的线性序列,实现了非线性结构的线性化,遍历运算是基础。其二,二叉树遍历运算中 的递归实现是程序设计中重要技术,理解递归含义、使用递归控制条件都非常重要。
(4)明确递归到非递归的转换需求原因:因为递归执行时需要系统提供隐式栈实现递归, 效率较低;适应无应用递归语句的语言设施环境条件;由于递归算法是一次执行完,这在处 理有些问题时不适合。
(5).注意理解遍历应用,一要注意访问操作的可以包罗对访问结点的所有操作,并不 只有打印等操作(如线索化题例);二要注意分析应用问题对遍历顺序的要求(如树状打印 题例)。
(6)掌握哈夫曼树的概念,应用哈夫曼树构造哈夫曼编码,为解决数据压缩问题提供方法。
(7)理解并查集。掌握并查集中的查找、合并等运算。并能运用并查集进行等价类划分。
试设计算法,判断两棵二叉树是否相似。所谓二叉树 t1 与 t2 相似,指的是 t1 和 t2 都 是空的二叉树;或者 t1 的左子树与 t2的左子树相似,同时 t1 的右子树与 t2 的右子树相似。
【问题分析】依题意,本题的递归函数如下:
f(t1, t2)=TRUE 若 t1=t2=NULL
f(t1, t2)=FALSE 若 t1 与 t2 之一为 NULL,另一个不为 NULL
f(t1, t2)= f(t1->left, t2->left) && f(t1->right, t2->right) 若 t1 与 t2 均不为 NULL
【算法描述】 判断二叉树 b1 和 b2 是否相似算法
int like (BiTree b1, BiTree b2) /*判断二叉树 b1 和 b2 是否相似,若相似,返回 1,否则返回 0*/
{
int like1, like2;
if (b1==NULL && b2==NULL)
return(1); /*b1 和 b2 均为空树,相似,返回 1*/
else if (b1==NULL || b2==NULL)
return(0); /*b1 和 b2 其中之一为空树,不相似,返回 0*/
else
{
like1=like (t1->left, t2->left); /*判断 b1 和 b2 的左子树是否相似*/
like2=like (t1->right, t2->right); /*判断 b1 和 b2 的右子树是否相似*/
return (like1 && like2);
}
}
假设二叉树采用二叉链表方式存储,root 指向根结点,r 所指结点为任一给定的结点。 编写算法,求出从根结点到结点 r 之间的路径。
【问题分析】
按题意要求,本题要求出从根结点到结点 r 之间的路径。①由于后序遍历访问 到 r 所指结点时,此时栈中所有结点均为 r 所指结点的祖先,由这些祖先便构成了一条从根 结点到 r 所指结点之间的路径,故应采用后序遍历方法。 ②由于递归方式的栈区是由系统 隐式控制,为了能在找到结点 r 时控制输出,需要将递归方式中系统隐含的栈变成为用户自 己控制的栈,故应使用非递归的后序遍历算法。此算法与后序遍历非递归算法 6.12 类似, 不同之处在下述算法中用方框标明。
【算法描述】 求出从二叉树中根结点到 r 所指结点之间的路径并输出算法
void path (BiTree root, BiTNode *r) /*求出从二叉树根结点到 r 结点之间的路径并输出*/
{
BiTNode * p, *q;
int i, top=0;
BiTree s[Stack_Size];
q = NULL; /* 用 q 保存刚遍历过的结点,初始为空*/
p = root;
while (p != NULL || top != 0)
{
while ( p != NULL )
{
top ++;
if(top>=Stack_Size)
OverFlow(); /*栈溢出处理*/
s [ top ] = p; p = p -> LChild;
} /* 遍历左子树 */
if ( top > 0 )
{
p=s[top]; /* 根结点 */
if ( p -> RChild == NULL || p->RChild == q )
{
if(p= =r) /*找到 r 所指结点,则显示从根结点到 r 所指结点之间的路径 */
{
for(i=1;i<=top;i++)
printf(“%d”,s[i ]->data);
return;
}
else
{
q = p; /* 用 q 保存刚遍历过的结点 */
top--;
p = NULL; /* 跳过前面左遍历,继续退栈 */
}
}
else
p = p -> RChild; /* 遍历右子树 */
}
}
}
层次遍历是指从二叉树的第一层(根结点)开始,从上至下逐层遍历,在同一层中, 则按照从左到右的顺序对结点逐个访问。以此类推,直到二叉树中所有结点均被访问且仅访问一次。对于图 6.11 所示的二叉树,按照层次遍历得到的结点序列为:A B C D E F G H
【问题分析】实现层次遍历,需要设置一个队列 Q,暂存某层已访问过得结点,同时也保存了该层结点访问的先后次序。按照对该层结点访问的先后次序实现对其下层孩子结点的按次序访问。
【算法思想】
(1)初始化空队列 Q;
(2)若二叉树 bt 为空树,则直接返回;
(3)将二叉树的根结点指针 bt 放入队列 Q;
(4)若队列非空,则重复如下操作:
【算法描述】 层次遍历二叉树算法
int LayerOrder(BiTree bt) /*层次遍历二叉树 bt*/
{
SeqQueue * Q;
BiTree p;
InitQueue(Q); /*初始化空队列 Q*/
if(bt= =NULL)
return ERROR; /* 若二叉树 bt 为空树,则结束遍历*/
EnterQueue(Q, bt); /* 若二叉树 bt 非空,则根结点 bt 入队,开始层次遍历*/
while (!IsEmpty(Q)) /*若队列非空,则遍历为结束,继续进行*/
{
DeleteQueue(Q, &p); /*队头元素出队并访问 */
visit(p->data); if(p-> LChild )
EnterQueue(Q, p-> LChild) ; /*若 p 的左孩子非空,则进队*/
if(p-> RChild )
EnterQueue(Q, p-> RChild) ; /*若 p 的右孩子非空,则进队*/
} /*while*/
return OK;
} /* LayerOrder */