数据结构课程设计:二叉树的基本运算实现
数据结构课程设计:二叉树的基本运算实现:
编写一个程序实现二叉树的基本功能:
1、用户输入字符串创建二叉树:A(B(D,E(H(J,K(L,M(,N))))),C(F,G(,I))) 并允许用户以括号表示法自行输入二叉树
2、实现二叉树的先序遍历、中序遍历、后序遍历的递归和非递归算法、以及层次遍历。
3、要求能查找任一结点在某种遍历序列中的前驱和后继。
4、查找输出从根结点A出发到任意指定结点的路径。
5、查找任意一个结点为根的子树所有结点。
界面如图:
1.前期准备及结点、基本运算编写
#include<iostream>
#include<stdlib.h>
#include<cstring>
using namespace std;
#define ElemType char
#define MaxSize 100 //二叉树的最大长度
#define st_MaxSize 30 //栈的最大长度
#define qu_MaxSize 30 //队列的最大长度
/*关于结点的声明*/
typedef struct node //定义二叉树的结点类型BTNode
{
ElemType data;
struct node* lchild;
struct node* rchild;
int ltag,rtag; //用于标记左右结点是否为空,构建线索二叉树时用
}BTNode;
typedef struct //定义顺序栈的结点类型
{
BTNode* data[st_MaxSize]; //存放栈中的二叉结点元素
int top; //栈顶指针
}SqStack;
typedef struct //定义顺序队列的结点类型
{
BTNode* data[qu_MaxSize]; //定义一个结点数组
int front,rear; //队首队尾结点
} SqQueue;
/*关于顺序栈操作的声明*/
void InitStack(SqStack* &s) //初始化栈
{
s=(SqStack*)malloc(sizeof(SqStack)); //分配一个顺序栈空间,首地址存放在s中
s->top=-1; //栈顶指针置为-1
}
void DestroyStack(SqStack* &s) //销毁顺序栈
{
free(s);
}
bool StackEmpty(SqStack* s) //判断栈是否为空
{
return(s->top==-1);
}
bool Push(SqStack* &s,BTNode* e) //入栈
{
if(s->top==st_MaxSize-1)
return false;
s->data[++s->top]=e;
return true;
}
bool Pop(SqStack* &s,BTNode* &e) //出栈
{
if(s->top==-1)
return false;
e=s->data[s->top--];
return true;
}
bool GetTop(SqStack* s,BTNode* &e) //取栈顶元素
{
if(s->top==-1)
return false;
e=s->data[s->top];
return true;
}
/*关于环形队列操作的声明*/
void InitQueue(SqQueue* &q) //初始化环形队列
{
q=(SqQueue*)malloc(sizeof(SqQueue));
q->front=q->rear=0;
}
void DestroyQueue(SqQueue* &q) //销毁环形队列
{
free(q);
}
bool QueueEmpty(SqQueue* q) //判断环形队列是否为空
{
return (q->front==q->rear);
}
bool enQueue(SqQueue* &q,BTNode* &e) //入队操作
{
if((q->rear+1)%qu_MaxSize==q->front)
return false;
q->rear=(q->rear+1)%qu_MaxSize;
q->data[q->rear]=e;
return true;
}
bool deQueue(SqQueue* &q,BTNode* &e)
{
if(q->front==q->rear)
return false;
q->front=(q->front+1)%qu_MaxSize;
e=q->data[q->front];
return true;
}
2. 二叉树的运算
/*关于二叉树操作的声明*/
void CreateBTree(BTNode* &b,char* str) //创建二叉树
{
BTNode* St[MaxSize];
BTNode* p; //用于创建结点,然后接到树上
int top=-1; //初始化栈顶指针
int k=0; //用于标明操作左孩子结点还是右孩子结点的flag
int j=0; //用于遍历字符串
char ch; //用于遍历给定字符串的字符参数
b=NULL; //先将根结点初始化为空结点
ch=str[j]; //指遍历参数指向开头,准备遍历
while(ch!='\0') //开始遍历字符串
{
switch(ch)
{
case '(': //遍历到左括号
top++;
St[top]=p; //结点入栈,作为子树的根结点
k=1; //k=1:准备创建左孩子结点
break;
case ')': //遍历到右括号
top--; //元素出栈
break;
case ',': //有逗号说明有右孩子结点不为空,准备创建右孩子结点
k=2;
break;
default:
p=(BTNode*)malloc(sizeof(BTNode)); //为p创建一个新的空结点
p->data=ch; //在新结点中存入数据
p->lchild=p->rchild=NULL; //将左右结点首先设为空结点
if(b==NULL) //若操作的是树的根结点
b=p;
else //若操作的不是树的根结点
{
switch(k) //判断标明操作左右孩子结点的flag
{
case 1: //k=1,新建的结点为0
St[top]->lchild=p;
break;
case 2:
St[top]->rchild=p;
break;
}
}
}
ch=str[++j]; //继续遍历str字符串
}
}
void RPreOrder(BTNode* b) //先序遍历递归输出
{
if(b!=NULL)
{
cout<<b->data<<" "; //输出根结点数据
RPreOrder(b->lchild); //递归遍历左树
RPreOrder(b->rchild); //递归遍历右树
}
}
void RInOrder(BTNode* b) //中序遍历递归输出
{
if(b!=NULL)
{
RInOrder(b->lchild); //递归遍历左树
cout<<b->data<<" "; //输出根结点的数据
RInOrder(b->rchild); //递归遍历右树
}
}
void RPostOrder(BTNode* b) //后序遍历递归输出
{
if(b!=NULL)
{
RPostOrder(b->lchild); //递归遍历左树
RPostOrder(b->rchild); //递归遍历右树
cout<<b->data<<" "; //输出根结点的数据
}
}
void NPreOrder(BTNode* b) //先序遍历非递归输出(第一种方法)
{
BTNode* p;
SqStack *st; //声明一个顺序栈
InitStack(st); //初始化栈st
p=b;
while(!StackEmpty(st)||p!=NULL) //当栈不为空,且未输出完所有的叶子结点
{
while(p!=NULL) //一路向左,把所有左下结点入栈
{
cout<<p->data<<" "; //输出根结点
Push(st,p); //然后将根结点的左孩子结点入栈
p=p->lchild;
}
if(!StackEmpty(st))
{
Pop(st,p); //出栈结点p,
p=p->rchild; //然后将右孩子结点入栈,进而循环遍历右子树
}
}
DestroyStack(st);
}
void NPreOrder2(BTNode* b) //先序遍历非递归输出(第二种方法)
{
BTNode* p;
SqStack *st; //声明一个顺序栈
InitStack(st); //初始化栈st
if(b!=NULL)
{
Push(st,b); //根结点入栈
while(!StackEmpty(st)) //栈不空时循环
{
Pop(st,p); //出栈结点p并访问
cout<<p->data<<" ";
/*先进后出,所以先入栈右孩子*/
if(p->rchild!=NULL)
Push(st,p->rchild); //有右孩子时将其入栈
if(p->lchild!=NULL)
Push(st,p->lchild); //有左孩子时将其出栈
}
}
DestroyStack(st); //销毁栈
}
void NInOrder(BTNode* b) //中序遍历非递归输出
{
BTNode* p;
SqStack *st; //声明一个顺序栈
InitStack(st); //初始化栈st
p=b;
while(!StackEmpty(st)||p!=NULL) //当栈不为空,且未输出完所有的叶子结点
{
while(p!=NULL) //一路向左,把所有左下结点入栈
{
Push(st,p);
p=p->lchild;
}
if(!StackEmpty(st)) //当栈不为空时
{
Pop(st,p); //出栈结点p
cout<<p->data<<" "; //输出p
/*至此,完成了最小子树的左孩子的输出和子根的输出,转而处理右子树*/
p=p->rchild; //转而处右子树
}
}
DestroyStack(st);
}
void NPostOrder(BTNode* b) //后序遍历非递归输出
{
BTNode* p;
BTNode* r;
bool flag;
SqStack* st; //定义一个顺序栈指针st
InitStack(st); //初始化栈
p=b;
do
{
while(p!=NULL) //一路向左,把所有左下结点入栈
{
Push(st,p);
p=p->lchild;
}
r=NULL; //r指向刚访问的结点,初始时为空
flag=true; //flag为真表示正在处理栈顶结点
while(!StackEmpty(st)&&flag) //当栈不为空且即将处理栈顶结点
{
GetTop(st,p); //取出当前的栈顶结点p
if(p->rchild==r) //若结点p的右孩子结点为空或为刚刚访问过的结点
{
cout<<p->data<<" "; //输出该根结点p
Pop(st,p);
r=p; //r指向刚刚访问过的结点
}
else
{
p=p->rchild; //转向处理右子树
flag=false; //表示现在不是在处理栈顶结点
}
}
}while(!StackEmpty(st));
DestroyStack(st); //销毁栈
}
void LevelOrder(BTNode* b) //层次遍历算法
{
BTNode* p;
SqQueue* qu;
InitQueue(qu);
enQueue(qu,b);
while(!QueueEmpty(qu))
{
deQueue(qu,p);
cout<<p->data<<" ";
if(p->lchild!=NULL)
enQueue(qu,p->lchild);
if(p->rchild!=NULL)
enQueue(qu,p->rchild);
}
}
BTNode* FindNode(BTNode* b,ElemType x) //查找二叉树b中值为x的结点
{
BTNode* p;
if(b==NULL)
return NULL; //若树或子树为空,直接返回NULL
else if(b->data==x)
return b; //若根结点或子根结点就是要找的结点,返回该(子)根结点
else
{
p=FindNode(b->lchild,x); //查找左树
if(p!=NULL)
return p; //若左树未搜索到,不一定没有,要转而搜索右树
else
return FindNode(b->rchild,x); //若右树未搜索到,那就是真的没有了,所以不需要判断NULL的情况了
}
}
BTNode* pre; //用于线索化二叉树的全局变量
void Thread(BTNode* &p) //将头结点为p的二叉树进行中序线索化
{
if(p!=NULL)
{
Thread(p->lchild); //将左子树线索化
if(p->lchild==NULL) //左孩子不存在时,进行前驱结点线索化
{
p->lchild=pre;
p->ltag=1;
}
else
p->ltag=0;
if(pre->rchild==NULL)
{
pre->rchild=p;
pre->rtag=1;
}
else
pre->rtag=0;
pre=p;
Thread(p->rchild); //右子树线索化
}
}
BTNode* CreateThread(BTNode* b) //将二叉树b进行中序线索化
{
BTNode* root; //创建指向根结点b的指针root
root=(BTNode*)malloc(sizeof(BTNode));
root->ltag=0; //root有左孩子结点,ltag设为0
root->rtag=1; //root无右孩子结点,rtag设为1
root->rchild=b; //右孩子结点指向后继结点,即根结点b
if(b==NULL) //空二叉树时
root->lchild=root; //root的前驱结点就是自己
else
{
root->lchild=b;
pre=root; //pre是p的前驱结点,供加线索用
Thread(b); //中序遍历线索化二叉树
pre->rchild=root;
pre->rtag=1;
root->rchild=pre; //将头结点右线索化
}
return root;
}
BTNode* InPrenode(BTNode* p) //查找中序线索二叉树的前驱
{
BTNode* Pre;
Pre=p->lchild;
if(p->ltag!=1)
while(Pre->rtag==0)
Pre=Pre->rchild;
return (Pre);
}
BTNode* InPostnode(BTNode* p) //查找中序线索二叉树的后继
{
BTNode* Post;
Post=p->rchild;
if(p->rtag!=1)
while(Post->ltag==0)
Post=Post->lchild;
return(Post);
}
bool PathStack(BTNode* b,SqStack* &st,BTNode* x) //将从根结点到某结点的路线入栈
{
if(b==NULL)
return false;
Push(st,b); //将根结点入栈
if(b==x)
return true;
bool temp=false;
if(b->lchild!=NULL) //先查找左子树
temp=PathStack(b->lchild,st,x);
if(!temp&&b->rchild!=NULL) //左子树未找到且右子树不为空时,查找右子树
temp=PathStack(b->rchild,st,x);
if(!temp) //左右都找不到,栈顶元素出栈
{
BTNode* ab; //用来舍弃的工具变量
Pop(st,ab);
}
return temp;
}
void SearchPath(BTNode* b,BTNode* x)
{
SqStack* nst; //创建并初始化第一个栈
InitStack(nst);
PathStack(b,nst,x); //将路径填写到第一个栈中,不过此时路径是反向的
SqStack* pst; //创建并初始化第二个栈
InitStack(pst);
BTNode* trans; //用于把路径从第一个栈搬到第二个栈的搬运变量
while(!StackEmpty(nst)) //把第一个栈的路径搬到第二个栈里:此时路径从反向变成正向
{
Pop(nst,trans);
Push(pst,trans);
}
BTNode* out;
while(!StackEmpty(pst)) //然后把第二个栈输出
{
Pop(pst,out);
cout<<out->data<<" ";
}
}
BTNode* b; //声明根结点
char a[MaxSize]="A(B(D,E(H(J,K(L,M(,N))))),C(F,G(,I)))"; //默认的二叉树
char se_le; //用于后续的结点值输入
BTNode* se; //用于存放搜索到的结点
BTNode* h; //用于构建线索二叉树
3.菜单函数及主函数的编写
bool Out()
{
cout<< "\n\n\t功能菜单:\n"
"\n\t\t1.实现二叉树的先序遍历(递归)\n"
"\n\t\t2.实现二叉树的先序遍历(非递归法1)\n"
"\n\t\t3.实现二叉树的先序遍历(非递归法2)\n"
"\n\t\t4.实现二叉树的中序遍历(递归)\n"
"\n\t\t5.实现二叉树的中序遍历(非递归)\n"
"\n\t\t6.实现二叉树的后序遍历(递归)\n"
"\n\t\t7.实现二叉树的后序遍历(非递归)\n"
"\n\t\t8.实现二叉树的层次遍历\n"
"\n\t\t9.查找任意结点在中序遍历中的前驱和后继\n"
"\n\t\t10.查找输出从根结点A出发到任意指定结点的路径\n"
"\n\t\t11.查找任意一个结点为根的子树所有结点。\n"
"\n\t\t12.退出\n"
"\n\t 请输入功能编号:";
int choice;
cin>>choice;
switch(choice)
{
case 1:
cout<<"\n\n\t二叉树的递归先序遍历如下:\n\t\t";
RPreOrder(b);
cout<<endl;
system("pause");
cout<<endl<<endl;
Out();
cout<<endl<<endl;
break;
case 2:
cout<<"\n\n\t二叉树的第一种非递归先序遍历如下:\n\t\t";
NPreOrder(b);
cout<<endl;
system("pause");
cout<<endl<<endl;
Out();
cout<<endl<<endl<<endl;
break;
case 3:
cout<<"\n\n\t二叉树的第二种非递归先序遍历如下:\n\t\t";
NPreOrder2(b);
cout<<endl;
system("pause");
cout<<endl<<endl<<endl;
Out();
cout<<endl<<endl;
break;
case 4:
cout<<"\n\t\t二叉树的递归中序遍历如下:\n\t\t";
RInOrder(b);
cout<<endl;
system("pause");
cout<<endl<<endl<<endl;
Out();
cout<<endl<<endl;
break;
case 5:
cout<<"\n\t\t二叉树的非递归中序遍历如下:\n\t\t";
NInOrder(b);
cout<<endl;
system("pause");
cout<<endl<<endl<<endl;
Out();
cout<<endl<<endl;
break;
case 6:
cout<<"\n\t\t二叉树的递归后序遍历如下:\n\t\t";
RPostOrder(b);
cout<<endl;
system("pause");
Out();
cout<<endl<<endl;
cout<<endl<<endl<<endl;
break;
case 7:
cout<<"\n\t\t二叉树的非递归后序遍历如下:\n\t\t";
NPostOrder(b);
cout<<endl;
system("pause");
cout<<endl<<endl<<endl;
Out();
cout<<endl<<endl;
break;
case 8:
cout<<"\n\t\t二叉树的层次遍历如下:\n\t\t";
LevelOrder(b);
cout<<endl;
system("pause");
cout<<endl<<endl<<endl;
Out();
cout<<endl<<endl;
break;
case 9:
cout<<"\n\t\t请输入待查找的结点的值(大写字母): ";
cin>>se_le;
se=FindNode(b,se_le);
h=CreateThread(b);
if(se==NULL)
cout<<"\n\t未找到值为"<<se_le<<"的结点!!\n";
else
{
cout<<endl<<"\t"<<se_le<<"的中序遍历过程中的前驱结点是: "<<InPrenode(se)->data<<endl;
cout<<endl<<"\t"<<se_le<<"的中序遍历过程中的后继结点是: "<<InPostnode(se)->data<<endl;
}
cout<<endl<<endl<<endl;
CreateBTree(b,a); //由于前面构建了线索二叉树,导致所有结点的指针都不为空指针,所以这里需要重新构建
system("pause");
Out();
cout<<endl<<endl;
break;
case 10:
cout<<"\n\t\t请输入待查找路径的结点:";
cin>>se_le;
se=FindNode(b,se_le);
if(se==NULL)
cout<<"\n\t未找到值为"<<se_le<<"的结点!!\n";
else
{
cout<<"\t\t";
SearchPath(b,se);
}
cout<<endl;
system("pause");
cout<<endl<<endl<<endl;
Out();
cout<<endl<<endl;
break;
case 11:
cout<<"\n\t\t请输入待查找的子树的根:\n";
cin>>se_le;
se=FindNode(b,se_le);
if(se==NULL)
cout<<"\n\t\t未找到值为"<<se_le<<"的结点!!\n";
else
{
cout<<"\t\t";
RPreOrder(se);
}
cout<<endl<<endl<<endl;
system("pause");
Out();
cout<<endl<<endl;
break;
case 12:
return 0;
default:
cout<<"\n\t 功能编号输入错误!\n";
system("pause");
Out();
}
}
int main()
{
cout<< "\n\t\t二叉树基本运算功能菜单:\n"
"\n\t默认二叉树为:A(B(D,E(H(J,K(L,M(,N))))),C(F,G(,I)))\n"
"\n\t是否修改?\n"
"\n\t若需修改:输入Y;\n"
"\n\t如使用默认:输入除Y的任意字符:";
char judge;
cin>>judge;
if(judge=='Y')
{
cout<<"\n\t请输入括号表示的二叉树:(长度小于100)\t";
cin>>a;
a[strlen(a)]='\0';
cout<<"\n\n修改成功!!\n";
system("pause");
}
else
{
cout<<"\n\t将使用默认二叉树:A(B(D,E(H(J,K(L,M(,N))))),C(F,G(,I)))\n";
system("pause");
}
CreateBTree(b,a);
Out();
return 0;
}