【二叉树】二叉树后序线索化以及后序遍历

本文介绍了一种基于二叉树的后序线索化处理方法,包括构建带双亲节点的二叉树结构、后序线索化的实现步骤及遍历算法。通过示例代码展示了如何进行后序线索化及遍历过程,并特别关注了右单支和左单支的情况。

构建节点:多了双亲节点

struct BinaryTreeNodeThd
{
	BinaryTreeNodeThd(const T& data)
	: _data(data)
	, _pLeft(NULL)
	, _pRight(NULL)
	, _pParent(NULL)
	, _LeftThread(LINK)
	, _RightThread(LINK)
	{}
	T _data;
	BinaryTreeNodeThd<T>* _pLeft;
	BinaryTreeNodeThd<T>* _pRight;
	BinaryTreeNodeThd<T>* _pParent;
	Info _LeftThread;
	Info _RightThread;
};

  后序线索化:先线索化左子树 再线索化右子树,最后线索化当前根节点

   直接贴代码

	void PostThread()
	{
		BinaryTreeNodeThd<T>* prev = NULL;
		_PostThread(_pRoot, prev);
	}
	void _PostThread(BinaryTreeNodeThd<T>* pRoot, BinaryTreeNodeThd<T>*& prev)
	{
		if (pRoot)
		{
			_PostThread(pRoot->_pLeft,prev);// 线索化左子树
			_PostThread(pRoot->_pRight,prev); //线索化右子树

			if (pRoot->_pLeft == NULL)
			{
				pRoot->_LeftThread = THREAD;
				pRoot->_pLeft = prev;
			}
			
			if (prev && prev->_pRight == NULL)
			{
				prev->_RightThread = THREAD;
				prev->_pRight = pRoot;
			}
			prev = pRoot;
		}
	}
 

后序遍历线索二叉树:

 思路:(1)找到最左边的节点(分为两种情况 :存在右子树 ,不存在右子树)

    (2)while循环一直遍历节点的后继(prev保存上一次访问的节点)注意左单支

跳出循环条件:当前节点有右子树或者可能到根节点

    (3)跳出循环:判断是否为根节点:如果根节点没有右子树,直接访问return退出

    (4)当前节点不为根节点,循环访问当前节点的双亲节点  注意右单支

    (5)最后判断是否为有右子树,当前节点指向其右子树

  

代码:

 

	void PostOrder()
	{
		_PostOrder(_pRoot);
	}
        void _PostOrder(BinaryTreeNodeThd<T>* pRoot) 
	{
		BinaryTreeNodeThd<T>* pCur = pRoot; 
		BinaryTreeNodeThd<T>* prev = NULL; //保存上一次访问的节点
		while (pCur)
		{
			//找最左边的节点
			while (pCur->_LeftThread == LINK && pCur->_pLeft != prev) //防止陷入死循环  
			{
				pCur = pCur->_pLeft;
			} //跳出循环的条件:pCur为最左边的节点
			
			//访问节点的后继
			while (pCur && THREAD == pCur->_RightThread) // 
			{
				cout << pCur->_data << " ";
				prev = pCur; //perv记录已经访问过的节点
				pCur = pCur->_pRight;
			}//跳出循环的条件:pCur为空(即左单支情况) 节点有右子树,节点为根节点

			//跳出循环,判断是否为根节点
			if (pCur == pRoot && pCur->_pRight == prev)
			{
				cout << pCur->_data << " ";
				return;
			}
			//不是根节点,访问当前节点的双亲节点
			while (pCur && pCur->_pRight == prev) // 注意 右单支情况
			{
				cout << pCur->_data << " ";
				prev = pCur;
				pCur = pCur->_pParent;
			}

			// 判断根节点是否有右子树
			if (pCur && pCur->_RightThread == LINK)
			{
				pCur = pCur->_pRight;
			}
		}
	}

  代码测试分析:

 


  测试代码:

void FunTest2()
{
	char* pTreeInfo = "1245##6#7###3";
	BinaryTreeThd<char> bt(pTreeInfo, strlen(pTreeInfo));
	bt.PostThread();
	bt.PostOrder(); //结果:5764231
}

测试一下特殊的右单支的情况:

        

 分析右单支:注意(3)步

     

测试一下特殊的左单支:所以循环访问后继的时候需要加判断pCur是否为空

 


 

               

### 实现二叉树的先序遍历线索 对于二叉树的先序遍历线索,其核心在于利用原本为空指针的位置存储指向特定节点的信息。具体来说,在先序遍历过程中: - 如果当前节点存在左孩子,则设置该节点的`ltag=0`;否则将其`lchild`设为前驱节点地址,并令`ltag=1`。 - 对于右孩子的处理方式相同,即若有则标记为实际链接(`rtag=0`),无则作为后继节点连接(`rtag=1`)。 此过程通过递归或迭代完成,确保每个节点都能正确指示自己的前后关系[^3]。 ```c++ struct ThreadNode { int data; struct ThreadNode *lChild, *rChild; bool lTag, rTag; // true 表示线索 false表示正常子树 }; void preThread(ThreadNode *&p, ThreadNode *&pre) { if (p != NULL) { if (p->lChild == NULL) { // 左子树为空时建立前驱线索 p->lTag = true; p->lChild = pre; } if (pre && pre->rChild == NULL) { // 右子树为空时建立后继线索 pre->rTag = true; pre->rChild = p; } pre = p; if (!p->lTag) // 若有左子树继续线索单词它 preThread(p->lChild, pre); if (!p->rTag)// 处理右子树 preThread(p->rChild, pre); } } ``` ### 后序遍历线索的实现方法 相较于先序遍历后序遍历的特点决定了其实现逻辑有所不同。因为后序遍历时最后才访问根节点,所以在构建线索的过程中需特别注意保持正确的访问次序。通常情况下,会优先考虑左右子树再回头处理父节点的关系[^4]。 为了简描述,这里给出一种基于栈的数据结构辅助下的非递归版本来说明如何创建后序遍历的线索链表: ```cpp // 定义一个用于保存节点及其状态(是否已处理过)的类 class StackItem { public: ThreadNode* node; bool visited; StackItem(ThreadNode *_node): node(_node), visited(false){} }; vector<StackItem> stack; ThreadNode *prev = nullptr; while(!stack.empty() || curr){ while(curr){ stack.push_back(StackItem(curr)); curr = curr->left ? curr->left : curr->right; } auto &top = stack.back(); if(top.visited){ top.node->rTag = !top.node->right; top.node->rChild = prev; prev = top.node; stack.pop_back(); }else{ top.visited = true; if(stack.size()>1) ((stack.end()-2)->node)->rTag=true, ((stack.end()-2)->node)->rChild=top.node; if(!top.node->left && !top.node->right){ top.node->lTag=true; top.node->lChild=((stack.size()==1)?nullptr:(stack.end()-2))->node; } curr = top.node->right ? top.node->right : top.node->left; } } if(prev){ prev->rTag=false; prev->rChild=nullptr; } ``` 上述代码片段展示了如何使用栈来进行后序遍历的同时完成线索的工作。需要注意的是,这段程序假设输入是一棵完整的二叉树,并且所有节点都遵循相同的定义模式。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值