在此之前,对非递归先序遍历还有一种新的思路,具体方法如下:
非递归先序遍历
此方法类似于非递归实现的快排,利用栈的先进后出特性,在其中一个元素没有完全断绝关系前,下一个元素绝没有机会出栈。同时,这种方法与二叉树的层次遍历有异曲同工之妙,只不过层次遍历使用的数据结构为队列。
如上图,在出栈时判断结点是否有左右孩子,有则每次先将右孩子入栈,再将左孩子入栈。同时出栈时每次都先出左孩子的结点,如果左孩子有子节点将继续入栈。如此一来就可以实现先遍历完左子树再遍历右子树的需求。
其中,灰色框出的为每次出栈的栈顶元素,而红色箭头代表此结点引入的两个新结点,绿色部分代表此结点没有孩子,没有引入新结点。此栈中的出栈顺序即为先序遍历的序列。
void NicePreOreder(struct BtNode* p)
{
if (NULL == p) return;
std::stack<struct BtNode*> st;
st.push(p);
while (!st.empty())
{
p = st.top(); st.pop();
std::cout << p->data << " ";
if (p->rightchild != NULL) // 先入右孩子
{
st.push(p->rightchild);
}
if (p->leftchild != NULL) // 后入左孩子
{
st.push(p->leftchild);
}
}
std::cout << std::endl;
}
非递归后序遍历
对于一个二叉树来说,他需要三部分构成:根、左孩子,右孩子 。而我们仔细观察就会发现对于我们的优先深度遍历来说,不论是哪一种遍历 根结点都会被访问三次。
并且,针对后序遍历的【左、右、根】模式,我们可以总结出如下规律:
- 当根结点访问次数为1次时,下一步将访问左孩子。
- 当根结点访问次数为2次时,下一步将访问右孩子。
- 当根结点访问次数为3次时,下一步将访问自己。
tips:次方法同样适用于先序遍历和中序遍历。先序遍历在根结点访问次数为1时命中,中序遍历在根结点访问次数为2时命中,后序遍历在根节点访问次数为3时命中。
struct StkNode
{
struct BtNode* pnode;
int popnum; // 结点访问次数
StkNode(struct BtNode* p = NULL) :pnode(p), popnum(0) {};
};
void StkPastOreder(struct BtNode* p)
{
if (NULL == p) return;
std::stack<StkNode> st;
st.push(StkNode(p));
while (!st.empty())
{
StkNode node = st.top();
st.pop(); node.popnum++; // 用出栈次数表示结点访问次数
if (node.popnum == 3) // 后序 3 打印根结点
{
std::cout << node.pnode->data << " ";
}
else
{
st.push(node);
if (node.popnum == 1 && node.pnode->leftchild != NULL)
{
st.push(node.pnode->leftchild);
}
else if(node.popnum == 2 && node.pnode->rightchild != NULL)
{
st.push(node.pnode->rightchild);
}
}
}
std::cout << std::endl;
}
先序遍历和中序遍历的写法一样,只需改变对结点引用次数(popnode)的判断条件即可。
代码:略 。。。