刷题:二叉树的非递归遍历方式

本篇博客介绍二叉树的非递归遍历方式,需借助栈实现,三种遍历方式算法难易不同。先序遍历较简单,中序遍历稍难,后序遍历最难。文中分别阐述了三种非递归遍历算法的实现思想,帮助读者理解二叉树的非递归遍历。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

二叉树的非递归的遍历方式

上篇博客记录了二叉树的递归遍历方式以及根据二叉树的遍历结果还原二叉树的内容。

本篇博客记录二叉树的非递归的遍历方式。

二叉树的非递归遍历需要借助栈来实现,而且三种遍历的方式的算法难易程度并不相同。其中,先序遍历较为简单,中序遍历稍难,后序遍历最伤脑筋。下面分别说一说三种非递归遍历算法的实现方式。

1. 非递归先序遍历

如上所述,非递归先序遍历方式较为简单。其主要思想是:

  1. 将二叉树的根节点压入栈中。
  2. 获取栈顶元素P并打印其值,做出栈操作。
  3. p的右节点存在,将其压入栈中。
  4. p的左节点存在,将其压入栈中。
  5. 如果栈不为空的话,重复2-4操作。

这种方法说的直白一点就是先读取栈顶节点,然后在将该节点的子节点按照先右后左的方式入栈。这样就保证了在每次循环过程中都能先访问根节点,再以先序的方式遍历左子树和右子树。

void PreTraverseNoRecursion(BiTree T)
{    
    //BiTree  与TreeNode* 是相同类型
	stack<TreeNode*> temp_stack;
	TreeNode* temp_node;
	temp_stack.push(T);
	do 
	{
		temp_node = temp_stack.top();
		cout << temp_node->data << " ";
		temp_stack.pop();

		if (temp_node->Rchild)
			temp_stack.push(temp_node->Rchild);
		if (temp_node->Lchild)
			temp_stack.push(temp_node->Lchild);

	} while (!temp_stack.empty());

}

2.非递归中序遍历

非递归的中序遍历要比先序遍历难一些,主要是不容易思考全面。
非递归中序遍历的主要思想是:

  1. 定义一个结点指针p,使其指向根节点。
  2. p不为空或栈不为空,将根节点和二叉树最左边的各个节点逐个压入栈中。中序遍历必须先访问左子树,因此要把根节点和二叉树最左边的各个节点逐个压入栈中。这个过程能够保证栈顶的节点是整个二叉树最左边最底层的节点,该节点不存在左子树。
  3. 访问栈顶节点q并出栈。
  4. p等于q的右节点。节点q不存在左子树,但不一定不存在右子树。若其存在右子树,令p等于q的右节点,相当于让p作为一颗新二叉树的根节点,重复2-3操作。若q没有右子树,p将访问栈中新的顶节点。
  5. 重复2-4操作。
void MidTraverseNoRecursion(BiTree T)
{
	stack<TreeNode*> temp_stack;
	TreeNode* temp_node = T;
	
	while (temp_node || !temp_stack.empty())
	{
		while (temp_node)
		{
			temp_stack.push(temp_node);
			temp_node = temp_node->Lchild;
		}
		temp_node = temp_stack.top();
		cout << temp_node->data << " ";
		temp_stack.pop();
		temp_node = temp_node->Rchild;
	}
}

3.非递归后序遍历

非递归后序遍历是三种非递归遍历算法中最难的一种,而它难就难在需要另外一个指针来记录上一次访问的节点。
其主要思想是:

  1. 定义两个节点指针cur_node 和last_node,并使他们指向根节点。
  2. 将根节点压入栈中。
  3. 若栈不为空,则令cur_node指向栈顶节点。
  4. 若 (cur_node指向节点没有子节点) 或 (cur_node指向节点的右节点为空 且last_node指向的节点为当前节点的左节点) 或 (last_node指向的节点为当前节点的右节点),则访问该节点并出栈。否则将该节点的子节点按照先右后左的方式入栈。
  5. 重复3-4操作。

该方法不易想到,但是想到后理解起来也很容易。其实它要做的就是先将节点从根节点开始按照先右后左的顺序存入栈中,直到找到最左边最底层的叶节点L。找到该叶节点后对其进行访问,然后令last_node指向它,用已标记,并进行出栈操作,表示完成该节点的访问。若L的父节点存在右节点(即L存在兄弟节点),则此时栈顶元素应该是这个L的兄弟节点(称其为b)。若b无子节点,则对其访问,并用last_node进行标记,然后出栈。否则将其子节点入栈。如果完成了对L 的访问,且b不存在,则访问其父节点,然后出栈。该过程由cur_node ->Rchild == NULL && last_node == cur_node ->Lchild 条件控制。如果完成了对Lb的访问,则访问其父节点,然后出栈。该过程由last_node == cur_node ->Rchild条件控制。

理解清楚之后也不是很难。

void PostTTraverseNoRecursion(BiTree T)
{
	stack<TreeNode*> temp_stack;
	TreeNode* cur_node = T;
	TreeNode* last_node = T;
	temp_stack.push(T);

	while (!temp_stack.empty())
	{
		cur_node = temp_stack.top();
		if ((cur_node ->Lchild == NULL && cur_node ->Rchild == NULL) || (cur_node ->Rchild == NULL && last_node == cur_node ->Lchild) || (last_node == cur_node ->Rchild))
		{
			cout << cur_node ->data << " ";
			last_node = cur_node ;
			temp_stack.pop();
		}
		else
		{
			if (cur_node ->Rchild)
				temp_stack.push(cur_node ->Rchild);
			if (temp_node->Lchild)
				temp_stack.push(cur_node ->Lchild);
		}

	}
}

若有人看到本篇博客,那我就送他一句话:世界上就怕认真二字,格物致知方能有所收获。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值