首先递归法是操作二叉树的首选方法。非递归法也分前序、中序、后序三种。后序比较特殊,我们在二叉树的节点中设置了成员变量flag,约定创建节点时候初始化为false。定义如下:
typedef struct BinaryTreeNode
{
int data;
BinaryTreeNode * leftchild;
BinaryTreeNode * rightchild;
bool flag;//一定初始化为0
}Node,*NodePoint;
1.非递归前序遍历
void NonRecursivePreOrder(NodePoint Root)
{
stack<NodePoint> s;
NodePoint p = Root;
while(p)
{
cout<<p ->data<<" ";
if(p->rightchild)
s.push(p->rightchild);//注意此处的顺序,先压入右孩子再压入左孩子
if(p->leftchild)
s.push(p->leftchild);
if(s.empty())
break;
else
{p = s.top();s.pop();}
}
}
前序=根左右。对于任一个节点,先输出节点值,然后再操作左子树,因此右子树先压入stack。紧接着的是弹出top顶端的指针进行操作,若上一步左子树为空,则弹出的指针指向右子树。如果上一级的左右孩子均为空,则弹出的为指向上2级的右子树的指针,以此完成递归操作。这里需要控制的压入stack的当前子树先后,大范围来说,操作完某一个节点的左子树,再操作其右子树。
使用栈的非递归前序遍历的另外一种形式:
void NonRecursivePreOrder(NodePoint Root)
{
stack<NodePoint> s;
while(Root||!s.empty())
{
if(Root){
cout<<Root->data;
s.push(Root);
Root=Root->leftchild;
}
else{
Root=s.top();
s.pop();
Root=Root->rightchild;
}
}
}
2.非递归中序遍历
void NonRecursiveInOrder(NodePoint Root)
{
stack<NodePoint> s;
NodePoint p = Root;
while(p||!s.empty())
{
while(p)
{s.push(p);
p = p->leftchild;}
p = s.top();
s.pop();//弹出的根节点
cout<<p ->data<<" ";
p = p->rightchild; }
}
中序=左根右。首先找到某个节点的最左边的节点,以此控制向后退和向右转。找到最左边的节点后,接着做的是判断其右子树是否空。倘若为空,则弹出指向上两级节点的指针。倘若不为空,则遍历以右孩子为根节点的子树的最左边的节点。节点一旦输出,马上弹出。
另外一种非递归中序遍历:
void NonRecursiveInOrder(NodePoint Root)
{
stack<NodePoint> s;
while(Root||!s.empty())
{
if(Root){
s.push(Root);
Root=Root->leftchild; }
else{
Root=s.top();
s.pop();
cout<<Root->data;
Root=Root->rightchild; }
}
}
}
3.非递归后序遍历
void NonRecursivePostOrder(NodePoint Root)
{
stack<NodePoint> s;
NodePoint p=Root;
while(p||!s.empty()){
while(p){
s.push(p);
p=p->leftchild;//遍历至最左根节点
}
p=s.top();
if(p->flag||!p->rightchild)//p尚且不能输出,或者右子树为NULL
{cout<<p->data<<" ";
s.pop();
p=NULL;}//先赋值,仅仅在满足条件的时候,再将元素弹出stack,此处弹出的为子‘根’节点
else
{p->flag=true;
p=p->rightchild;}//顺序不能换
}
}
后序=左右根。先输出左子树。倘若右子树为空,或者后退的时候已经显示遍历过了,那么输出当前节点数据并弹出stack即可;倘若不为空,则对以右子树为根节点的子树遍历左节点压栈。
4.非递归层序遍历
需要使用队列,满足先遍历的节点。且子节点也要先遍历。
void NonRecursivelevelOrder(Tree Root){
queue<Node> q;
Tree temp;
q.push(Root);
while(!q.empty()){
temp=q.front();
q.pop();
cout<<Root->data;
if(Root->leftchild)
q.push(Root->leftchild);
if(Root->rightchild)
q.push(Root->rightchild);
}
}
非递归算法的时间复杂度和空间复杂度都是O(n)。在二叉树的遍历算法中,每个算法都访问了节点的每一个域,其每个域仅被访问一次,故时间复杂度为O(n),三种非递归遍历中,栈的最大空间为深度k倍的节点元素存储空间,最坏的情况,空间复杂度也为O(n)。层序遍历时,队列的最大长度不会超过二叉树中某一层最多的节点数,故最坏的情况空间复杂度也是O(n)。
不论是那种遍历方式,仅仅考虑叶子节点,排序方式都是从左到右。
部分参考修改自:二叉树常用算法总结