绪论
本文主要就二叉树的前、中、后序遍历的非递归实现进行一个总结,采取的主要方式为栈(此处会用到C++的STL库)。
一、前序遍历
I.思路
1.构建一个栈
2.迭代至树的最左侧,并将其入栈,打印栈顶元素
3.出栈,指向右孩子
II.代码
void PreOrderTraversal(TreeNode* BT)
{
stack<TreeNode*>st; //此处用到了C++STL库,构建一个储存树结点的栈
while(BT||!st.empty()) //BT不为空或栈不为空时进入循环
{
while(BT) //迭代至最左边并入栈
{
st.push(BT);
cout<<BT->val; //打印栈顶结点
BT=BT->left;
}
//循环结束后BT指向NULL
//此处有些资料加上判断if(!st.empty()),实际上可省略
BT=st.top();
st.pop(); //接受栈顶结点并出栈
BT=BT->right; //若无右孩子,指向NULL;否则指向其右孩子
}
二、中序遍历
I.思路
1.构建一个栈
2.迭代至树的最左侧,并将其入栈
3.出栈,打印栈顶元素,指向右孩子
II.代码
void InOrderTraversal(TreeNode* BT)
{
stack<TreeNode*>st; //构建一个栈
while(BT||!st.empty()) //BT不为空或栈不为空时进入循环
{
while(BT) //迭代至最左边并入栈
{
st.push(BT);
BT=BT->left;
}
BT=st.top(); //接收栈顶结点,并出栈
st.pop();
cout<<BT->val; //打印栈顶结点
BT=BT->right; //指向右孩子
}
}
三、后序遍历
I.思路
1.构建一个栈,创建一个标记变量,用于判断右子树是否已被访问(若某结点被标记,则其自身和其右子树都已经被访问)
2.迭代至树的最左侧,并将其入栈
3.接收栈顶结点,并进行判断
4.若当前结点的右孩子为空或被标记,访问打印当前结点,出栈,并将指针置空,使其再次执行步骤3;否则指向右孩子
II.代码
void PostOrderTraversal(TreeNode* BT)
{
stack<TreeNode*>st;
TreeNode* tag=NULL; //此处需要增加一个tag变量,判断结点及其右子树是否已经被访问
while(BT||!st.empty())
{
while(BT) //迭代至最左边并入栈
{
st.push(BT);
BT=BT->left;
}
BT=st.top(); //接受栈顶结点,但并没有出栈
if(!BT->right||BT->right==tag) //若栈顶结点的右孩子为空或被标记
{
tag=BT; //标记当前结点,表明当前结点及其右子树已被访问完全
cout<<BT->val; //访问当前结点的值
st.pop(); //将当前结点出栈
BT=NULL; //将BT指向NULL,使下次循环继续接收新的栈顶结点
}
else //若栈顶结点的右孩子不为空或未被标记
{
BT=BT->right; //指向其右孩子
}
}
}
四、总结
1.对于前序遍历和中序遍历,我们可以看到它们的代码非常的相似,区别在于前序遍历是在结点入栈的时候打印数据;而中序遍历是在结点出栈的时候打印数据。
2.对于后序遍历,则需要判断当前结点的右子树是否遍历完全,分而治之的进行讨论
尾声
由于是第一次发博客,很多东西可能整理的不到位,如有错误,欢迎各位大牛指正,谢谢!