数据结构-树的基本操作

#include<iostream>
#include<stack> 
#include<queue> 
using namespace std;

//ADT
typedef struct BiTNode{
	char data;
	struct BiTNode *lchild, *rchild;
}BiTNode, *BiTree; 

//建树 
void build(BiTree &T){
	char ch;
	cin>>ch;
	if(ch == '.') T = NULL;
	else{
		T = (BiTNode *)malloc(sizeof(BiTNode));
		T->data = ch;
		build(T->lchild);
		build(T->rchild); 
	}
}
//前序递归 
void PreOrder(BiTree T){
	if(T != NULL){
		cout<<T->data<<endl;;
		PreOrder(T->lchild);
		PreOrder(T->rchild);
	}
}
//中序递归 
void InOrder(BiTree T){
	if(T != NULL){
		InOrder(T->lchild);
		cout<<T->data<<endl;
		InOrder(T->rchild);
	}
}

//后序递归 
void PostOrder(BiTree T){
	if(T != NULL){
		PostOrder(T->lchild);
		PostOrder(T->rchild);
		cout<<T->data<<endl;;
	}
}

//前序遍历非递归版本 
//由于整个左子树被访问完才能访问右子树,故需要借助栈来保存当前子树的根节点 
void PreOrder2(BiTree T){
	stack<BiTNode*> S;//开辟一个栈 
	BiTree p = T;//迭代指针 开始指向root 
	while(p || !S.empty()){ //只要还有结点 
		if(p){ //如果当前结点不空 
			cout<<p->data<<endl; //则打印当前结点 因为是先序,遇到新结点就打印 
			S.push(p);//当前结点入栈 
			p = p->lchild;//访问其左子树,向左访问结点 
		}
		else{
			p = S.top();//如果当前结点是空的,就退回到上一个结点 
			S.pop();//退栈 
			p = p->rchild;//访问其右子树 
		}
	}
}

//中序非递归  左 中 右
//根节点必须在左结点访问之后再访问,而根节点又是先于左子节点被遍历到,故要设置一个栈来保存 
void InOrder2(BiTree T){
	stack<BiTNode*> S;//开辟一个栈 
	BiTree p = T;//迭代器 
	while(p || !S.empty()){ //如果还有结点 
		if(p){//如果当前结点不为空 
			S.push(p);// 不要急于访问根节点,先压栈。 
			p = p->lchild;//访问左子树 
		}
		else{//如果当前节点是空的。则需要退回到上一个结点 
			p = S.top();
			S.pop();//退到该子树的根节点立即访问 
			cout<<p->data<<endl;
			p = p->rchild;//继续访问右子树  
		}
	}
}

//后序非递归 双栈法
//由于是左 右 中,因此入栈次序是中右左,
//先看根节点,再看右孩子,再看左孩子 
void PostOrder2(BiTree T){
	stack<BiTNode*> S; //遍历栈 
	stack<BiTNode*> result; //结果栈 
	BiTree p = T;//迭代器 
	while(p || !S.empty()){
		if(p){ //如果当前结点不为空 
			S.push(p);//结点入栈 
			result.push(p);//结点入结果栈 
			p = p->rchild;//看其右孩子结点 
		}  
		else{ //如果当前结点为空 ,退到上一个结点 
			p = S.top();
			S.pop();//退栈 
			p = p->lchild;//找其左孩子结点 
		}
	}
	//输出后序序列 
	while(!result.empty()){
		p = result.top();
		result.pop();
		cout<<p->data<<endl;
	}
}

// 后序遍历  访问标记法
void postOrder3(BiTree T){
	stack<BiTNode*> S; //开辟一个栈 
	BiTNode* p = T; //迭代器 
	BiTNode* r = NULL; //保存最近访问过的结点 
	while(p || !S.empty()){
		if(p){ //如果当前结点不为空 则走到该子树的最左边 
			
			S.push(p);
			p = p->lchild;
		}
		else{ //若为空,退到上一个结点 
			p = S.top();
			if(p->rchild && p->rchild != r){ //如果存在右子树并且右子树没有被访问过 
				p = p->rchild;//那么就转向右子树继续 
				S.push(p); // 保存子树根节点 
				p = p->lchild; //继续向左访问左子树 
			} 
			else{
				p = S.top(); 
				S.pop();//没有右子树那就可以访问根节点了 
				cout<<p->data<<endl;
				r = p; //标记r为最近访问的结点
				p = NULL; //当前结点置为空 ,因为这棵子树的根节点都访问完了说明这颗子树就全部访问完了,就等待退回上一个子树根节点 
			}
		}
	} 
} 
 
//层序遍历 队列 
void levelOrder(BiTree T){
	queue<BiTNode*> Q;
	BiTNode *p = T;
	Q.push(T);
	while(!Q.empty()){
		p = Q.front();
		Q.pop();
		cout<<p->data<<endl;
		if(p->lchild) Q.push(p->lchild);
		if(p->rchild) Q.push(p->rchild);
	}
}

//递归求树高 
int treeDepth(BiTree T){
	if(T == NULL) return 0;
	
	int rightTree = treeDepth(T->rchild);
	int leftTree = treeDepth(T->lchild);
	
	return max(rightTree , leftTree) + 1;
}
//层序遍历求树高

int levelDepth(BiTree T){
	int level = 0;
	if(!T) return 0;
	queue<BiTNode*> Q;
	BiTNode* last = T; //记录每层最后一个结点,第一层最后一个节点就是根节点 
	BiTNode* front,*rear;//记录队首结点 队尾结点 
	
	Q.push(T);//根节点入队 
	while(!Q.empty()){
		front = Q.front();//取队首 
		Q.pop();//队首弹出 
		if(front->lchild){
		 	Q.push(front->lchild);//如果有左孩子那么左孩子入队 
		 	rear = front->lchild;
		}	
		if(front->rchild){
			Q.push(front->rchild);//如果有右孩子那么右孩子入队 
			rear = front->rchild;
		}
		if(front == last){//如果当前结点是该层最后一个结点 
			//cout<<front->data<<" ?? "<<rear->data<<endl; 
			level++;//那么层数+1 ,到达下一层 
			last = rear;//更新last,下一层的最后一个结点必然是最后入队的那个结点 
		}
	}
	return level;
} 
 
//层次遍历求树宽度
//在层次遍历基础上。增加对每一层结点数的统计,维护max 
int levelWidth(BiTree T){
	int level = 0;
	if(!T) return 0;
	queue<BiTNode*> Q;
	BiTNode* last = T; //记录每层最后一个结点,第一层最后一个节点就是根节点 
	BiTNode* front,*rear;//记录队首结点 队尾结点 
	int ans = 0;//记录最大宽度 
	int cur = 0;//记录当前层的宽度 
	Q.push(T);//根节点入队 
	while(!Q.empty()){
		front = Q.front();//取队首 
		Q.pop();//队首弹出 
		cur++;
		if(front->lchild){
		 	Q.push(front->lchild);//如果有左孩子那么左孩子入队 
		 	rear = front->lchild;  
		}	
		if(front->rchild){
			Q.push(front->rchild);//如果有右孩子那么右孩子入队 
			rear = front->rchild;
		}
		if(front == last){//如果当前结点是该层最后一个结点 
			//cout<<front->data<<" ?? "<<rear->data<<endl; 
			level++;//那么层数+1 ,到达下一层 
			last = rear;//更新last,下一层的最后一个结点必然是最后入队的那个结点
			
			//到达每层最后一个结点时就知道了这一层的总的结点数
			//维护最大值即可 
			ans = max(ans , cur);
			cur = 0; 
		}
	}
	return ans;
} 



//递归遍历求树的宽度
//相当于先序遍历一遍然后把每个结点所在的层数记录下来 
//每访问一个新结点就把该层的count加上1,最终得到所有层的宽度 
void treeWidth(BiTree T, int level, int count[]){
	if(T == NULL) return; //如果子树树根为空就不要再统计下去 
	count[level]++;//树在第level层的宽度+1 
	treeWidth(T->lchild , level+1, count);
	treeWidth(T->rchild , level+1, count);
} 

int treeWidth(BiTree T){
	int count[200] = {0};
	int maxx = 0;
	treeWidth(T,0,count);
	for(int i=0; i<200; i++){
		maxx = max(maxx , count[i]);
	}
	return maxx;
}

//判断时否是完全二叉树
//利用性质 完全二叉树与满二叉树在层次遍历上的联系和区别,前者是后者序列的一部分 

bool isComplete(BiTree T){
	queue<BiTNode*> Q;//Q中存储层次遍历序列 
	BiTNode* p;
	if(!T) return true;//空树也当成完全二叉树 它是满二叉树
	Q.push(T);//首结点入队 
	while(!Q.empty()){ //如果Q不空 
		p = Q.front(); // 取出队首 
		Q.pop();
		if(p){ //如果当前结点不空,将其左右孩子入队,不管孩子空不空 
			Q.push(p->lchild);
			Q.push(p->rchild);
		}
		else{ //如果当前结点为空,那么之后的序列不能有不空的结点,因为层次遍历ABCD.E显然是非法的 
			while(!Q.empty()){
				p = Q.front();
				Q.pop();
				if(p) return false;
			}
		}
	}
	return true; 
} 



int main(){
	BiTree T;
	build(T);
	printf("构造完毕\n");
	cout<<"先序遍历"<<endl;
	PreOrder(T);
	cout<<"中序遍历"<<endl;
	InOrder(T);
	cout<<"后序遍历"<<endl;
	PostOrder(T);
	
	cout<<"非递归中序遍历"<<endl;
	InOrder2(T);
	cout<<"非递归先序遍历"<<endl;
	PreOrder2(T);
	cout<<"非递归后序遍历双栈法"<<endl;
	PostOrder2(T);
	cout<<"非递归后序遍历标记法"<<endl;
	postOrder3(T);
	
	cout<<"层序遍历"<<endl;
	levelOrder(T);
	
	cout<<"递归求树高:"<<treeDepth(T)<<endl; 
	cout<<"层序求树高:"<<levelDepth(T)<<endl;
	
	cout<<"递归求树宽度:"<<treeWidth(T)<<endl;
	cout<<"非递归求树宽度: "<<levelWidth(T)<<endl;
	cout<<"是否是完全二叉树:"<<isComplete(T)<<endl; 
	return 0;  
	
} 

//ABC..DE.G..F..H..
//ABS..C..DE..FG.H..I..
//ABD..E..CF... 完全二叉树  ABD..E..CF..G..满二叉树 
 
 
 
posted @ 2019-08-25 15:52  西风show码  阅读(246)  评论(0编辑  收藏  举报