昨天使用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