题目:二叉树的非递归遍历(前、中、后)
对于二叉树,有前序、中序以及后序三种遍历方法。
因为树的定义本身就是递归定义,因此采用递归的方法去实现树的三种遍历不仅容易理解而且代码很简洁。
而对于树的遍历若采用非递归的方法,就要采用栈去模拟实现。
思路标签:
- 栈的实现
解答:
1. 先序遍历
对于任一结点P:
- (1) 访问结点P,并将结点P入栈;
- (2) 将P的左孩子置为当前的结点P;判断结点P的左孩子是否为空,若为空,则取栈顶结点并进行出栈操作,并将栈顶结点的右孩子置为当前的结点P。
- (3) 直到P为NULL并且栈为空,则遍历结束。
void preOrder2(BinTree *root) //非递归前序遍历
{
stack<BinTree*> s;
BinTree *p=root;
while(p!=nullptr||!s.empty())
{
while(p!=nullptr)
{
cout<<p->data<<" ";
s.push(p);
p=p->lchild;
}
if(!s.empty())
{
p=s.top();
s.pop();
p=p->rchild;
}
}
}
2. 中序遍历
对于任一结点P:
- (1) 将P入栈并将P的左孩子置为当前的P,若不为空,然后对当前结点P再进行相同的处理;
- (2) 若其左孩子为空,则访问该栈顶元素并进行出栈操作,然后将当前的P置为栈顶结点的右孩子;
- (3) 直到P为nullptr 并且栈为空,则遍历结束。
void inOrder2(BinTree *root) //非递归中序遍历
{
stack<BinTree*> s;
BinTree *p=root;
while(p!=nullptr||!s.empty())
{
while(p!=nullptr)
{
s.push(p);
p=p->lchild;
}
if(!s.empty())
{
p=s.top();
cout<<p->data<<" ";
s.pop();
p=p->rchild;
}
}
}
3. 后序遍历
思路一:
对于任一结点P:
- (1) 为每个节点增加一个标志是否是第一次出现的栈顶的标志,如果第一次出现栈顶,则修改标志,不进行出栈操作;
- (2) 第二次出现在栈顶的元素则直接进行访问并出栈;
- (3) 直到P为空或者栈为空,则访问完毕。
typedef struct node
{
char data;
struct node *lchild,*rchild;
}BinTree;
typedef struct node1
{
BinTree *btnode;
bool isFirst;
}BTNode;
void postOrder2(BinTree *root) //非递归后序遍历
{
stack<BTNode*> s;
BinTree *p=root;
BTNode *temp;
while(p!=nullptr||!s.empty())
{
while(p!=nullptr) //沿左子树一直往下搜索,直至出现没有左子树的结点
{
BTNode *btn=(BTNode *)malloc(sizeof(BTNode));
btn->btnode=p;
btn->isFirst=true;
s.push(btn);
p=p->lchild;
}
if(!s.empty())
{
temp=s.top();
s.pop();
if(temp->isFirst==true) //表示是第一次出现在栈顶
{
temp->isFirst=false;
s.push(temp);
p=temp->btnode->rchild;
}
else //第二次出现在栈顶
{
cout<<temp->btnode->data<<" ";
p=nullptr;
}
}
}
}
思路二:
对于任一结点P:
- (1) 要保证根结点在左孩子和右孩子访问之后才能访问,因此对于根结点P,先将其入栈。
- (2) 如果P不存在左孩子和右孩子,则可以直接访问它;或者P存 在左孩子或者右孩子,但是访问的前一个节点是其左孩子或者右孩子,证明其左孩子和右孩子都已被访问过了,则同样可以直接访问该结点。
- 若非上述两种情况,则将P的右孩子和左孩子依次入栈,保证了 每次取栈顶元素的时候,左孩子在右孩子前面被访问,左孩子和右孩子都在根结点前面被访问。
void postOrder3(BinTree *root) //非递归后序遍历
{
stack<BinTree*> s;
BinTree *cur; //当前结点
BinTree *pre=nullptr; //前一次访问的结点
s.push(root);
while(!s.empty())
{
cur=s.top();
if((cur->lchild==nullptrL&&cur->rchild==nullptr)||
(pre!=nullptr&&(pre==cur->lchild||pre==cur->rchild)))
{
cout<<cur->data<<" "; //如果当前结点没有孩子结点或者孩子节点都已被访问过
s.pop();
pre=cur;
}
else
{
if(cur->rchild!=nullptr)
s.push(cur->rchild);
if(cur->lchild!=nullptr)
s.push(cur->lchild);
}
}
}