实验报告
实验四
实验内容:
实验内容:二叉树的遍历
基本内容:
建立以左右孩子链接结构表示的二叉树,实现二叉树的先序、中序、后序的递归和非递归方式遍历,分层遍历、统计树的高度。
实验思路:
基本内容:
- 创建二叉树:通过函数创建二叉树,利用了函数的递归。从屏幕输入字符,如果字符为‘#’证明该节点为空,赋值为NULL,并结束函数的调用。如果该节点不为空,那么将该节点录入字符正常录入结构体内的存储数据的地方,之后递归调用此函数创建左子树,创建右子树,利用递归从而实现树的创建。
- 二叉树的前序,中序,后序递归调用:先序是当根节点不为空时,先打印输出储存的根节点的字符数据,再递归调用自己的左子树,右子树。中序是当根节点不为空时,先调用左子树,打印再调用右子树。后序是当根节点不为空时,先调用左子树,再调用右子树,最后打印存储数据。
- 二叉树的前序非递归:此函数需要栈来存储,当栈不空或当前节点不空时进入循环。当根节点不空时先打印根节点,然后将该节点入栈,并向左子树移动。当节点为空时,从栈中出栈取出节点并移向右子树。做以上动作直至该循环结束,也就是栈空且当前的节点为空时函数结束。
- 二叉树的中序非递归遍历:此函数需要栈来存储,当栈不空或当前节点不空时进入循环。当根节点不空时将该节点入栈,并向左子树移动。当节点为空时,从栈中出栈取出节点,打印该节点,并移向右子树。做以上动作直至该循环结束,也就是栈空且当前的节点为空时函数结束。
- 二叉树后序非递归遍历:此函数需要栈来存储,当栈不空或当前节点不空时进入循环。当根节点不空时将该节点入栈,并向左子树移动。当节点为空时,从栈中出栈取出节点,当该节点的右子树为标记(也就意味着被遍历过时)或右子树为空时,打印该节点,并将该节点设为标记,并将该节点设为空,以便再次进入循环时再次取栈顶元素。如果该节点的右子树不是标记且不是空时,就移向右子树。做以上动作直至该循环结束,也就是栈空且当前的节点为空时函数结束。
- 二叉树的层次遍历:此函数需要用队列来存储,当该根节点非空时让该节点入队,然后当队列不空时进入循环,取队列中的节点,打印其存储数据并入队其左子树,再入队其右子树。做以上动作,直至队列空。
代码
#include <stdio.h>
#include <stdlib.h>
typedef struct treenode
{
char data;
struct treenode *lson;
struct treenode *rson;
}tn;
void create(tn* * T)
{
char ch;
scanf("%c", &ch);
if (ch == '#')
{
(*T) = NULL;
return;
}
else
{
(*T) = (tn*)malloc(sizeof(tn));
(*T)->data = ch;
create(&(*T)->lson);
create(&(*T)->rson);
}
return;
}
void preorder_recursion(tn *t)/*前序递归遍历函数*/
{
if(t!=NULL)
{
printf("%c",t->data);
preorder_recursion(t->lson);
preorder_recursion(t->rson);
}
return;
}
void inorder_recursion(tn *t)/*中序递归遍历函数*/
{
if(t!=NULL)
{
inorder_recursion(t->lson);
printf("%c",t->data);
inorder_recursion(t->rson);
}
return;
}
void postorder_recursion(tn *t)/*后序递归遍历函数*/
{
if(t!=NULL)
{
postorder_recursion(t->lson);
postorder_recursion(t->rson);
printf("%c",t->data);
}
return;
}
void preorder_non_recursion(tn *p)/*前序非递归遍历函数*/
{
tn* a[100]= {NULL};
tn* t=p;
int top=-1;/*栈底参数*/
while (t!=NULL||top!=-1)/*当栈不空或结点不为空的时候就进入循环*/
{
if (t!=NULL)
{
printf("%c",t->data);/*输出根节点*/
top++;
if (top>99)/*栈满就输出溢出*/
{
printf("the stack is fulled.");
return;
}
a[top]=t;
t=t->lson;/*入栈并从左子树向下遍历*/
}
else
{
t=a[top];
a[top]=NULL;
top--;
t=t->rson;/*出栈并从向右子树向下遍历*/
}
}
}
void inorder_non_recursion(tn *p)/*中序非递归遍历函数*/
{
tn* a[100]= {NULL};
tn* t=p;
int top=-1;/*栈底参数*/
while (t!=NULL||top!=-1)/*当栈不空或结点不为空的时候就进入循环*/
{
if (t!=NULL)
{
top++;
if (top>99)
{
printf("the stack is fulled.");
return;
}
a[top]=t;
t=t->lson;/*入栈并从左子树向下遍历*/
}
else
{
t=a[top];
a[top]=NULL;
top--;
printf("%c",t->data);/*输出根节点*/
t=t->rson;/*出栈并从向右子树向下遍历*/
}
}
}
void postorder_non_recursion(tn *p)/*后序非递归遍历函数*/
{
tn* a[100]= {NULL};
tn* t=p;
tn* pre=NULL;
int top=-1;/*栈底参数*/
while (t!=NULL||top!=-1)
{
while (t!=NULL)
{
top++;
if (top>99)
{
printf("the stack is fulled.");
return;
}
a[top]=t;
t=t->lson;/*入栈并从左子树向下遍历*/
}
t=a[top];/*取栈顶元素*/
if (t->rson==NULL||t->rson==pre)/*如果该节点的右子树遍历过或者该节点的右子树为空*/{
pre=t;/*将该节点当作标记*/
printf("%c",t->data);/*并打印该节点*/
t=NULL;/*令节点为空,用于再一次出栈进行遍历*/
top--;/*将栈顶参数向下移动*/
}
else
{
t=t->rson;/*向右节点移动*/
}
}
return;
}
void levelorder(tn* p)/*层次遍历函数*/
{
tn * t=p;
tn * a[100]= {NULL};
if (p==NULL)return;
int front=-1,rear=0;
a[rear]=t;
while (front!=rear)
{
front++;
printf("%c",a[front]->data);
if (a[front]->lson)/*左子树入队*/
{
rear++;
if (rear>99)/*检测队列是否已经满了*/
{
printf("the queue is fulled.");
return;
}
a[rear]=a[front]->lson;
}
if (a[front]->rson)/*右子树入队*/
{
rear++;
if (rear>99)/*检测队列是否已经满了*/
{
printf("the queue is fulled.");
return;
}
a[rear]=a[front]->rson;
}
}
return;
}
int depth(tn* p)/*统计树的深度函数*/
{
tn* t=p;
if (t==NULL)
{
return 0;/*当该子树为空时,该子树高度为0*/
}
else if(depth(t->lson)>depth(t->rson))return depth(t->lson)+1;/*选取左子树和右子树深度最大的一个加一当作本子树的高度*/
else return depth(t->rson)+1;
}
int main()
{
tn * tree;
printf("请输入二叉树:");
create(&tree);
if (tree==NULL){printf("THIS TREE IS EMPTY.");return 0;}
printf("THE PREORDER(recursion):");
preorder_recursion(tree);
printf("\nTHE INORDER(recursion):");
inorder_recursion(tree);
printf("\nTHE POSTORDER(recursion):");
postorder_recursion(tree);
printf("\nTHE PREORDER(nonrecursion):");
preorder_non_recursion(tree);
printf("\nTHE INORDER(nonrecursion):");
inorder_non_recursion(tree);
printf("\nTHE POSTORDER(nonrecursion):");
postorder_non_recursion(tree);
printf("\nTHE LEVELORDER:");
levelorder(tree);
printf("\nTHE DEPTH OF TREE:");
printf("%d",depth(tree));
return 0;
}
运行结果:
第一组:
输入数据:#(树为空)
输出结果:
第二组:
输入数据:
ABD###C##
输出结果:
第三组:
输入数据:
ABC##D##EFG####
输出结果:
选做内容:
实验思路:
二叉排序树的节点插入:先弄一个搜索指针,指向树根,若n比搜索指针的存储的数据要小就让搜索指针向左移动,并设置一个前驱节点,指向搜索指针的前驱,直到搜索指针为空,并记录搜索指针是前驱指针的左孩子还是右孩子,,最后插入n进入二叉树,如果一开始搜索指针就是空那就直接插入进树根节点。
二叉排序树的建立:与节点插入相同,最后当输入为-1时结束录入
二叉排序树的节点删除:先找到删除的节点,若这个节点在这排序树里面没有,返回0来表示失败,若找到了则看该删除节点有没有左右孩子,都没有直接删掉即可,并将前驱节点的对应值赋为空,若有一个孩子,就让这个孩子替代自己。若有两个孩子则沿左孩子的右链下降,找到比删除节点小的最大的元素,让其代替自己,若这个元素就为删除节点的左孩子,那将删除节点的左孩子赋为该节点的左孩子,并直接进行前驱节点与该节点的对接,再让删除节点的左右孩子赋给该节点,若不是删除节点的左孩子,就让搜索到该替换节点的前驱节点的右子树为替换节点的左孩子,然后将进行前驱节点与该节点的对接,再让删除节点的左右孩子赋给该节点。
代码
#include <stdio.h>
#include <stdlib.h>
typedef struct treenode
{
int data;
struct treenode *lson;
struct treenode *rson;
} tn;
tn * create(tn * T)
{
tn * temp,*q;
int da=0,a=-1/*用来记录到空的节点之前是沿前面的节点的左子树还是右子树向下的*/;
while (1)
{
temp=T;
scanf("%d",&da);
if (da==-1)break;
while (temp!=NULL)
{
q=temp;//搜索到空的时候的前驱节点
if (da<temp->data)
{
temp=temp->lson;
a=0;
}
else
{
temp=temp->rson;
a=1;
}
}
temp = (tn*)malloc(sizeof(tn));
temp->data=da;
temp->lson=NULL;
temp->rson=NULL;
//添加到树中
if (a==-1)
{
T=temp;
continue;
}
else if (a==0)
{
q->lson=temp;
}
else if (a==1)
{
q->rson=temp;
}
}
return T;
}
int insertnode(tn *tree,int n)//与创建二叉树类似
{
tn *temp=tree,*p;
int a=-1;
while (temp!=NULL)
{
p=temp;
if (n==temp->data)return 0;
else if (n<temp->data)
{
temp=temp->lson;
a=0;
}
else
{
temp=temp->rson;
a=1;
}
}
temp = (tn*)malloc(sizeof(tn));
temp->data=n;
temp->lson=NULL;
temp->rson=NULL;
if (a==-1)
{
tree=temp;
return 1;
}
else if (a==0)
{
p->lson=temp;
return 1;
}
else if (a==1)
{
p->rson=temp;
return 1;
}
else return 0;
}
int deletenode(tn*tree,int n)
{
if (!tree)return 0;//若树本身就是空的那没啥可以删的就返回
tn *temp=tree,*p,*q,*k;
int a=-1;//依旧是记录是前驱节点的左子树还是右子树的
while (temp->data!=n)
{
p=temp;
if (n<temp->data)
{
temp=temp->lson;
a=0;
}
else
{
temp=temp->rson;
a=1;
}
if (temp==NULL)return 0;//如果遍历到最后没有找到这个要删除的节点就返回
}
if (temp->lson==NULL&&temp->rson==NULL)//左右子树均空直接删除
{
if (a)p->rson=NULL;
else p->lson=NULL;
return 1;
}
else if (temp->lson==NULL&&temp->rson!=NULL)//左或右有一个不是空的一个是空的就把他们替代自己
{
if (a)p->rson=temp->rson;
else p->lson=temp->rson;
return 1;
}
else if (temp->lson!=NULL&&temp->rson==NULL)//左或右有一个不是空的一个是空的就把他们替代自己
{
if (a)p->rson=temp->lson;
else p->lson=temp->lson;
return 1;
}
else//左右子树都不空
{
q=temp->lson;//沿左子树的右孩子向下
if (q->rson==NULL)//若左孩子的右孩子为空的情况下将左子树的节点替代自己
{
temp->lson=q->lson;//将左孩子挂接
goto there;
}
while (q->rson!=NULL)//一直沿左子树的右子树一直向右下降直到其右孩子是空
{
k=q;
q=q->rson;
}
k->rson=q->lson;//将左孩子挂接
there:
if (a)p->rson=q;
else p->lson=q;
q->lson=temp->lson;
q->rson=temp->rson;
temp=NULL;
return 1;
}
}
void inorder_recursion(tn *t)/*中序递归遍历函数*/
{
if(t!=NULL)
{
inorder_recursion(t->lson);
printf("%d ",t->data);
inorder_recursion(t->rson);
}
return;
}
void levelorder(tn* p)/*层次遍历函数*/
{
tn * t=p;
tn * a[100]= {NULL};
if (p==NULL)return;
int front=-1,rear=0;
a[rear]=t;
while (front!=rear)
{
front++;
printf("%d ",a[front]->data);
if (a[front]->lson)/*左子树入队*/
{
rear++;
if (rear>99)/*检测队列是否已经满了*/
{
printf("the queue is fulled.");
return;
}
a[rear]=a[front]->lson;
}
if (a[front]->rson)/*右子树入队*/
{
rear++;
if (rear>99)/*检测队列是否已经满了*/
{
printf("the queue is fulled.");
return;
}
a[rear]=a[front]->rson;
}
}
return;
}
int main()
{
tn * tree=NULL;
printf("请在输入的最后加上-1代表已经结束输入。\n");
printf("请输入排序二叉树:");
tree=create(tree);
if (tree==NULL)
{
printf("此树为空。\n");
}
printf("中序遍历:");
inorder_recursion(tree);
printf("\n");
printf("层次遍历:");
levelorder(tree);
printf("\n\n");
int a;
printf("请输入插入的节点:");
scanf("%d",&a);
if (insertnode(tree,a))
{
printf("下面是插入完成后的中序遍历:");
inorder_recursion(tree);
printf("\n");
printf("层次遍历:");
levelorder(tree);
printf("\n\n");
}
else printf("插入失败。\n");
printf("请输入删除的节点:");
scanf("%d",&a);
if (deletenode(tree,a))
{
printf("下面是删除完成后的中序遍历:");
inorder_recursion(tree);
printf("\n");
printf("层次遍历:");
levelorder(tree);
printf("\n\n");
}
else printf("插入失败。");
return 0;
}
运行结果:
输入数据:
请在输入的最后加上-1代表已经结束输入。
请输入排序二叉树:235 48 704 15 91 9 321 82 -1
请输入插入的节点:86
请输入删除的节点:48
输出结果:
Pains and Gains:
- 熟练了二叉树的各种基本操作,加深了对二叉树这一重要数据结构的理解。
- 学习了二叉排序树的各种操作,在具体代码中感受到了二叉排序树的特殊性质带来的许多便利之处。
- 使用到了栈和队列这两个之前学习的数据结构,让我切实理解了这两个数据结构的特点与便捷之处,提高了我对数据结构的理解。