【数据结构】二叉树的基本操作
根据二叉树的特性,栈式创建结构体
typedef struct BiTNode{
char data;
struct BiTNode *lchild,*rchild;//左右孩子指针
}BiTNode,*BiTree;
typedef struct stack_1//顺序栈
{
tree* a[MAX];//用来存树的每一个结点
int tag[MAX];//起标记作用,用于非递归建树和非递归后序遍历
int top;//始终指向当前栈中元素的下一个位置,下标从零开始
}stack;
问题:
1、编写函数,创建一棵二叉树(递归算法和非递归算法);
2、编写函数,按树状结构输出二叉树;
3、编写函数,用递归算法分别求二叉树的各种遍历序列;
算法思想阐述:
递归建树:
依次读入输入的字符序列ch,若ch是一个’#’字符,则表明该树为空树,否则:
- 申请一个结点空间T;
- 将ch赋给T->data;
- 递归创建T的左子树;
- 递归创建T的右子树;
非递归建树:
分别定义树根、左子树、右子树的结构体指针,用先序遍历的顺序分别对树根、左子树、右子树进行入栈操作,当左右子树都已建立时,进行出栈操作;
树状输出二叉树:
最直观的打印二叉树,只能用队列记录二叉树的层次遍历,并记录每个节点的层数及这层里的列数,最后再调整位置打印输出。这样的方法实现起来非常麻烦,所以本报告采用逆90度输出的方法打印二叉树。
每个节点都是独立的一行,记录当前是第几层次,根据层数控制输出位置。从最右子树开始输出,再输出根结点,最后输出左子树。
先序递归遍历:
采用递归,先判断节点是否为空,如果为空则返回。先打印当前节点内容,然后依次递归所有左子节点,再依次递归所有右子节点。
中序递归遍历:
采用递归,先判断节点是否为空,如果为空则返回。先依次递归所有左子节点,然后打印当前节点内容,再递归所有右子节点。
后序递归遍历:
采用递归,先判断节点是否为空,如果为空则返回。先依次递归所有左子节点,再递归所有右子节点,最后打印当前节点内容。
一、各函数代码如下
解题一:
//入栈
void push(stack* st, tree* t){
st->a[st->top] = t;
st->top++;
}
//出栈,即删除栈顶元素,并返回指向已删除元素的指针
tree* pop(stack* st){
if (st->top != 0)
{
st->top--;
return st->a[st->top];
}
else
return NULL;
}
//取栈顶元素,但不删除,返回指向栈顶元素的指针
tree* top(stack* st){
if (st->top != 0)
{
return st->a[st->top - 1];
}
else
{
return NULL;
}
}
//非递归建树
tree* NoRecursiveCreate(){
//0---左孩子为空,1---右孩子为空,2建树完成,"#"代表空;
stack_1 A;
stack_1 *st;
st = &A;
tree *root;//树的根结点
tree *p1, *p2;//因为根结点不能动,所有用两个移动指针,用来建树,
int i = 0;//访问ch数组
char ch[MAX];//存放结点的data值的字符数组。
scanf("%s", &ch);
st->top = 0;//栈初始化
while (ch[i] != '\0')
{
if (i == 0)//建立根结点
{
root = (tree*)malloc(sizeof(tree));//建立根结点
root->data = ch[i];
root->lchild = NULL;//左右孩子都置空;
root->rchild = NULL;
p1 = root;//p1代替root操作,因为root不能移动。
push(st, p1);//入栈
st->tag[st->top - 1] = 0;//将他标记为0---左孩子为空,当然右孩子也为空,但是用不着标记,
//因为是按照前序遍历的顺序来建树,即:根节点-左子树-右子树。先建左树
}
else//非根节点
{
if (ch[i] != '#'&&st->tag[st->top - 1] == 0)//建左树(元素非空,即输入不是"#",并且他的标记是0);
{
p2 = (tree*)malloc(sizeof(tree));//创建一个新结点
p2->data = ch[i];
p2->lchild = NULL;//一定要有,不然会和下面的代码冲突,可以看做是对这个结点的初始化;
p2->rchild = NULL;
push(st, p2);//入栈
p1->lchild = p2;//将新创建的左孩子连接的树上
p1 = p2;//指针下移,即:指针指向现在的左孩子。
st->tag[st->top - 1] = 0;//标记为0,表示此节点没有左孩子
}
else if (ch[i] == '#'&&st->tag[st->top - 1] == 0)//左树完成
{
p1->lchild = NULL;
st->tag[st->top - 1] = 1;//左子树构建完成
}
else if (ch[i] == '#'&&st->tag[st->top - 1] == 1)//右树完成
{
p1->rchild = NULL;
st->tag[st->top - 1] = 2;//右子树构建完成,即:这课小树构建完成;
}
else if (ch[i] != '#'&&st->tag[st->top - 1] == 1)//建右树
{
p2 = (tree*)malloc(sizeof(tree));
p2->data = ch[i];
p2->lchild = NULL;
p2->rchild = NULL;
push(st, p2);
p1->rchild = p2;
p1 = p2;
st->tag[st->top - 1] = 0;
}
while (st->tag[st->top - 1] == 2)//是否出栈操作,当左右子树都已建立时,出栈
{
st->tag[st->top - 1] = 0;//出栈之后,清除标记
p1 = pop(st);//出栈
if(st->top != 0)//避免最后一个节点误操作,如果没有此句,会导致栈的top越界
p1 = top(st);//取当前的栈顶元素
else
break;//所有元素均已出栈,当前的栈为空
if (st->tag[st->top - 1] == 1)//说明此时结点的左子树已存在,并且当前指针是从下一右子树返上来的结点,即:右子树也存在;
{
st->tag[st->top - 1] = 2;
}
}
if (p1->lchild != NULL || st->tag[st->top - 1] ==1)//对返上来的结点进行判断,有左子树则改变标记为1
{
st->tag[st->top - 1] = 1;
}
}
i++;//输入下一个元素
}
return root;
}
//递归先序输出非递归创建的树
void PreprintNoRecursive(tree* t){
tree* p1;
p1 = t;
if (!p1)
return;
else
{
printf("%c ", p1->data);
PreprintNoRecursive(p1->lchild);
PreprintNoRecursive(p1->rchild);
}
}
//递归中序输出非递归创建的树
void InprintNoRecursive(tree* t){
tree* p1;
p1 = t;
if (!p1)
return;
else
{
InprintNoRecursive(p1->lchild);
printf("%c ", p1->data);
InprintNoRecursive(p1->rchild);
}
}
//递归后序输出非递归创建的树
void PostprintNoRecursive(tree* t){
tree* p1;
p1 = t;
if (!p1)
return;
else
{
PostprintNoRecursive(p1->lchild);
PostprintNoRecursive(p1->rchild);
printf("%c ", p1->data);
}
}
//递归建树
void RecursiveCreate(BiTree &T){//按先序输入
cin>>ch;
if(ch =='#'){
T = NULL; //递归结束,建空树
}
else{
T = new BiTNode;
T->data = ch; //生成根结点
RecursiveCreate(T->lchild); //递归创建左子树
RecursiveCreate(T->rchild); //递归创建右子树
}
}
//递归先序输出递归创建的树
void PreprintRecursive(BiTree T){
int n = 0,i;
if(T == NULL) return;
printf("%c ", T->data);
for(i = 0; i < n;i++)
printf(" ");
PreprintRecursive(T->lchild);
PreprintRecursive(T->rchild);
}
//递归中序输出递归创建的树
void InprintRecursive(BiTree T){
int n = 0,i;
if(T == NULL) return;
InprintRecursive(T->lchild);
for(i = 0; i < n;i++)
printf(" ");
printf("%c ", T->data);
InprintRecursive(T->rchild);
}
//递归后序输出递归创建的树
void PostprintRecursive(BiTree T){
int n = 0,i;
if(T == NULL) return;
PostprintRecursive(T->lchild);
for(i = 0; i < n;i++)
printf(" ");
PostprintRecursive(T->rchild);
printf("%c ", T->data);
}
//树状打印二叉树: type : 0表示根节点,1表示左节点,2表示右节点. level表示层次,用于控制显示的距离
void printTree(BiTNode *n, int type, int level){
int i = 0;
if (NULL == n)
return;
printTree(n->rchild,2,level+1);
switch (type)
{
case 0:
printf("%2c\n", n->data);
break;
case 1:
for (i = 0; i < level; i++)
printf("\t");
printf("\\\n");//转义打印"\"并换行
for (i = 0; i < level; i++)
printf("\t");
printf(" %2c\n", n->data);
break;
case 2:
for (i = 0; i < level; i++)
printf("\t");
printf(" %2c\n", n->data);
for (i = 0; i < level; i++)
printf("\t");
printf("/\n");
break;
}
printTree(n->lchild,1,level+1);
}
//构造一个选择,以实现用户交互
void Switchice(){
BiTree T;
int choice = 0;
cout<<"请选择一个功能:"<<endl;
cout<<"1、递归创建二叉树 2、非递归创建二叉树"<<endl;
scanf("%d",&choice);
switch(choice)
{
case 1: cout<<"按扩展先序遍历序列建立二叉树,请输入序列(一个结点存一个字符):"<<endl;
RecursiveCreate(T);
cout<<"递归创建完成!"<<endl<<endl;
cout<<"逆90°树状打印二叉树,把屏幕逆时针旋转90°食用更佳!"<<endl;
printTree(T,0,0);
printf("\n\n先序遍历输出:");PreprintRecursive(T); cout<<endl;
printf("中序遍历输出:");InprintRecursive(T); cout<<endl;
printf("后序遍历输出:");PostprintRecursive(T);cout<<endl;
system("pause");
system("cls");
Switchice();//递归实现清屏
break;
case 2: tree* F;
printf("输入一组数据(以#分隔):\n");
F = NoRecursiveCreate();
cout<<"非递归创建完成!"<<endl<<endl;
cout<<"逆90°树状打印二叉树,把屏幕逆时针旋转90°食用更佳!"<<endl;
printTree(F,0,0);
printf("\n\n先序遍历输出:");PreprintNoRecursive(F); cout<<endl;
printf("中序遍历输出:");InprintNoRecursive(F); cout<<endl;
printf("后序遍历输出:");PostprintNoRecursive(F);cout<<endl;
system("pause");
system("cls");
Switchice();//递归实现清屏
break;
}
}
解题一完整代码:
#include <iostream.h>
#include <stdlib.h>
#define MAX 100
#define OK 1
using namespace std;
typedef struct BiTNode{
char data;
struct BiTNode *lchild,*rchild; //左右孩子指针
}BiTNode,*BiTree,tree;
/*以下是非递归创建二叉树的各函数*/
typedef struct stack_1//顺序栈
{
tree* a[MAX];//用来存树的每一个结点
int tag[MAX];//起标记作用,用于非递归建树和非递归后序遍历
int top;//始终指向当前栈中元素的下一个位置,下标从零开始
}stack;
char ch;//某些函数用到(char)ch,故在函数外规范以减少代码量
//入栈
void push(stack* st, tree* t){
st->a[st->top] = t;
st->top++;
}
//出栈,即删除栈顶元素,并返回指向已删除元素的指针
tree* pop(stack* st){
if (st->top != 0)
{
st->top--;
return st->a[st->top];
}
else
return NULL;
}
//取栈顶元素,但不删除,返回指向栈顶元素的指针
tree* top(stack* st){
if (st->top != 0)
{
return st->a[st->top - 1];
}
else
{
return NULL;
}
}
//非递归建树
tree* NoRecursiveCreate(){
//0---左孩子为空,1---右孩子为空,2建树完成,"#"代表空;
stack_1 A;
stack_1 *st;
st = &A;
tree *root;//树的根结点
tree *p1, *p2;//因为根结点不能动,所有用两个移动指针,用来建树,
int i = 0;//访问ch数组
char ch[MAX];//存放结点的data值的字符数组。
scanf("%s", &ch);
st->top = 0;//栈初始化
while (ch[i] != '\0')
{
if (i == 0)//建立根结点
{
root = (tree*)malloc(sizeof(tree));//建立根结点
root->data = ch[i];
root->lchild = NULL;//左右孩子都置空;
root->rchild = NULL;
p1 = root;//p1代替root操作,因为root不能移动。
push(st, p1);//入栈
st->tag[st->top - 1] = 0;//将他标记为0---左孩子为空,当然右孩子也为空,但是用不着标记,
//因为是按照前序遍历的顺序来建树,即:根节点-左子树-右子树。先建左树
}
else//非根节点
{
if (ch[i] != '#'&&st->tag[st->top - 1] == 0)//建左树(元素非空,即输入不是"#",并且他的标记是0);
{
p2 = (tree*)malloc(sizeof(tree));//创建一个新结点
p2->data = ch[i];
p2->lchild = NULL;//一定要有,不然会和下面的代码冲突,可以看做是对这个结点的初始化;
p2->rchild = NULL;
push(st, p2);//入栈
p1->lchild = p2;//将新创建的左孩子连接的树上
p1 = p2;//指针下移,即:指针指向现在的左孩子。
st->tag[st->top - 1] = 0;//标记为0,表示此节点没有左孩子
}
else if (ch[i] == '#'&&st->tag[st->top - 1] == 0)//左树完成
{
p1->lchild = NULL;
st->tag[st->top - 1] = 1;//左子树构建完成
}
else if (ch[i] == '#'&&st->tag[st->top - 1] == 1)//右树完成
{
p1->rchild = NULL;
st->tag[st->top - 1] = 2;//右子树构建完成,即:这课小树构建完成;
}
else if (ch[i] != '#'&&st->tag[st->top - 1] == 1)//建右树
{
p2 = (tree*)malloc(sizeof(tree));
p2->data = ch[i];
p2->lchild = NULL;
p2->rchild = NULL;
push(st, p2);
p1->rchild = p2;
p1 = p2;
st->tag[st->top - 1] = 0;
}
while (st->tag[st->top - 1] == 2)//是否出栈操作,当左右子树都已建立时,出栈
{
st->tag[st->top - 1] = 0;//出栈之后,清除标记
p1 = pop(st);//出栈
if(st->top != 0)//避免最后一个节点误操作,如果没有此句,会导致栈的top越界
p1 = top(st);//取当前的栈顶元素
else
break;//所有元素均已出栈,当前的栈为空
if (st->tag[st->top - 1] == 1)//说明此时结点的左子树已存在,并且当前指针是从下一右子树返上来的结点,即:右子树也存在;
{
st->tag[st->top - 1] = 2;
}
}
if (p1->lchild != NULL || st->tag[st->top - 1] ==1)//对返上来的结点进行判断,有左子树则改变标记为1
{
st->tag[st->top - 1] = 1;
}
}
i++;//输入下一个元素
}
return root;
}
//递归先序输出非递归创建的树
void PreprintNoRecursive(tree* t){
tree* p1;
p1 = t;
if (!p1)
return;
else
{
printf("%c ", p1->data);
PreprintNoRecursive(p1->lchild);
PreprintNoRecursive(p1->rchild);
}
}
//递归中序输出非递归创建的树
void InprintNoRecursive(tree* t){
tree* p1;
p1 = t;
if (!p1)
return;
else
{
InprintNoRecursive(p1->lchild);
printf("%c ", p1->data);
InprintNoRecursive(p1->rchild);
}
}
//递归后序输出非递归创建的树
void PostprintNoRecursive(tree* t){
tree* p1;
p1 = t;
if (!p1)
return;
else
{
PostprintNoRecursive(p1->lchild);
PostprintNoRecursive(p1->rchild);
printf("%c ", p1->data);
}
}
//递归建树
void RecursiveCreate(BiTree &T){//按先序输入
cin>>ch;
if(ch =='#'){
T = NULL; //递归结束,建空树
}
else{
T = new BiTNode;
T->data = ch; //生成根结点
RecursiveCreate(T->lchild); //递归创建左子树
RecursiveCreate(T->rchild); //递归创建右子树
}
}
//递归先序输出递归创建的树
void PreprintRecursive(BiTree T){
int n = 0,i;
if(T == NULL) return;
printf("%c ", T->data);
for(i = 0; i < n;i++)
printf(" ");
PreprintRecursive(T->lchild);
PreprintRecursive(T->rchild);
}
//递归中序输出递归创建的树
void InprintRecursive(BiTree T){
int n = 0,i;
if(T == NULL) return;
InprintRecursive(T->lchild);
for(i = 0; i < n;i++)
printf(" ");
printf("%c ", T->data);
InprintRecursive(T->rchild);
}
//递归后序输出递归创建的树
void PostprintRecursive(BiTree T){
int n = 0,i;
if(T == NULL) return;
PostprintRecursive(T->lchild);
for(i = 0; i < n;i++)
printf(" ");
PostprintRecursive(T->rchild);
printf("%c ", T->data);
}
//树状打印二叉树: type : 0表示根节点,1表示左节点,2表示右节点. level表示层次,用于控制显示的距离
void printTree(BiTNode *n, int type, int level){
int i = 0;
if (NULL == n)
return;
printTree(n->rchild,2,level+1);
switch (type)
{
case 0:
printf("%2c\n", n->data);
break;
case 1:
for (i = 0; i < level; i++)
printf("\t");
printf("\\\n");//转义打印"\"并换行
for (i = 0; i < level; i++)
printf("\t");
printf(" %2c\n", n->data);
break;
case 2:
for (i = 0; i < level; i++)
printf("\t");
printf(" %2c\n", n->data);
for (i = 0; i < level; i++)
printf("\t");
printf("/\n");
break;
}
printTree(n->lchild,1,level+1);
}
//构造一个选择,以实现用户交互
void Switchice(){
BiTree T;
int choice = 0;
cout<<"请选择一个功能:"<<endl;
cout<<"1、递归创建二叉树 2、非递归创建二叉树"<<endl;
scanf("%d",&choice);
switch(choice)
{
case 1: cout<<"按扩展先序遍历序列建立二叉树,请输入序列(一个结点存一个字符):"<<endl;
RecursiveCreate(T);
cout<<"递归创建完成!"<<endl<<endl;
cout<<"逆90°树状打印二叉树,把屏幕逆时针旋转90°食用更佳!"<<endl;
printTree(T,0,0);
printf("\n\n先序遍历输出:");PreprintRecursive(T); cout<<endl;
printf("中序遍历输出:");InprintRecursive(T); cout<<endl;
printf("后序遍历输出:");PostprintRecursive(T);cout<<endl;
system("pause");
system("cls");
Switchice();//递归实现清屏
break;
case 2: tree* F;
printf("输入一组数据(以#分隔):\n");
F = NoRecursiveCreate();
cout<<"非递归创建完成!"<<endl<<endl;
cout<<"逆90°树状打印二叉树,把屏幕逆时针旋转90°食用更佳!"<<endl;
printTree(F,0,0);
printf("\n\n先序遍历输出:");PreprintNoRecursive(F); cout<<endl;
printf("中序遍历输出:");InprintNoRecursive(F); cout<<endl;
printf("后序遍历输出:");PostprintNoRecursive(F);cout<<endl;
system("pause");
system("cls");
Switchice();//递归实现清屏
break;
}
}
int main(int argc, char *argv[])
{
Switchice();
return 0;
}
分享完毕~
欢迎小伙伴们在文下评论和私信喔~