实验五 二叉树建立及应用
一、实验目的
1.熟悉二叉树的存贮结构及遍历方式,掌握有关算法的实现。
2.能够利用二叉树解决具体问题。
二、实验环境
1.硬件:每个学生需配备计算机一台。
2.软件:Windows操作系统+Visual C++。
三、实验要求
⒈ 要求采用二叉链表作为存贮结构,完成二叉树的建立、先序、中序、和后序遍历的操作。其中先序遍历和后序遍历采用递归算法,中序遍历采用非递归算法。
⒉ 输入数据:树中每个结点的数据类型设定为字符型。
3。设计一棵二叉树,输入完全二叉树的先序序列,用#代表虚结点(空指针),如ABD###CE##F##,建立二叉树,求先序、中序和后序遍历,求该二叉树所有叶子结点总数。
四、实验内容
附:参考程序为类C语言程序,非标准C语言程序,需要进行相应的修改。
二叉链表结构如下:
typedef struct lnode
{char data;
struct lnode *lchild,*rchild;
}lnode,*tree;
1.建树子函数
status creat(tree &t)
{//按先序次序输入二叉树中结点的值,’.’字符表示空树
scanf(&ch);
if(ch=='.')
t=null;
else
{t=(tree)malloc(sizeof(lnode));
t->data=ch;
creat(t->lchild);
creat(t->rchild);}
return ok;
}
2.先序遍历子函数
preorder(tree t)
{
if(t!=null)
{
printf(t->data);
preorder(t->lchild);
preorder(t->rchild);
}
}
3.中序遍历子函数
inorder(tree t)
{//s是一个栈,初始化一个空栈
p=t;
initstack(s);
while(p||s.top!=s.base)
{
while(p)
{push(p);
p=p->lchild;}
if(s.top!=s.base)
{pop(s,p);
printf(p->data);
p=p->rchild;
}
}
}
4.后序遍历子函数
postorder(tree t)
{if(t!=null)
{postorder(t->lchild);
postorder(t->rchild);
printf(t->data);
}
}
程序实现:
#include<stdio.h>
#include<stdlib.h>
#define MaxSize 50
typedef char ElemType;
typedef struct node{
ElemType data;
struct node *lchild;
struct node *rchild;
}BTNode;
//创建二叉树
void CreateBTree(BTNode *&b,char *str){
BTNode *ST[MaxSize],*p;
int top=-1,k,j=0;
char ch;
ch=str[j];
b=NULL;
while(ch!='\0'){
switch(ch){
case '(': top++;ST[top]=p;k=1;break;
case ')': top--;break;
case ',': k=2;break;
default: p=(BTNode *)malloc(sizeof(BTNode));
p->data=ch;
p->lchild=p->rchild=NULL;
if(b==NULL) b=p;
else{
switch(k){
case 1:ST[top]->lchild=p;break;
case 2:ST[top]->rchild=p;break;
}
}
}
j++;
ch=str[j];
}
}
//先序遍历二叉树
void PreOrder(BTNode *b){
if(b!=NULL){
printf("%c ",b->data);
PreOrder(b->lchild);
PreOrder(b->rchild);
}
}
//中序遍历二叉树
void InOrder(BTNode *b){
if(b!=NULL){
InOrder(b->lchild);
printf("%c ",b->data);
InOrder(b->rchild);
}
}
//后序遍历二叉树
void PsotOrder(BTNode *b){
if(b!=NULL){
PsotOrder(b->lchild);
PsotOrder(b->rchild);
printf("%c ",b->data);
}
}
//求二叉树的高度
int BTHeight(BTNode *b){
int lchildh,rchildh;
if(b==NULL)return(0);
else{
lchildh=BTHeight(b->lchild);
rchildh=BTHeight(b->rchild);
return(lchildh>rchildh)?(lchildh+1):(rchildh+1);
}
}
//求二叉树叶子节点的数量
int LeafNodes(BTNode *b){
int num1,num2;
if(b==NULL)
return 0;
else if(b->lchild==NULL&&b->rchild==NULL)
return 1;
else{
num1=LeafNodes(b->lchild);
num2=LeafNodes(b->rchild);
return(num1+num2);
}
}
//按层次遍历二叉树
void TravLevel(BTNode *b){
BTNode *Qu[MaxSize];
int front,rear;
front =rear=0;
if(b!=NULL)printf("%c",b->data);
rear++;
Qu[rear]=b;
while(rear!=front){
front=(front+1)%MaxSize;
b=Qu[front];
if(b->lchild!=NULL){
printf("%c",b->lchild->data);
rear=(rear+1)%MaxSize;
Qu[rear]=b->lchild;
}
if(b->rchild!=NULL){
printf("%c",b->rchild->data);
rear=(rear+1)%MaxSize;
Qu[rear]=b->rchild;
}
}
printf("\n");
}
//主程序
void main(){
char *str="A(B(E,F),D(H,I(K,M)))";
BTNode *b=NULL;
CreateBTree(b,str);
printf("先序遍历二叉树的结果为:");
PreOrder(b);
printf("\n");
printf("中序遍历二叉树的结果为:");
InOrder(b);
printf("\n");
printf("后序遍历二叉树的结果为:");
PsotOrder(b);
printf("\n");
printf("二叉树的叶子节点个数:%d,\n",LeafNodes(b));
printf("二叉树的高度为:%d\n",BTHeight(b));
printf("层次遍历序列:");
TravLevel(b);
printf("\n");
}
运行结果:
测试结果:
通过!
五、思考题
1. 已知二叉树先序和中序序列,唯一地构造一棵二叉树并且验证其正确性。
实现程序:
#include<string>
#include<cstring>
using namespace std;
const int maxint = 10000;
char ch1[maxint], ch2[maxint]; //前序序列,中序序列
int length; //二叉树结点的个数
struct tree {
char name;
struct tree *leftchild;
struct tree *rightchild;
};
//访问函数
void vis(char name) {
printf("%s",name);
}
//初始化
void init(struct tree **root){
*root = (struct tree *)malloc(sizeof(struct tree));
(*root)->leftchild = NULL;
(*root)->rightchild = NULL;
}
//创建左子树
struct tree *build_ltree(struct tree *h,char name) {
struct tree *p, *t;
if (h == NULL) return NULL;
t = h->leftchild;
p= (struct tree*)malloc(sizeof(struct tree));
p->name = name;
p->leftchild = t;
p->rightchild = NULL;
h->leftchild = p;
return h->leftchild;
}
//创建右子树
struct tree *build_rtree(struct tree *h, char name) {
struct tree *p, *t;
if (h == NULL) return NULL;
t = h->rightchild;
p = (struct tree*)malloc(sizeof(struct tree));
p->name = name;
p->leftchild = NULL;
p->rightchild = t;
h->rightchild = p;
return h->rightchild;
}
//凹入法打印二叉树
void print_tree(struct tree *t, int n) {
if (t == NULL) return;
print_tree(t->rightchild, n + 1);
for (int i = 0; i < n - 1; i++)
printf(" ");
if (n > 0) {
printf("***");
printf("%s\n",t->name);
}
print_tree(t->leftchild, n + 1);
}
//前序遍历
void preorder(struct tree *t, void vis(char name)) {
if (t != NULL) {
vis(t->name);
preorder(t->leftchild, vis);
preorder(t->rightchild, vis);
}
}
//中序遍历
void inorder(struct tree *t, void vis(char name)) {
if (t != NULL) {
inorder(t->leftchild, vis);
vis(t->name);
inorder(t->rightchild, vis);
}
}
//后序遍历
void postorder(struct tree *t, void vis(char name)) {
if (t != NULL) {
postorder(t->leftchild, vis);
postorder(t->rightchild, vis);
vis(t->name);
}
}
//寻找对应中序序列中和前序序列相对应的结点的位置
int bfs(char ch[],char name) {
int i(0);
while (ch[i] != name) ++i;
return i;
}
//找到左子树的位置
int seek_left(int flag[], int t){
int temp;
temp = t;
while (flag[temp] != 1 && temp >= 0)
temp--;
if (flag[temp] == 1)
return temp;
else return -1;
}
//找到右子树的位置
int seek_right(int flag[], int t)
{
int temp;
temp = t;
while (flag[temp] != 1 && temp <= 10000)
temp++;
if (flag[temp] == 1)
return temp;
else return -1;
}
//主函数
void main() {
struct tree *root; //定义根节点
init(&root); //创建根节点
struct tree *node_tree[maxint]; //二叉树中的结点
int flag[maxint]; //标记数组
int left, right;
memset(flag, 0, sizeof flag); //标记数组全部赋值为0
printf("请输入前序序列");
char ch1;
ch1=getchar();
printf("请输入中序序列:");
char ch2;
ch2=getchar();
length = sizeof(ch1);
char node; //前序序列中的结点
int num; //中序序列中对应前序序列结点的位置
for (int i = 0; i < length; ++i) {
node = ch1[i];
num = bfs(ch2, node);
left = seek_left(flag, num); //找到左子树位置
right = seek_right(flag, num); //找到右子树位置
if (left == -1 && right == -1) { //第一次的时候肯定会执行这个条件后面的语句
node_tree[num] = build_ltree(root, node);
flag[num] = 1;
}
else if (left != -1 && node_tree[left]->rightchild == NULL) {
node_tree[num] = build_rtree(node_tree[left], node);
flag[num] = 1;
}
else if (right != -1 && node_tree[right]->leftchild == NULL) {
node_tree[num] = build_ltree(node_tree[right], node);
flag[num] = 1;
}
}
printf("此二叉树的结构是:\n");
print_tree(root, 0);
printf("此二叉树的前序序列为:");
preorder(root->leftchild, vis);
printf("\n");
printf("此二叉树的中序序列为:");
inorder(root->leftchild, vis);
printf("\n");
printf("此二叉树的后序序列为:");
postorder(root->leftchild, vis);
printf("\n");
}
运行结果:
测试结果:
通过!
2.对该二叉树进行中序线索化的实现。
实现程序:
#include <stdio.h>
#include <stdlib.h>
typedef enum Tag{
Ptr,Thread
}PointerTag;
//Ptr=0:指针,Thread=1:线索
//二叉链表的结点结构
typedef struct ThreadTreeNode{
char data; //数据域
struct ThreadTreeNode* lchild; //左孩子指针域
struct ThreadTreeNode* rchild; //右孩子指针域
PointerTag LTag; //左标志位 0 1
PointerTag RTag; //右标志位 0 1
}Tnode,*Ttree;
Ttree pre; //指向刚刚访问过的结点
void createBiTree(Ttree& T);
void inOrderThreadTree(Ttree& Thrt,Ttree T);
void inThreading(Ttree p);
void inOrderTraverse(Ttree Thrt);
void createBiTree(Ttree& T)
{
char ch;
ch=getchar();
if(ch=='#') T=NULL;
else
{
if(!(T=(Tnode*)malloc(sizeof(Tnode)))) exit(0);
//这里注意正确输入的顺序
T->data=ch;
//所有结点的左右标志初始化置为Ptr 0
T->LTag=Ptr;
T->RTag=Ptr;
createBiTree(T->lchild); //先构造左子树
createBiTree(T->rchild); //再构造右子树
}
}
void visit(char data)
{
printf("%s",data);
}
//根据线索中序遍历二叉树(非递归)
void inOrderTraverse(Ttree Thrt)
{
Ttree p;
//T指向头结点,p指向根结点
p=Thrt->lchild;
while(p!=Thrt) //若p==T,则遍历完成,再次回到头结点
{
while(p->LTag==Ptr)
{
p=p->lchild; //直到找到根结点的最左子树的最左结点
}
visit(p->data);
printf("%s",p->LTag);
printf("%s",p->RTag);
printf("\n");
while(p->RTag==Thread&&p->rchild!=Thrt)
{
p=p->rchild;
visit(p->data);
printf("%s",p->LTag);
printf("%s",p->RTag);
printf("\n");
}
p=p->rchild;
}
}
void inOrderThreadTree(Ttree& Thrt,Ttree T)
{
//创建头结点Thrt
if(!((Thrt)=(Ttree)malloc(sizeof(Tnode)))) exit(0);
Thrt->LTag=Ptr; //头结点的左标志位为0,指针
Thrt->RTag=Thread; //头结点的右标志位为1,线索
Thrt->rchild=Thrt; //右指针回指本身
if(!T) Thrt->lchild=Thrt; //二叉树为空时,指向为无,回指本身
else
{
//若二叉树存在,将头结点与二叉树进行相应的链接
Thrt->lchild=T;
pre=Thrt; //头结点为前驱
inThreading(T); //中序遍历进行中序线索化
pre->rchild=Thrt; //右指针回指本身
pre->RTag=Thread;
Thrt->rchild=pre;
}
}
void inThreading(Ttree p)
{
//同样是左根右的递归思想
if(p)
{
inThreading(p->lchild);
visit(p->data);
if(!p->lchild) //左指针域为空则线索化
{
p->LTag=Thread;
p->lchild=pre;
}
if(!pre->rchild)
{
pre->RTag=Thread;
pre->rchild=p;
}
pre=p;
inThreading(p->rchild);
}
}
void main()
{
Ttree Thrt; //头结点
Ttree T; //根结点
createBiTree(T);
inOrderThreadTree(Thrt,T);
printf("\n");
inOrderTraverse(Thrt);
}
运行结果:
测试结果:
未通过!
3.建立一个二叉树,并且按层次遍历操作。
实现程序:
#include<stdio.h>
#include<stdlib.h>
typedef struct tree
{
int nValue; //数据域
struct tree *pLeft;//左指针域
struct tree *pRight;//右指针域
}Tree;
//创建二叉树 采用递归创建
void CreateTree(Tree **ptree)
{
printf("请输入节点值\n");
int num;
scanf("%d",&num);
if(num == 0)
return;
(*ptree) = (Tree*)malloc(sizeof(Tree));
(*ptree)->nValue = num;
(*ptree)->pLeft = NULL;
(*ptree)->pRight = NULL;
CreateTree(&((*ptree)->pLeft));
CreateTree(&((*ptree)->pRight));
}
//先序遍历二叉树
//Visit只是遍历过程中的一个输出函数
void Visit(Tree* root)
{
printf("%d ",root->nValue);
}
void Traversal(Tree* root)
{
if(root!=NULL)
{
Visit(root);
Traversal(root->pLeft);
Traversal(root->pRight);
}
}
void main()
{
Tree *ptree = NULL;
CreateTree(&ptree);
printf("先序遍历为:\n");
Traversal(ptree);
}
运行结果:
测试结果:
通过!
六、实验总结
通过本次实验学习了练习了二叉树的存贮结构及遍历方式掌握了有关算法的实现、能够利用二叉树解决一些问题问题。实验中遇到很多问题思考二并没有运行成功,需要继续学习。