目录
第六章 树与二叉树笔记
1、无左子树的二叉树前序序列与中序序列相同
作业:列出所有中序与后序相同的二叉树
答:无右子树的二叉树
2、输出二叉树中的叶子结点
思路:叶子节点无左孩子与右孩子,在遍历过程中判断即可。
void PreOrder(BiTree root)
{
if(root != NULL)
{
if(root->LChild == NULL && root->RChild == NULL){
printf("%c",root->data)
}
PreOrder(root->LChild);
PreOrder(root->RChild);
}
}
思考:输出非叶子结点。
答案:将判断条件改为左孩子或右孩子不为空。
3、统计叶子结点数目
方法一:设置计数器为全局变量,保存叶子结点数目,调用之前初值为0。
void leaf(BiTree root)
{
if(root != NULL)
{
leaf(root->LChild);
leaf(root->RChild);
if(root->LChild == NULL && root->RChild == NULL){
LeafCount++;
}
}
}
方法二:按递归函数方式求解计算。采用递归算法,如果为空树,返回0;如果结点数为1,返回1;否则为左右子树的叶子结点数之和。
int leaf(BiTree root)
{
int LeafCount;
//空树
if(root == NULL)LeafCount = 0;
//只有一个结点
else if(root->LChild == NULL && root->RChild == NULL)LeafCount = 1;
//其他情况
else
LeafCount = leaf(root->LChild)+leaf(root->RChild);
return LeafCount;
}
4、建立二叉链表方式存储的二叉树
给定一棵二叉树→得到它的扩展前序遍历序列;
给定前序遍历序列→创建相应二叉树。
/*利用"扩展先序遍历序列"创建二叉链表*/
void CreateBiTree(BiTree *bt)
{
char ch;
ch = getchar();
if(ch == '.')*bt = NULL;
else
{
*bt = (BiTree)malloc(sizeof(BiTNode));
(*bt)->data = ch;
CreateBiTree(&((*bt)->LChild));
CreateBiTree(&((*bt)->RChild));
}
}
5、按树状打印的二叉树
逆中序遍历实现横向树状打印
void PrintTree(TreeNode Boot,int nLayer)
{
if(Boot == NULL)return;
PrintTree(Boot->RChild,nLayer+1);
for(int i=0;i<nLayer;i++)printf(" ");
printf("%c\n",Boot->ch);
PrintTree(Boot->LChild,nLayer+1);
}
问题:把二叉树中的所有结点的左右子树交换。可采用何种遍历方式实现?
分析:使用前序、后序遍历均可实现;中序遍历无法实现。
原因:当遍历右子树时,原来的右子树已经换到左边,不可能再被交换,而现在的右子树已经是被处理过的左子树,不应该再被交换。
6、基于栈的递归消除
原因:递归的执行效率低(进入、保存、出来、恢复);运行环境没有递归机制。
递归转换的两种方法:①直接尾递归:可化为直线型,用循环进行替代。
②复杂方法:采用工作栈消除递归。
6.1利用工作栈消除递归
将递归中系统隐含的栈→用户自己操纵的栈(工作栈提供一种控制结构)
递归进层时,需保留信息;
进层三件事:①保存本层参数,返回地址②传递参数,分配局部数据空间③控制转移
递归退层时,需恢复信息。
退层三件事:①恢复上层②传递结果③转断点执行
中序遍历递归算法:
void Inorder(BiTree root)
{
if(root != NULL)
{
Inorder(root->LChild);
Visit(root->data);
Inorder(root->RChild);
}
}
中序遍历非递归算法:
void inorder(BiTree root)
{
int top=0;
p=root;
L1:if(p != NULL){ //遍历左子树
top = top+2;
if(top > m)... //溢出处理
s[top-1] = p; //本层参数进栈
s[top] = L2; //返回地址进栈
p = p->LChild; //给下层参数赋值
goto L1; //转向开始
L2:Visit(p->data); //访问根
top = top+2;
if(top < m)... //溢出处理
s[top-1] = p; //遍历右子树
s[top] = L3;
p = p->RChild;
goto L1;
}
L3:if(top != 0)
{
addr = s[top];
p = s[top-1]; //取出返回地址
top = top-2; //退出本层参数
goto addr; //恢复上层断点
}
}
7、线索二叉树
二叉树的遍历运算是将二叉树中结点按一定规律线性化的过程。不能直接得到结点在遍历序列中的前驱和后继信息。
思路:利用二叉链表中的空链域,将遍历过程中结点的前驱、后继信息保存下来。
问题:n个结点的二叉树中有多少个空链域?
结论:2n个链域;n-1个非空链域;n+1个空链域。
线索:在这种存储结构中,指向前驱和后继结点的指针叫做线索。
线索链表:以这种结构存储的二叉链表作为二叉树的存储结构,叫做线索链表。
线索化:对二叉树以某种次序进行遍历并且加上线索的过程叫做线索化。
线索二叉树:被线索化的二叉树。
中序线索化算法思路:
//中序线索化算法思路
void Inthread(BiTree root)
{
if(root != NULL)
{
Inthread(root->LChild)
if(root->LChild == NULL){
root->Ltag = 1;
root->LChild = pre;
}
if(pre != NULL && pre->RChild == NULL){
pre->RChild = root;
pre->Rtag = 1;
}
pre = root;
Inthread(root->RChild);
}
}
//在中序线索二叉树中查找p的中序前驱,并用pre指针返回结果
void InPre(BiTNode *p,BiTNode *pre)
{
if(p->Ltag == 1)pre = p->LChild;
else
{
for(q = p->LChild;q->Rtag == 0;q = q->RChild)
pre = q;
}
}
/*在中序线索二叉树中查找p的后继结点,并用succ指针返回结果*/
void InSucc(BiTNod *p,BiTNode *succ)
{
if(p->Rtag == 1)succ = p->RChild;
else{
for(q = p->RChild;q->Ltag == 0;q = q->LChild){
succ = q;
}
}
}
8、树、森林和二叉树的关系
8.1树的存储结构
①双亲表示法②孩子表示法③孩子兄弟表示法
双亲表示法:在保存每个结点的同时附设一个指示器来指示其双亲结点在表中的位置。
typedef struct TNode
{
DataType data;
int parent;
}TNode;
typedef struct
{
TNode terr[MAX];
int nodenum;//结点数
}parentTree;
孩子表示法:每个结点的孩子结点构成一个单链表。
孩子兄弟表示法(二叉链表表示法):链表中的每个结点设两个链域,分别指向该结点的第一个孩子结点和下一个兄弟结点。