数据结构课程设计:二叉树的基本运算实现

数据结构课程设计:二叉树的基本运算实现:
       编写一个程序实现二叉树的基本功能:
           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;

} 
  • 3
    点赞
  • 60
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值