山东大学数据结构与算法实验9二叉树操作(二叉树基础/二叉树遍历)

A : 二叉树基础

题目描述

创建二叉树类。二叉树的存储结构使用链表。提供操作:前序遍历、中序遍历、后序遍历、层次遍历、计算二叉树结点数目、计算二叉树高度,其中前序遍历要求以递归方式实现,中序遍历、后序遍历要求以非递归方式实现。

输入输出格式

输入格式

第一行为一个数字 n (10<=n<=100000),表示有这棵树有 n 个节点,编号为 1~n。
之后 n 行每行两个数字,第 i 行的两个数字 a、b 表示编号为 i 的节点的左孩子节点为 a,右孩子节点为 b,-1 表示该位置没有节点。
保证数据有效,根节点为 1。

输出格式

第一行,n 个数字,表示该树的前序遍历。
第二行,n 个数字,表示该树的中序遍历。
第三行,n 个数字,表示该树的后序遍历。
第四行,n 个数字,表示该树的层次遍历。
第五行,n 个数字,第 i 个数字表示以 i 节点为根的子树的节点数目。
第六行,n 个数字,第 i 个数字表示以 i 节点为根的子树的高度。

数据结构与算法描述

现需要构造一个树的节点结构体,包括节点的数字,左右孩子。构造二叉树链表类,私有成员为根节点,节点的指针数组,树的节点个数。构造函数中要用new给初始化根节点,并分配空间。给指针数组分配空间,并把根节点传到数组里。

struct binaryTreeNode {//树节点 
	int element;
	binaryTreeNode* leftchild;
	binaryTreeNode* rightchild;
	binaryTreeNode(int a){
		element=a;
		leftchild=rightchild=NULL;
	}
};

输入函数:参数有结点序号,左右孩子的输入,利用结点的指针数组依次将存进数组的树节点提取出来,不为-1则说明孩子存在,则将其存在数组对应的位置。再主函数中依次将n个节点传入函数。

	void input(int i,int a,int b){//输入函数 
			binaryTreeNode* x=treenode[i]; 
			if(a!=-1){
				x->leftchild=new binaryTreeNode(a);//初始化该孩子 
				treenode[a]=x->leftchild;//将孩子传给对应位置的指针数组 
			}
			if(b!=-1){
				x->rightchild=new binaryTreeNode(b);
				treenode[b]=x->rightchild;
			}
		
	}

前序遍历:利用递归,每次先输出根节点的数,再依次递归左子树、右子树。

void preOrder(binaryTreeNode *t) {//前序遍历
		if (t != NULL) {
			cout << t->element << " ";
			preOrder(t->leftchild);
			preOrder(t->rightchild);
		}
	}
	void outputpreOrder(){//前序遍历输出 
		preOrder(root);
		cout << endl;
	}

中序遍历:利用栈先进后出的性质。从根开始压入栈中,依次寻找左子树并依次压入栈中。等到没有左子树了,将栈顶输出,再转向右子树,如果右子树存在,再判断右子树是否有左子树,再次循环。

	void inOrder(){//中序遍历 
		arrayStack<binaryTreeNode*> s(treesize);//声明一个treesize大小的栈 
		binaryTreeNode* t;
		t=root;
		while(t!=NULL || !s.empty()){
			while(t!=NULL){ 
				s.push(t);//不为空压入栈中 
				t=t->leftchild;//看是否有左子树 
			}
			//此时t的左子树或没有,或者已经输出了 
			t=s.top();
			s.pop();
			cout<<t->element<<" ";//将栈顶输出 
			t=t->rightchild;
		}
	}

后序遍历:前序遍历的顺序是根左右,后序遍历的顺序是左右根,将前序遍历的左右反过来,就变成了根右左,借助栈倒过来就变成了左右根和后序遍历的顺序一样了。前序遍历需要一个栈,存后序遍历倒过来的数也要一个栈。将原前序遍历输出的位置改为进入后序遍历的栈压入,依次找是否存在右子树(原前序遍历为左子树),当没有时将栈顶元素弹出,并判断其是否有左子树(原前序遍历是右子树)。依次压入栈后,因为栈是后进先出,使得根右左倒着输出,为后序遍历的左右根。

void postOrder(){//后序遍历 
		arrayStack<binaryTreeNode*> s1(treesize);//前序遍历需要用的栈 
		arrayStack<binaryTreeNode*> s2(treesize);//存后序遍历 
		binaryTreeNode* t=root;
		while(t!=NULL || !s1.empty()){
			while(t!=NULL){
				s2.push(t);//中间的根节点放到后序的栈里 
				s1.push(t);
				t=t->rightchild;
			}
			//此时t的左(后序改为右)子树或没有,或者已经输出了 
			t=s1.top();
			s1.pop();
			t=t->leftchild;//查看已经输出的节点是否有右(后序改为左)子树 
		}
		while(!s2.empty()){//利用栈后进先出,输出后序遍历 
			binaryTreeNode* a=s2.top();
			cout<<a->element<<" ";
			s2.pop();
		}
	}

层次遍历:利用队列先进先出,将跟压入队列中。在循环中先去除队列的第一个,并输出、删除,然后依次判断左右子树是否有,如果有则压入队列中,直到队列为空循环结束。

void linkedBinaryTree::levelOrder() {//层次遍历
	queue<binaryTreeNode*> q(treesize);//声明一个 treesize大小的队列,利用队列先进先出 
	binaryTreeNode* t;
	q.push(root);//将根压入队列中 
	while (!q.empty()) {
		t = q.front();
		cout << t->element << " ";
		q.pop();
		//先判断左,再判断右 
		if (t->leftchild != NULL) {
			q.push(t->leftchild);
		}
		if (t->rightchild != NULL) {
			q.push(t->rightchild);
		}
	}
	cout << endl;
}

计算以i为根节点的子树的节点个数:利用递归当i为NULL时返回0,依次用nl,nr接收左右子树的节点数,返回值为左右子树节点数求和并加1,原左右子树节点数本身也要加一。

int linkedBinaryTree::number(binaryTreeNode* t){
	if (t == NULL) return 0;
	int nl = number(t->leftchild);//左子树节点数 
	int nr = number(t->rightchild);//右子树节点数 
	return (nl++)+(nr++)+1;//左右节点数求和并加上t节点,原节点数本身加一(递归返回的过程) 
}

输出节点个数:类似层次遍历,用一个数组,数组对应的位置就是以对应节点为根时子树的节点个数。再层次遍历时依次调用上一个函数,将返回值存入数组中。最后将数组输出。

void linkedBinaryTree::outputnumber(){
	queue<binaryTreeNode*> q(treesize);//队列类层次遍历 
	binaryTreeNode* t;
	int b[treesize + 1];//以各节点为根的节点数 
	q.push(root);
	while (!q.empty()) {//层次遍历 
		t = q.front();
		b[t->element]=number(t);//将节点数,存在对应节点的数组位置上 
		q.pop();
		if (t->leftchild != NULL) {
			q.push(t->leftchild);
		}
		if (t->rightchild != NULL) {
			q.push(t->rightchild);
		}
	}
	for(int i=1;i<=treesize;i++){
		cout<<b[i]<<" ";
	}
	cout<<endl;
}

计算以i为根节点的子树的高:利用递归,用hl,hr分别接受递归产生的左右子树的高,判断hl,hr哪个大,大的加一就为要返回的高,本身也要加一。

int linkedBinaryTree::height(binaryTreeNode* t) {
	if (t == NULL) return 0;
	int hl = height(t->leftchild);//左子树的高 
	int hr = height(t->rightchild);//右子树的高 
	if (hl > hr)return ++hl;//将较大的加一(加上t本身)作为高,原高本身加一(递归返回的过程) 
	else return ++hr;
}

输出个节点的高:与输出系节点个数相同。致死将数组改为存高度。

void linkedBinaryTree::outputheight() {
	queue<binaryTreeNode*> q(treesize);//队列,类层次遍历 
	binaryTreeNode* t;
	int b[treesize + 1];//各节点高度 
	q.push(root);
	while (!q.empty()) {//层次遍历 
		t = q.front();
		b[t->element]=height(t);//将高度存到数组的对应位置 
		q.pop();
		if (t->leftchild != NULL) {
			q.push(t->leftchild);
		}
		if (t->rightchild != NULL) {
			q.push(t->rightchild);
		}
	}
	for(int i=1;i<=treesize;i++){
		cout<<b[i]<<" ";
	}
	cout<<endl;
}

测试结果

完整代码(含注释) 

#include<iostream>
using namespace std;
template<class T>
class queue {
private:
	int queuefront;//第一个数前一个位置的索引
	int queueback;//最后一个数的索引
	int arraylength;//队列长度
	T* queue0;
public:
	queue(int l) {
		arraylength = l;
		queue0 = new T[arraylength];
		queuefront = queueback = 0;
	}
	~queue() { delete[] queue0; }
	bool empty()const { return queuefront==queueback; }//是否为空
	int qsize()const { return queueback - queuefront; }//列表大小
	T& front() { return queue0[queuefront]; }//第一个位置
	void pop() {//删除
		queuefront++;
	}
	void push(T& theelement) {//插入
		queue0[queueback] = theelement;
		queueback++; 
	}
};
template<class T>
class arrayStack {
private:
	int stackTop;//栈顶
	int arrayLength;//栈的容量
	T* stack;//元素数组
public:
	arrayStack(int al=2000) {
		arrayLength = al;
		stack = new T[al];
		stackTop = -1;//栈为空
	}
	~arrayStack() { delete[] stack; }
	bool empty()const { return stackTop == -1; }//判断是否为空
	int Stacksize()const { return stackTop + 1; }//元素个数
	T& top() { return stack[stackTop]; }//返回栈顶元素
	void pop() {//删除栈顶元素
		stack[stackTop] = 0;
		stackTop--;
	}
	void push(const T& theElement) {//在栈顶后加入元素
		++stackTop;
		stack[stackTop] = theElement;
	}
};
struct binaryTreeNode {//树节点 
	int element;
	binaryTreeNode* leftchild;
	binaryTreeNode* rightchild;
	binaryTreeNode(int a){
		element=a;
		leftchild=rightchild=NULL;
	}
};
class linkedBinaryTree {//二叉树链表 
private:
	binaryTreeNode* root;//根节点 
	binaryTreeNode** treenode;//节点的指针数组 
	int treesize;//树的节点个数 
public:
	linkedBinaryTree(int t) {
		root = new binaryTreeNode(1);//初始化根节点 
		treesize = t;
		treenode=new binaryTreeNode*[treesize+1];//给指针数组分配空间 
		treenode[1]=root;
	}
	bool empty()const { return treesize == 0; }
	void input(int i,int a,int b){//输入函数 
			binaryTreeNode* x=treenode[i]; 
			if(a!=-1){
				x->leftchild=new binaryTreeNode(a);//初始化该孩子 
				treenode[a]=x->leftchild;//将孩子传给对应位置的指针数组 
			}
			if(b!=-1){
				x->rightchild=new binaryTreeNode(b);
				treenode[b]=x->rightchild;
			}
		
	}
	void preOrder(binaryTreeNode *t) {//前序遍历
		if (t != NULL) {
			cout << t->element << " ";
			preOrder(t->leftchild);
			preOrder(t->rightchild);
		}
	}
	void outputpreOrder(){//前序遍历输出 
		preOrder(root);
		cout << endl;
	}
	void inOrder(){//中序遍历 
		arrayStack<binaryTreeNode*> s(treesize);//声明一个treesize大小的栈 
		binaryTreeNode* t;
		t=root;
		while(t!=NULL || !s.empty()){
			while(t!=NULL){ 
				s.push(t);//不为空压入栈中 
				t=t->leftchild;//看是否有左子树 
			}
			//此时t的左子树或没有,或者已经输出了 
			t=s.top();
			s.pop();
			cout<<t->element<<" ";//将栈顶输出 
			t=t->rightchild;
		}
	}
	void postOrder(){//后序遍历 
		arrayStack<binaryTreeNode*> s1(treesize);//前序遍历需要用的栈 
		arrayStack<binaryTreeNode*> s2(treesize);//存后序遍历 
		binaryTreeNode* t=root;
		while(t!=NULL || !s1.empty()){
			while(t!=NULL){
				s2.push(t);//中间的根节点放到后序的栈里 
				s1.push(t);
				t=t->rightchild;
			}
			//此时t的左(后序改为右)子树或没有,或者已经输出了 
			t=s1.top();
			s1.pop();
			t=t->leftchild;//查看已经输出的节点是否有右(后序改为左)子树 
		}
		while(!s2.empty()){//利用栈后进先出,输出后序遍历 
			binaryTreeNode* a=s2.top();
			cout<<a->element<<" ";
			s2.pop();
		}
	}
	void levelOrder();
	int number(binaryTreeNode* t);
	void outputnumber();
	int height(binaryTreeNode* t); 
	void outputheight();
};
void linkedBinaryTree::levelOrder() {//层次遍历
	queue<binaryTreeNode*> q(treesize);//声明一个 treesize大小的队列,利用队列先进先出 
	binaryTreeNode* t;
	q.push(root);//将根压入队列中 
	while (!q.empty()) {
		t = q.front();
		cout << t->element << " ";
		q.pop();
		//先判断左,再判断右 
		if (t->leftchild != NULL) {
			q.push(t->leftchild);
		}
		if (t->rightchild != NULL) {
			q.push(t->rightchild);
		}
	}
	cout << endl;
}
int linkedBinaryTree::number(binaryTreeNode* t){
	if (t == NULL) return 0;
	int nl = number(t->leftchild);//左子树节点数 
	int nr = number(t->rightchild);//右子树节点数 
	return (nl++)+(nr++)+1;//左右节点数求和并加上t节点,原节点数本身加一(递归返回的过程) 
}
void linkedBinaryTree::outputnumber(){
	queue<binaryTreeNode*> q(treesize);//队列类层次遍历 
	binaryTreeNode* t;
	int b[treesize + 1];//以各节点为根的节点数 
	q.push(root);
	while (!q.empty()) {//层次遍历 
		t = q.front();
		b[t->element]=number(t);//将节点数,存在对应节点的数组位置上 
		q.pop();
		if (t->leftchild != NULL) {
			q.push(t->leftchild);
		}
		if (t->rightchild != NULL) {
			q.push(t->rightchild);
		}
	}
	for(int i=1;i<=treesize;i++){
		cout<<b[i]<<" ";
	}
	cout<<endl;
}
int linkedBinaryTree::height(binaryTreeNode* t) {
	if (t == NULL) return 0;
	int hl = height(t->leftchild);//左子树的高 
	int hr = height(t->rightchild);//右子树的高 
	if (hl > hr)return ++hl;//将较大的加一(加上t本身)作为高,原高本身加一(递归返回的过程) 
	else return ++hr;
}
void linkedBinaryTree::outputheight() {
	queue<binaryTreeNode*> q(treesize);//队列,类层次遍历 
	binaryTreeNode* t;
	int b[treesize + 1];//各节点高度 
	q.push(root);
	while (!q.empty()) {//层次遍历 
		t = q.front();
		b[t->element]=height(t);//将高度存到数组的对应位置 
		q.pop();
		if (t->leftchild != NULL) {
			q.push(t->leftchild);
		}
		if (t->rightchild != NULL) {
			q.push(t->rightchild);
		}
	}
	for(int i=1;i<=treesize;i++){
		cout<<b[i]<<" ";
	}
	cout<<endl;
}

int main(){
	int n;
	cin>>n;
	linkedBinaryTree tree(n);
	for(int i=1;i<=n;i++){
		int a,b;
		cin>>a>>b;
		tree.input(i,a,b);
	}
	tree.outputpreOrder();
	tree.inOrder();
	cout<<endl;//原函数没回车 
	tree.postOrder();
	cout<<endl;
	tree.levelOrder();
	tree.outputnumber();
	tree.outputheight();
	return 0;
}

B : 二叉树遍历

题目描述

接收二叉树前序序列和中序序列(各元素各不相同),输出该二叉树的后序序列。

输入输出格式

输入格式

输入有三行:
第一行为数字 n。
第二行有 n 个数字,表示二叉树的前序遍历。
第三行有 n 个数字,表示二叉树的中序遍历。

输出格式

输出一行,表示该二叉树的后序遍历序列。

数据结构与算法描述

前序遍历先输出根,中序遍历为左根右,可以利用中序遍历分开左右子树。在递归函数中找到根在中序遍历的位置,然后分别进行左子树,右子树递归,后面输出根的数。递归函数的参数有,前序的数组,中序的数组,前序数组的起始位置,后序数组的起始位置,各子树数组的长度。

测试结果

完整代码(含注释)

#include<iostream>
using namespace std;
void postOrder(int* preOrder,int* inOrder,int prestart,int instart,int length){//前序的数组,中序的数组,前序数组的起始位置,后序数组的起始位置,各子树数组的长度  
	int i=0;
	while(preOrder[prestart]!=inOrder[instart+i]){//找到中序数组中,根节点的位置,i为左右子树的分界线 
		i++;
	}
	if(i>0){
		postOrder(preOrder,inOrder,prestart+1,instart,i);//左子树 
	}
	if(length-i-1>0){
		postOrder(preOrder,inOrder,prestart+i+1,instart+i+1,length-i-1);//右子树 
	}		
	cout<<preOrder[prestart]<<" ";
	
}
int main(){
	int n;
	cin>>n;
	int preOrder[n];
	for(int i=0;i<n;i++){
		cin>>preOrder[i];
	}
	int inOrder[n];
	for(int i=0;i<n;i++){
		cin>>inOrder[i];
	}
	postOrder(preOrder,inOrder,0,0,n);
	return 0;
}

如能打赏,不胜感激[叩谢]。

  • 16
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值