目录
一、建立二叉链表存储的二叉树
二叉树结点的结构:
typedef struct Node { TypeData data; struct Node*LChild; struct Node*RChild; }BiTreeNode,*BiTree;
采用扩展的先序遍历序列建立二叉链表
存储时为了区分空子树与非空子树,自定义个符号用于区分。
函数中的形参:指向结点的指针(*root),用于接收实参,(指向根结点的指针)root 的地址。
如果输入的值 A 非空,则为(*root)申请一个结点空间,用于存放输入的值,
按照先序遍历的顺序,该 A 结点的左子树 B 接收下一个输入的值,
如果左子树 B 为空(自定义值):跳转到上一层,开始遍历其右子树,
其左子树B非空时: 遍历左子树根结点的左子树 C (此时采用递归调用),
……
结点C调用结束后,会跳转到上一层B,继续遍历右子树D。
补充:
递归实现,有一个缺点是,
因为为了区分空树与非空树,我们采用自定义符号,这里采用 ^,
而在递归一层层调用时,需要注意右子树为^时会回溯到上一层,而如果输入的值与^数目不符合时,也就是不满足回到根结点的左子树时,造成根结点的右子树没有进行存储,此二叉树会继续让你输入值。
建立的二链表存储结束的条件是,根结点的右子树存储结束。
二、统计二叉树的结点数
结点数=根结点+左子树结点数+右子树结点
先序遍历,先遍历左子树,(采用递归的方法)直到左子树为0;然后遍历右子树,
最后加上根结点。
根结点a
遍历其左子树b(采用递归调用),
如果 b 的左子树为空,跳转到上一层,递归遍历右子树
如果右子树也为空 则 b 的结点数只有一个根结点,结点数为1,然后跳转到上一层,
然后递归遍历右子树 f 。
三、统计二叉树的叶子结点
int number0(BiTree root) { if(root==NULL) //判断树是否为空,空树是无结点,也可以是子树 return 0; if((root->LChild==NULL)&&(root->RChild==NULL)) //左右子树都为空时满足叶子结点,返回1 return 1; else return number0(root->LChild) + number0(root->RChild); //如果根结点有左子树或右子树,或同时有,先继续递归调用左子树,结束后返回到上一层,调用右子树 }
注意:叶子结点数=左子树的叶子结点数+右结点的叶子结点数,因为是递归调用,
观察图:
b 的左子树结点 C非空:
判断结点c的左右子树,左右子树都为空,返回 1,
然后判读b的右子树结点 d 非空:
判断b的右子树 d 的叶子结点只有 e返回1,此时b的左右子树调用完后,
返回到上一层b,此时b的叶子结点为(c+d)=2 ,此时a的左子树b判断完毕
继续调用a的右子树 f,
同理,f的叶子结点数为1,a的左右子树调用完毕,
返回到上一层 a,此时a的叶子结点数为(b+f)=3
如果a为空:则为空树,叶子结点为0
否则: 判断 a 的左子树 b和右子树 f 是否为空。
如果:左右子树都存在或只存在一个:先递归调用 左子树 b 继续判断,调用结束,然后调用其右子树 f。
否则:返回1,只有一个叶子结点 a。
调用左子树 b 时:如果 b 为空返回0,
否则:判段 b的左右子树
四、输出叶子结点
输出叶子结点时,需要遍历树,每一个结点需要判断是不是叶子结点
先序遍历应用:
void InOrder(BiTree root) { if (root) { if (root->LChild == NULL && root->RChild) //叶子结点的判断条件 printf("%c ", root->data); InOrder(root->LChild); //递归遍历左子树 InOrder(root->RChild); //递归遍历右子树 } }
五、求二叉树的高度
求二叉树的高度,需要遍历左右子树,判断左右子树哪颗树更高,则需要知道左右子树的深度的层次,定义一个 h 表示树的层次。
int Deepth(BiTree root) { int h1,h2; if(root==NULL) return 0; h1=Deepth(root->LChild); h2=Deepth(root->RChild); return (h1>h2?h1:h2)+1; //判读h1,h2的大小,输出大的一个高度再加上根结点 }
六、求结点的双亲
求一个结点A的的双亲,函数的参数就需要接收这个结点,便于查找双亲。
查找双亲结点B的方法:
递归判断左子树中是否存在一个结点B的左孩子或右孩子等于条件结点A,如果是,直接返回结点 B。否则,同理,进入右子树判断是否存在。
先从左子树中查找,查找结束判断,符合条件在从右子树查找。
BiTree relative(BiTree root,BiTree Node) { BiTree* p; //指向双亲结点的指针,方便判断是否遍历右子树 if(root==NULL) //如果树为空返回NULL return NULL; if((root->LChild==Node)||(root->RChild==Node)) //如果存在满足条件,返回双亲结点 retrun root; // 没有找到时,继续递归左子树。 p=relative(root->LChild); if(p!=NULL) return *p; //如果左子树查找到结点,返回双新结点,否则,递归遍历右子树 else return relative(root->RChild); }
七、二叉树相似性的判定
明确:二叉树相似是结构相似,不是二叉树的值都相等
二叉树相似的判定过程(两个二叉树为例)中出现的情况有三种:
1、两个二叉树都为空树
2、一个二叉树为空,另一个二叉树非空
3、两个二叉树都不为空,都不为空时,分别需要判断两树的左子树,和右子树结构是否相似。
如何判断结构是否相似呢?或者是如何知道两树的左子树或者右子树相似呢?
判断两左子树时需要,共同比较两左子数是否都为空,都为空时,结构相似,因为是一起递归遍历。
int similar(BiTree root1,BiTree root2) { int like1,like2; //用于判断左右子树是否相似 if(root1==NULL&&root2==NULL) //两个二叉树都为空 return 1; //相似返回1 if(root1==NULL||root2==NULL) return 0; like1=similar(root1->LChild,root2->LChild); //递归判断两树的左子树是否相似 like2=similar(root1->RChild,root2->RChild); return (like1&&like2); //根据两左子树和右子树的结果判断两树是否相似 }
八、按照树形形状打印二叉树
展示的效果是横向的二叉树,
按照右子树,然后根结点,然后左子树的方式打印二叉树。
如何确定结点的横向位置?
需要根据结点的层次h,然后在结点前打印h-1个空,用于展示位置的效果
//按照树的形状打印树 打印顺序是先右子树,根结点,左子树 void Print(BiTree root,int h) //通过结点的层数判断打印的位置 { if (root == NULL) return; Print(root->RChild, h + 1); //先打印右子树 for (int i = 1; i <= h; i++) //结点的位置,前面的空位数为上一层的层数 printf(" "); printf("%c\n", root->data); Print(root->LChild, h + 1); //打印左子树; }
九、算法实现综合应用
以此图为例:
算法实现:
#include<stdio.h>
#include<stdlib.h>
// 建立二叉树的结构
typedef struct Node
{
char data;
struct Node* LChild;
struct Node* RChild;
}BiTreeNode ,*BiTree;
//建二叉链表存储的二叉树
void Establish(BiTree*root)
{
char demo;
demo = getchar();
if (demo == '^')
*root = NULL;
else
{
*root = (BiTree)malloc(sizeof(BiTreeNode));
(*root)->data = demo;
Establish(&((*root)->LChild));
Establish(&((*root)->RChild));
}
}
// 先序遍历求二叉树的结点数
int Number(BiTree root)
{
if (root == NULL)
return 0;
else
return Number(root->LChild)+Number(root->RChild)+1;
}
// 先序遍历求二叉树的叶子结点数
int Number0(BiTree root)
{
if (root == NULL) //如果是空树返回0
return 0;
if ((root->LChild == NULL) && (root->RChild == NULL)) //该结点为空
return 1;
else //该结点不为空,继续调用函数遍历
return Number0(root->LChild) + Number0(root->RChild);
}
//先序遍历输出二叉树的叶子结点
void InOrder(BiTree root)
{
if (root)
{
if (root->LChild == NULL && root->RChild==NULL)
printf("%c ", root->data);
InOrder(root->LChild);
InOrder(root->RChild);
}
}
//先序遍历求二叉树的深度
int Deepth(BiTree root)
{
if (root == NULL)
return 0;
else
{
int h1 = Deepth(root->LChild);
int h2 = Deepth(root->RChild);
return (h1 > h2 ? h1 : h2 )+ 1;
}
}
//求结点的双亲
BiTree relative(BiTree root,BiTree TreeNode)
{
BiTree *p=NULL;
if (root == NULL)
return NULL;
if ((root->LChild == TreeNode) || (root->RChild == TreeNode))
return root; // 结点的左子树与TreeNode不等
*p = relative(root->LChild, TreeNode); // 继续遍历左子树,
if (p != NULL) // 左子树遍历结束没有找到,在右子树中查找
return *p;
else
return relative(root->RChild, TreeNode);
}
// 求结点的相似性 是判断两个树的结构是否相同,而不是相等
int simmilar(BiTree root, BiTree root1)
{
int like1, like2;
if (root == NULL && root1 == NULL) //两个都为空树,相似返回1
return 1;
if (root == NULL || root1 == NULL) // 其中一个为空树,返回0
return 0;
like1 = simmilar(root->LChild, root1->LChild); // 继续遍历左子树
like2 = simmilar(root->RChild, root1->RChild);
return (like1 && like2);
}
//按照树的形状打印树 打印顺序是先右子树,根结点,左子树
void Print(BiTree root,int h) //通过结点的层数判断打印的位置
{
if (root == NULL)
return;
Print(root->RChild, h + 1); //先打印右子树
for (int i = 1; i <= h; i++) //结点的位置,前面的空位数为上一层的层数
printf(" ");
printf("%c\n", root->data);
Print(root->LChild, h + 1); //打印左子树;
}
main()
{
BiTree root;
Establish(&root);
printf("-----------统计二叉树结点数----------\n");
printf("%d\n",Number(root));
printf("-----------统计二叉树叶子结点数----------\n");
printf("%d\n", Number0(root));
printf("------------输出叶子结点--------------\n");
InOrder(root);
printf("\n");
printf("-------------统计二叉树的高度--------------\n");
int h=Deepth(root);
printf("%d\n", h);
printf("-----------打印二叉树------------\n");
Print(root, h);
}
运行结果: