最近在刷题,经常会遇到一些树的遍历问题。在之前也写过博客详细讲解二叉树遍历问题的递归实现,可以戳有关二叉树的遍历问题去瞅瞅。这篇博客主要想整理一下非递归的实现及遍历思想。
如上图这是一个二叉树。以前序为例,如果我们想直接去遍历结点,肯定是不行的。首先肯定需要一个大循环,然后一直找左孩子,直到左孩子不存在,然后需要跳出循环,去找没有左孩子的当前根结点是否有右孩子。在这里我们需要借助一个数据结构—栈,我们先将遍历的结点都放在栈里,如果没有左孩子就将当前结点pop掉,去找右孩子,然后接着寻找右孩子的左孩子……依次类推,直到遍历完整个树。
整个过程如上图所示。代码如下。
void PrevOrderNonR()
{
if (_root == NULL)
return;
Node* cur = _root;
stack<Node*> s;
while (cur || !s.empty())
{
while (cur)
{
s.push(cur);
cout << cur->_data << " ";
cur = cur->_left;
}
Node* tmp = s.top();
s.pop();
cur = tmp->_right;
}
cout << endl;
}
前序遍历规则是根-左-右,所以我们在找最左孩子的时候,就对数据进行了访问。如果是中序遍历,那么需要先找到最左孩子,然后在进行访问。中序遍历的思路和上面差不多就不多赘余了。代码如下。
void InOrderNonR()
{
if (_root == NULL)
return;
Node* cur = _root;
stack<Node*> s;
while (cur || !s.empty())
{
while (cur)
{
s.push(cur);
cur = cur->_left;
}
Node* tmp = s.top();
cout << tmp->_data << " ";
s.pop();
cur = tmp->_right;
}
cout << endl;
}
下来要说说后序遍历。后序遍历的情况比较特殊。
以上图的二叉树为例,我们进行左-右-根遍历的时候,最左孩子是2,但2并不是我们要找的结点,第一个访问的结点是3。所以找到当前结点的最左结点后,如果当前结点没有右孩子或者说它自己就是上个结点的右孩子那么就访问,其他情况,就是左孩子找完了,需要找右孩子了。代码如下。
void PostOrderNonR()
{
if (_root == NULL)
return;
Node* cur = _root;
Node* prev = _root;
stack<Node*> s;
while (cur || !s.empty())
{
while (cur)
{
s.push(cur);
prev = cur;
cur = cur->_left;
}
Node* tmp = s.top();
if (tmp->_right == NULL || tmp->_right == prev)
{
cout << tmp->_data << " ";
prev = tmp;
s.pop();
}
else
cur = tmp->_right;
}
cout << endl;
}
总结一下,二叉树的遍历问题,最常见的是递归。如果要用到非递归,那么需要考虑的就是如何将这些问题一点点细化。比如说后序遍历的时候,我什么时候选择打印结点,我如何判断这个结点是不是我要找符合条件的结点,该借助怎样的数据结构可以使整个结构变得清楚。