一、基本概念
二叉树是笔试面试中高频数据结构。
而二叉树的遍历是解决一切二叉树题目的基础。因此,有必要熟练掌握二叉树的遍历方法,包括递归方法 和 非递归方法。
由于递归方法比较简单,因此,这里我们着重来学习二叉树的非递归遍历方法。
首先明确,二叉树的遍历方法按照访问根节点的次序不同,可以分为如下三种:先序遍历、中序遍历、后序遍历。
先序遍历,即先访问根节点,然后再分别访问左子节点、右子节点。
二、实现策略
实现先序遍历的思路:
1、深度优先法则
深度优先遍历是图的一种遍历算法,二叉树是一种图的特例,因此也可以用深度优先法则来进行遍历。
需要注意的问题是:要考虑节点重复访问的问题!
具体实现方法:利用辅助栈!存储访问的节点
举例说明,比如有树结构如图所示:
按照先序遍历的策略:向左走到头,向右走一步
- 访问根节点,并将根节点入栈
- 栈不为空,沿栈顶节点的左子树遍历,将途经的所有节点访问并入栈,同时断开父子节点之间的指针;
- 若当前栈顶点无左子树,则访问右子树;
- 若当前栈顶点无右子树,则将栈顶节点弹出,回到第2步;
- 若当前栈顶点有右子树,则将右孩子节点压入栈,并且断开父子之间的指针,回到第2步;
- 当栈为空,结束。
实现代码:
#include"iostream"
#include"stack"
using namespace std;
struct BiTNode{
int data;
struct BiTNode *lchild, *rchild;
};
class Solution{
public:
void preOrderTraverse(BiTNode *T)
{
if (T == NULL)
return;
stack<BiTNode*> assi;//定义辅助栈
assi.push(T);//压栈
cout << assi.top()->data;//入栈时候访问
while (!assi.empty())
{
while (assi.top()->lchild)
{
cout << assi.top()->lchild->data;
BiTNode *temp = assi.top();//用于断开父子连接 防止重复访问
assi.push(assi.top()->lchild);
temp->lchild = NULL;//断开父子连接
}
if (!assi.top()->rchild)//若右孩子为空则直接弹出,该节点已经访问输出过了。
assi.pop();
else
{
cout << assi.top()->rchild->data;
BiTNode *temp = assi.top();
assi.push(assi.top()->rchild);
temp->rchild = NULL;
}
}
}
};
2、利用栈的特性
栈是后进先出,所以可以先访问右子树 再访问左子树,那么在出栈的时候,就是左子树在右子树前面,符合先序遍历的要求。
这种方法的好处是:写代码的时候不用考虑重复访问,断开指针的事情。
具体操作步骤:
- 根节点入栈
- 栈不为空时,访问栈顶节点并出栈
- 若出栈的节点右孩子不为空,右孩子入栈;若出栈节点的左孩子不为空,左孩子入栈
- 栈为空时,结束
void preOrderTraverse2(BiTNode *T)
{
if (T == NULL)
return;
stack<BiTNode *> st;
st.push(T);
BiTNode *p = NULL;
while (!st.empty())
{
p = st.top();
cout << p->data;
st.pop();
if (p->rchild != NULL)
st.push(p->rchild);
if (p->lchild != NULL)
st.push(p->lchild);
}
}
三、总结
两种思路都借助了栈,思路一只是借助栈临时存储遍历的变量,
思路二,则利用栈的后进先出的特性,来实现先序遍历。
两种思路在编码实现的时候略有区别,需加注意。
总得来说,理解了先序遍历的过程,就可以顺利地写出上面的代码。