昨天使用stack编写了一个简单的加减乘除计算器

stack的用处非常广泛,个人觉得核心在于其先进后出,后进先出的特性,对解决一些嵌套式的任务非常有用,上学期用C++实现了一个Pascal的简单编译器,其中解释执行那一部分也是通过栈(当然这里需要加上随机访问)来实现的,最先的父进程的地址和相关参数保存在栈底,子进程的地址和相关参数接近栈顶,当子进程结束完就回到父进程

利用这个特性,今天尝试用stack实现树的非递归遍历,含先序,中序,后序,也是一个经典的问题了

建树这一部分参考了这篇博客二叉树的非递归遍历

 读入的格式为 x1(x2(x3(...),x4(...)),x5(x6(...),x7(...)), 即根据括号识别结点所在的位置

例如A(B,C(D,E))

其中A为根节点,B是A的左孩子,C是A的右孩子,D,E分别是C的左孩子和右孩子

当然这部分不是最关键的,可是根据实际要求改动,重点看如何利用Stack实现树的先序遍历,中序遍历,后序遍历

其中最简单的先序遍历和中序遍历,代码也很相似

先序遍历即“根左右”,每访问一个结点就输出其值,然后访问左子结点,若为空,则访问其右子结点,如此往复。

void preOrder(BiTree *root){
	stack<BiTree*>s;
	BiTree *p=root;//p指向当前结点
	while(p!=NULL||!s.empty()){//只要p不为空,或者栈中还有剩余结点,即没输出的结点,循环继续
		while(p!=NULL){//此子循环用于定位优先输出的结点 
                 //在前序遍历中,每访问到一个结点,都认为是根节点,因此直接输出,继而访问左子结点
			cout<<p->data<<" ";
			s.push(p);
			p=p->lchild;
		}//当左子结点为空时,根据根左右的顺序,接着访问右子节点
		if(!s.empty()){
			p=s.top();
			s.pop();
			p=p->rchild;
		}
	}
}

中序遍历即“左根右”,每访问一个结点,继续访问其左子节点,若为空,则输出该结点的值,然后再访问右子节点,如此往复

void inOrder(BiTree *root){
	stack<BiTree*>s;
	BiTree *p=root;
	while(p!=NULL||!s.empty()){
		while(p!=NULL){
            //在中序遍历中,需要先访问左子结点,因此每访问一个结点,都需要先找到“最左子结点”
			s.push(p);
			p=p->lchild;
		}
		if(!s.empty()){//左子节点为空时,输出结点的值,根据“左根右”的顺序,接着访问右子结点
			p=s.top();
			cout<<p->data<<" ";
			s.pop();
			p=p->rchild;
		}
	}
}

 后序遍历即“左右根”,每访问一个结点,继续访问其左子节点,若为空,则输出该结点的值,然后回到根节点,再访问根节点的右节点,在访问右节点时,依然需要先访问该右节点的左子节点,以及该右节点的右子节点,当左右子结点全部遍历完,才能输出根节点

后序遍历的复杂之处在于,每一个根节点都会被访问2次,因为左子树遍历完回到根节点时,不能输出根节点的值,需要等右子树遍历完,再次回到根节点时,才能输出根节点的值,在这之前根节点一直在栈中,这就涉及到判断是第一次还是第二次访问根节点的问题,我是通过设置一个flag来实现的

具体做法是在每一个node中添加一个flag标志,初始化为0,第一次访问时设为1,表示这时候左子树遍历完了,但还需要遍历右子树,等右子树遍历完再次回到根节点,flag设为2,在这时候才能输出根节点的值

二叉树的非递归遍历这篇博客中用到了一些其他的处理方法,可以参考

先序和中序遍历则较为简单,不需要加这个判断

typedef struct node{
	char data;
	struct node *lchild, *rchild;
	int flag;//后序遍历时需要判断时第一次访问(flag=0->flag=1)还是第二次访问(flag=1->flag=2)
}BiTree;
void postOrder(BiTree *root){
	stack<BiTree*>s;
	BiTree *p=root;
	while(p!=NULL||!s.empty()){
		while(p!=NULL){
			s.push(p);
			p=p->lchild;
		}
		if(!s.empty()){
			p=s.top();
			if(p->flag==0){//之前没访问过 第一次访问
				p->flag=1;
				p=p->rchild;
			}
			else if(p->flag==1){//之前访问过一次 第二次访问
				p->flag=2;
				cout<<p->data<<" ";
				s.pop();
				p=NULL;
			}
		}
	}
}

以下是包含了非递归先序,中序,后序遍历的完整的代码 

#include<bits/stdc++.h>
using namespace std;

typedef struct node{
	char data;
	struct node *lchild, *rchild;
	int flag;//后序遍历时需要判断时第一次访问(flag=0->flag=1)还是第二次访问(flag=1->flag=2)
}BiTree;

void preOrder(BiTree *root){//先序遍历
	stack<BiTree*>s;
	BiTree *p=root;
	while(p!=NULL||!s.empty()){
		while(p!=NULL){
			cout<<p->data<<" ";
			s.push(p);
			p=p->lchild;
		}
		if(!s.empty()){
			p=s.top();
			s.pop();
			p=p->rchild;
		}
	}
}

void inOrder(BiTree *root){//中序遍历
	stack<BiTree*>s;
	BiTree *p=root;
	while(p!=NULL||!s.empty()){
		while(p!=NULL){
			s.push(p);
			p=p->lchild;
		}
		if(!s.empty()){
			p=s.top();
			cout<<p->data<<" ";
			s.pop();
			p=p->rchild;
		}
	}
}

void postOrder(BiTree *root){//后序遍历
	stack<BiTree*>s;
	BiTree *p=root;
	while(p!=NULL||!s.empty()){
		while(p!=NULL){
			s.push(p);
			p=p->lchild;
		}
		if(!s.empty()){
			p=s.top();
			if(p->flag==0){//之前没访问过 第一次访问
				p->flag=1;
				p=p->rchild;
			}
			else if(p->flag==1){//之前访问过一次 第二次访问
				p->flag=2;
				cout<<p->data<<" ";
				s.pop();
				p=NULL;
			}
		}
	}
}

void creatBiTree(char*s,BiTree *&root)  //创建二叉树,s为形如A(B,C(D,E))形式的字符串 
{
    int i;
    bool isRight=false;
    stack<BiTree*> s1;          //存放结点 
    stack<char> s2;              //存放分隔符
    BiTree *p,*temp;
    root->data=s[0];
    root->lchild=NULL;
    root->rchild=NULL;
    root->flag=0;
    s1.push(root);
    i=1;
    while(i<strlen(s))
    {
        if(s[i]=='(')
        {
            s2.push(s[i]);
            isRight=false;
        }    
        else if(s[i]==',')    
        {
            isRight=true;
        }
        else if(s[i]==')')
        {
            s1.pop();
            s2.pop();
        }
        else if(isalpha(s[i]))
        {
            p=(BiTree *)malloc(sizeof(BiTree));
            p->data=s[i];
            p->lchild=NULL;
            p->rchild=NULL;
            p->flag=0;
            temp=s1.top();
            if(isRight==true)    
            {
                temp->rchild=p;
                cout<<temp->data<<"'s right child is "<<s[i]<<endl;
            }
            else
            {
                temp->lchild=p;
                cout<<temp->data<<"'s left child is "<<s[i]<<endl;
            }
            if(s[i+1]=='(')
                s1.push(p);
        }
        i++;
    }    
}

void display(BiTree *root)        //显示树形结构 
{
    if(root!=NULL)
    {
        cout<<root->data;
        if(root->lchild!=NULL)
        {
            cout<<'(';
            display(root->lchild);
        }
        if(root->rchild!=NULL)
        {
            cout<<',';
            display(root->rchild);
            cout<<')';
        }
    }
}

int main(){	
	char s[100];
    while(scanf("%s",s)==1)
    {
        BiTree *root=(BiTree *)malloc(sizeof(BiTree));
        creatBiTree(s,root);
        display(root);
        cout<<endl;
        preOrder(root);
        cout<<endl; 
        inOrder(root);
        cout<<endl;
        postOrder(root);
        cout<<endl;
    }
    return 0;
}

测试样例及输出如下

树的结构

      A

B           C

         D       E  

先序遍历 A B C D E

中序遍历 B A D C E

后序遍历 B D E C A

copyProperties 遍历 遍历stack_算法