如何实现二叉树的非递归先序、中序、后序遍历(附C++详细实现代码)

二叉树的递归遍历算法是比较容易实现的,那如何实现二叉树的非递归遍历算法呢?

总体具体实现思路:

  • 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;//因为此时已经访问了这个根节点的左子树了
			}
		}
	}
}

初学数据结构不久,有错误欢迎在评论区留言,一起学习!

  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
二叉树的遍历有三种方式:先序遍历中序遍历后序遍历非递归算法实现二叉树的遍历需要借助栈来实现。 以先序遍历为例,具体实现步骤如下: 1. 创建一个栈,将根节点入栈。 2. 当栈不为空时,取出栈顶元素并访问它。 3. 如果该节点有右子树,则将右子树入栈。 4. 如果该节点有左子树,则将左子树入栈。 5. 重复步骤2-4,直到栈为空。 中序遍历后序遍历实现方式类似,只需要在访问节点的时机上稍作调整即可。 下面是先序遍历中序遍历后序遍历非递归算法实现代码: ```c++ // 先序遍历 void preOrder(TreeNode* root) { if (root == nullptr) return; stack<TreeNode*> s; s.push(root); while (!s.empty()) { TreeNode* node = s.top(); s.pop(); visit(node); if (node->right != nullptr) s.push(node->right); if (node->left != nullptr) s.push(node->left); } } // 中序遍历 void inOrder(TreeNode* root) { if (root == nullptr) return; stack<TreeNode*> s; TreeNode* node = root; while (node != nullptr || !s.empty()) { while (node != nullptr) { s.push(node); node = node->left; } node = s.top(); s.pop(); visit(node); node = node->right; } } // 后序遍历 void postOrder(TreeNode* root) { if (root == nullptr) return; stack<TreeNode*> s; TreeNode* node = root; TreeNode* lastVisited = nullptr; while (node != nullptr || !s.empty()) { while (node != nullptr) { s.push(node); node = node->left; } node = s.top(); if (node->right == nullptr || node->right == lastVisited) { visit(node); s.pop(); lastVisited = node; node = nullptr; } else { node = node->right; } } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值