二叉树的递归遍历算法是比较容易实现的,那如何实现二叉树的非递归遍历算法呢?
总体具体实现思路:
- 1、非递归实现二叉树的遍历我们都借助栈来实现
- 2、借用栈的来实现功能是:我们在进行遍历二叉树的时候,总会先遍历左子树,然后才遍历右子树,借助栈来保存左子树的节点,然后借助栈来从左子树进入右子树。
- 3、然后根据访问根节点的先后顺序来进行具体调整即可。
一、我们从非递归实现中序遍历开始讲起: 因为非递归实现的中序遍历是比较容易理解的
代码实现思路:
- 1、创建一个空栈和一个树指针p指向树的根节点
- 2、将根节点(树的每个节点都可以看做是根节点)压入栈中,并且判断是否有左子树
- 1)、如果有左子树不为NULL、接着将左子树压入栈中
- 2)、如果左子树为NULL、那么将栈顶元素弹出(栈顶元素是最近访问的上一个根节点)。打印栈顶元素,并且访问栈顶元素的右子树。
- 循环停止的条件是:当栈空,并且已经遍历完整棵树之后(即p==NULL) 。
c++实现代码如下:
void traverLDR(TreeNode* root){
if (root == NULL) {
cout << "这是一颗空的二叉树,遍历结果为空" << endl;
return;
}
stack<TreeNode*> s;//创建空栈
TreeNode* p = root;//指向二叉树
while (p != NULL || !s.empty()) {
if (p != NULL) {
//左子树不为空,那么就接着将左子树压入栈中
s.push(p);
p = p->lTree;
}
else {
//左子树为空,将栈顶元素弹出,然后遍历根节点的右子树
p = s.top();
cout << " " << p->value << endl;
s.pop();
p = p->rTree;//指向栈顶元素的右子树
}
}
}
二、然后先说非递归实现的先序遍历
因为后序遍历是这三种遍历中比较复杂的一个,我们放在最后来实现
实现思路:
先序遍历的非递归实现思路和实现中序遍历一样需要借助栈来实现,区别在于访问根节点的时机,先序遍历的遍历是先访问根节点,然后再访问左、右子树,这样的话,我们可以在将根节点压入栈中的时候就打印根节点其他不变即可。
代码实现思路如下:
1、创建一个空栈,然后创建一个树指针p指向根节点
2、将根节点压入栈中,并且将根节点打印出来,并且判断是否其有左子树
- 1)、如果左子树不为NULL,就将左子树压入栈中
- 2)、如果左子树为NULL,我们就将栈顶元素弹出,然后访问栈顶元素的右子树
3、循环结束的条件是:当栈空并且整棵树已经遍历完了(p==NULL)。
c++代码实现如下:
void traverDLR(TreeNode* root) {
if (root == NULL) {
cout << "这是一颗空的二叉树,遍历结果为空" << endl;
return;
}
//创建好空栈
stack<TreeNode*> s;
TreeNode * p = root;//p指向根节点
while (p != NULL || !s.empty()) {
if (p) {
cout <<" "<< p->value << endl;
s.push(p);//将结点压入栈中
p = p->lTree;
}
else {
//如果左子树为空,就弹出栈顶元素,访问其右子树
//或者右子树也为空,说明这个是叶子节点,继续弹栈顶元素,访问根节点
p = s.top();
s.pop();
p = p->rTree;
}
}
}
三、后序遍历的非递归实现
1)、后序遍历相比先序和中序遍历的非递归实现,不一样的地方是:由于是先遍历左右子树,然后再访问根节点,根据上面的思路我们仍然将借助栈来暂时存储左子树的所有节点,借此来进入右子树进行遍历,那么在遍历完左子树或者右子树的时候都是往回退一个节点,我们在遍历完左子树的时候,返回到根节点,这个时候是不需要输出打印根节点的,只有当从根的右子树返回到根节点的时候,这个时候才需要将根节点打印出来。
对于单一的栈来说,是没有办法来实现识别是从左子树返回的还有从右子树返回出来。那么怎么办?
我们可以设置一个标志flag来进行识别,默认值为false,如果是从左子树返回,那么flag值不变,然后如果从右子树返回,那么就将flag值置位true.如果flag为true,在返回根节点(栈顶元素),就将其打印出来。
那么这个flag应该怎么通过代码来实现呢?我们可以在将flag和树节点打包成一个抽象类型,然后栈的栈内元素的类型为该抽象类型。
C++代码实现如下:
//封装flag和树节点
class TreeNodeFlag {
//将树节点和标识位封装起来
public:
bool flag;
TreeNode* treeNode;
TreeNodeFlag(TreeNode* treeNode,bool flag) {
this->flag = false;//默认为false
this->treeNode = treeNode;
}
};
void traverLRD(TreeNode* root) {
if (root == NULL) {
cout << "这是一颗空的二叉树,遍历结果为空" << endl;
return;
}
//创建一个空栈,栈内元素是TreeNodeFlag
stack<TreeNodeFlag> s;
TreeNode* p = root;
while ( p || !s.empty()) {
if (p) {
//由于是将左子树的节点全部压入栈中
TreeNodeFlag temp(p, false);
s.push(temp);
p = p->lTree;
}
else {
if (s.top().flag) {
//说明右子树也访问完了,需要弹出栈顶元素
cout << s.top().treeNode->value << " ";
s.pop();
}
else {
p = s.top().treeNode->rTree;
s.top().flag = true;//因为此时已经访问了这个根节点的左子树了
}
}
}
}
初学数据结构不久,有错误欢迎在评论区留言,一起学习!