#include <iostream>
#include <queue>
#include <stack>
//二叉树的层次,先根,中根,后根遍历
//先根:访问根节点,访问左子树,访问右子树
//中根:访问左子树,访问根节点,访问右子树
//后根:访问左子树,访问右子树,访问根节点
class Node{
public:
int item;
Node* lChild;
Node* rChild;
public:
Node() = default;
explicit Node(int a);
void visit();
};
Node::Node() :item(0) {lChild = rChild = nullptr;}
Node::Node(int a) {item = a;
lChild = rChild = nullptr;
}
//visit代表访问此节点,可以是很多中操作,这里以读取为例
void Node::visit() {printf("%d", item);}
class BinaryTree{
public:
Node* root;
public:
BinaryTree() = default;
BinaryTree(Node *_root);
void levelOrder(Node* _root);
void preOrder(Node* _root);
void inOrder(Node* _root);
void lastOrder(Node* _root);
};
//levelOrder通过一个队列实现,每次从队首取出节点访问,然后将
//非空子节点进队,这样就能完成从上到下,从左到右的访问了
void BinaryTree::levelOrder(Node *_root)
{
using std::queue;
queue<Node*> q;
if(_root == nullptr)
return;
q.push(_root);
Node* pointer = _root;
while(!q.empty())
{
pointer = q.front();
pointer->visit();
q.pop();
if(pointer->lChild)
q.push(pointer->lChild);
if(pointer->rChild)
q.push(pointer->rChild);
}
}
//preOrder通过栈来实现,通过对preOrder递归过程的分析,每次转移
//到一棵子树的时候先访问根节点,然后对根的左子树进行访问,那么右
//子树就需要进栈以便访问完左子树之后再来访问它,。重复上述过程发
//现,实际上访问顺序就是总大树的根节点开始一直往左下角遍历,遍历
//途中访问根并且走到最左边后转到上一级父节点的右子树的根。
void BinaryTree::preOrder(Node *_root)
{
using std::stack;
stack<Node*> s;
if(_root == nullptr)
return;
Node* pointer = _root;
while(!s.empty()||pointer) //当栈空并且pointer也遍历到最右的节点的时候结束循环
{
if(pointer)
{
pointer->visit();
if(pointer->rChild != nullptr)
s.push(pointer);
pointer = pointer->lChild;
}
else
{
pointer = s.top();
s.pop();
}
}
}
//inOrder的访问是先左子树然后根,然后右子树,对于每个根都是这样
//所以从根节点开始需要每次向左下转移,将经过节点压栈,直到无左子
//树,弹栈访问根节点,访问此根的右子树。
void BinaryTree::inOrder(Node *_root)
{
using std::stack;
stack<Node*> s;
if(_root == nullptr)
return;
Node* pointer = _root;
while(!s.empty()||pointer)
{
if(pointer)
{
s.push(pointer);
pointer = pointer->lChild;
}
else
{
pointer = s.top();
pointer->visit();
pointer = pointer->rChild;
s.pop();
}
}
}
//lastOrder的访问顺序是先左子树,后右子树,最后根,对于每个子树也
//是按照这种顺序进行访问,所以需要栈来记录访问顺序,并且在当前节点
//的右子树访问完后再进行访问根节点。
void BinaryTree::lastOrder(Node *_root)
{
using std::stack;
stack<Node*> s;
if(_root == nullptr)
return;
Node* pointer = _root;
Node* prePointer = _root; //prePointer用来记录当前节点的访问前驱
// 在判断是否访问根节点中起作用
while(pointer)
{
for(;pointer->lChild!= nullptr;pointer = pointer->lChild)
s.push(pointer);
while(pointer->rChild == nullptr ||
pointer->rChild == prePointer)
{
//当前节点无右子树或右子树的根被访问完(右子树的根最后访问的)
pointer->visit();
prePointer = pointer;
if(s.empty()) //说明这棵大树的根节点已经被访问
return;
pointer = s.top();
s.pop();
}
s.push(pointer); //把上一过程取出的栈顶节点再压进去并转到右子节点访问
pointer = pointer->rChild;
}
}
//三种算法都是使用线性结构栈或者队列来存储节点信息以及遍历次序关系,在时间和
//空间复杂度上都比较高,并且不能寻找任意节点在某种遍历下的前驱和后继。