二叉树(4)非递归法遍历二叉树
首先递归法是操作二叉树的首选方法。非递归法也分前序、中序、后序三种。后序比较特殊,我们在二叉树的节点中设置了成员变量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)。
不论是那种遍历方式,仅仅考虑叶子节点,排序方式都是从左到右。
部分参考修改自:二叉树常用算法总结