• 数据结构-树的基本操作


    #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("构造完毕
    ");
    	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..满二叉树 
     
     
     
    
  • 相关阅读:
    前端面试集锦
    nodeJs上传附件
    逻辑于 逻辑或
    webpack 学习笔记 (一)
    yum 安装mongodb mysql
    闭包面试提 (2)
    主动的重要性
    1.1一天一题:逆转字符串
    编程提高:一天一道编程题
    iconv将文件编码从gb2312 转换为utf-8
  • 原文地址:https://www.cnblogs.com/czsharecode/p/11408155.html
Copyright © 2020-2023  润新知