二叉树的非递归遍历(前序遍历、中序遍历、后序遍历)
前言
之前写过二叉树的递归遍历,今天在这里介绍一下二叉树的非递归遍历,其中前序遍历和中序遍历较为简单,但是后序遍历需要更多的时间去理解,可以多看看代码,举一两个实例好好的理解一下,这里我会附上源码(完整),可以直接使用,方便一起学习。
一、前序遍历(非递归)
void PrintQ(Tree* tree) {
stack<Tree*> s;
while (tree || !s.empty()) {
if (tree != NULL) { //当节点不为空时
cout << tree->date << " "; //输出这个节点(前序输出,先输出,再遍历)
s.push(tree); //把当前节点压进去
tree = tree->left; //节点向左孩子前进
}
else { //当节点为空,证明左孩子或者右孩子为空
tree = s.top(); //所以需要走回上一个节点,继续找他的右孩子
s.pop(); //走到这里就证明他所有的左孩子必定都走完了,所以可以找右孩子了
tree = tree->right;
}
}
}
二、中序遍历(非递归)
void PrintZ(Tree *tree) {
stack<Tree*> s;
while (tree || !s.empty()) {
if (tree != NULL) { //当节点不为空时
s.push(tree); //把当前节点压进去
tree = tree->left; //节点向左孩子前进
}
else { //当节点为空,证明左孩子或者右孩子为空
tree = s.top(); //所以需要走回上一个节点,继续找他的右孩子
s.pop(); //走到这里就证明他所有的左孩子必定都走完了,所以可以找右孩子了
cout << tree->date << " "; //输出这个节点(中序输出,先遍历左,再输出,再遍历右)
tree = tree->right;
}
}
}
三、后序遍历(非递归)
void PrintH(Tree* tree) {
stack<Tree*> s;
Tree* r = NULL;
while (tree || !s.empty()) {
if (tree != NULL) { //当节点不为空时
s.push(tree); //把当前节点压进去
tree = tree->left; //节点向左孩子前进
}
else { //当节点为空,证明左孩子或者右孩子为空
tree = s.top(); //所以需要走回上一个节点,继续找他的右孩子
if (tree->right != NULL && tree->right != r) { //没有右孩子与右孩子没有走过
tree = tree->right; //走到右孩子
s.push(tree); //把右孩子放入栈中
tree = tree->left; //找右孩子的左子树
}
else {
s.pop(); //走到这里就证明他所有的都走完了
cout << tree->date << " "; //输出这个节点(后序输出,先遍历左,再遍历右,再输出)
r = tree; //r记录这个节点已经被访问,防止回到上一个节点后继续找到这个节点导致进入死循环
tree = NULL; //这里tree再次走到条件else(左右孩子都已经走完,需要重新走回到上一个节点)
}
}
}
}
四、完整代码
#include <iostream>
#include<iostream>
#include<stack>
using namespace std;
struct Tree
{
int date;
Tree* left, * right;
};
void Insert(Tree *& tree ,int date) {
tree = (Tree*)malloc(sizeof(Tree));
tree->date = date;
tree->left = NULL;
tree->right = NULL;
}
void PrintQ(Tree* tree) {
stack<Tree*> s;
while (tree || !s.empty()) {
if (tree != NULL) { //当节点不为空时
cout << tree->date << " "; //输出这个节点(前序输出,先输出,再遍历)
s.push(tree); //把当前节点压进去
tree = tree->left; //节点向左孩子前进
}
else { //当节点为空,证明左孩子或者右孩子为空
tree = s.top(); //所以需要走回上一个节点,继续找他的右孩子
s.pop(); //走到这里就证明他所有的左孩子必定都走完了,所以可以找右孩子了
tree = tree->right;
}
}
}
void PrintZ(Tree *tree) {
stack<Tree*> s;
while (tree || !s.empty()) {
if (tree != NULL) { //当节点不为空时
s.push(tree); //把当前节点压进去
tree = tree->left; //节点向左孩子前进
}
else { //当节点为空,证明左孩子或者右孩子为空
tree = s.top(); //所以需要走回上一个节点,继续找他的右孩子
s.pop(); //走到这里就证明他所有的左孩子必定都走完了,所以可以找右孩子了
cout << tree->date << " "; //输出这个节点(中序输出,先遍历左,再输出,再遍历右)
tree = tree->right;
}
}
}
void PrintH(Tree* tree) {
stack<Tree*> s;
Tree* r = NULL;
while (tree || !s.empty()) {
if (tree != NULL) { //当节点不为空时
s.push(tree); //把当前节点压进去
tree = tree->left; //节点向左孩子前进
}
else { //当节点为空,证明左孩子或者右孩子为空
tree = s.top(); //所以需要走回上一个节点,继续找他的右孩子
if (tree->right != NULL && tree->right != r) { //没有右孩子与右孩子没有走过
tree = tree->right; //走到右孩子
s.push(tree); //把右孩子放入栈中
tree = tree->left; //找右孩子的左子树
}
else {
s.pop(); //走到这里就证明他所有的都走完了
cout << tree->date << " "; //输出这个节点(后序输出,先遍历左,再遍历右,再输出)
r = tree; //r记录这个节点已经被访问,防止回到上一个节点后继续找到这个节点导致进入死循环
tree = NULL; //这里tree再次走到条件else(左右孩子都已经走完,需要重新走回到上一个节点)
}
}
}
}
int main()
{
Tree* tree = NULL;
Insert(tree, 1);
Insert(tree->left, 2);
Insert(tree->right, 3);
Insert(tree->left->left, 4);
Insert(tree->left->right, 5);
Insert(tree->right->left, 6);
Insert(tree->right->right, 7);
PrintQ(tree);
cout << endl;
PrintZ(tree);
cout << endl;
PrintH(tree);
}
总结
非递归中的后序遍历是最复杂的,需要判断右子树是否为空,需要好好的理解。