线索化二叉树、线索二叉树的遍历

*****一、线索化二叉树:

遍历二叉树是以一定的规则将二叉树中结点排列成一个线性序列,得到二叉树中结点的先序序列或中序序列或后序序列。

这实质上是对一个非线性结构进行线索化操作,使得每个结点(除第一个和最后一个外)在这些线性序列中有且仅有一个直接前驱和直接后继。

但是,当以二叉链表作为存储结构时,只能找到结点的左、右孩子信息,而不能直接得到结点在任意序列中的前驱和后继信息。

规定:若结点有左子树,左指针指向其左孩子,若没有左子树,则指向其前驱;同样的,若结点有右子树,指向其右子树,否则,指向其后继。

以这种结点结构构成的二叉链表作为二叉树的存储结构,叫做线索链表。其中指向结点前驱和后继的指针,叫做线索。

加上线索的二叉树称之为线索二叉树。

为了防止混淆,添加两个标志域,区分当前结点是否有左右子树,是否需要线索化。

线索化其实也就是:在遍历访问的过程中,修改它的空指针(其中第一个结点的前驱和最后一个结点的后继指向NULL)。(把它所有的指针域都利用上)

因此,线索化也有三种方式喽!先序、中序、后序线索化。(当然线索化二叉树之前要先创建二叉树)


*****二、线索二叉树的遍历

线索二叉树的遍历:也就是在遍历访问的过程中,访问每一个节点。若结点有左子树,左指针指向其左孩子,访问左孩子,若没有左子树,则指向其前驱,访问其前驱;同样的,若结点有右子树,指向其右子树,访问右孩子,否则,指向其后继,访问其后继。

重要的是:二叉树是要用哪种方式线索化的,就要用哪种方式去遍历线索化后的二叉树,它是一一对应的。

但是无论是线索化二叉树还是线索之后的遍历线索二叉树,都首先要创建二叉树。

而二叉树的创建对应三种方式,无论选择哪种方式都是可以的,它并不影响后面的线索化以及遍历线索化二叉树。

下面直接给出二叉树的线索化、遍历线索二叉树的代码:

//线索化二叉树:先要创建二叉树(此处选用先序),在线索化(在遍历的过程中修改二叉树的空指针);
#include<iostream>
using namespace std;
#pragma once

//新创建的二叉树中的节点,每一个节点都是一个结构体
template<class T>
struct BinaryTreeThdNode
{
	BinaryTreeThdNode(const T& data)
    : _data(data)
	, _pLeft(NULL)
	, _pRight(NULL)
	, _pParent(NULL)
	, _LeftThdFlag(LINK)//有左孩子
	, _RightThdFlag(LINK)//有右孩子
	{}
	T _data;
	BinaryTreeThdNode<T>* _pLeft;
	BinaryTreeThdNode<T>* _pRight;
	BinaryTreeThdNode<T>* _pParent;
	int _LeftThdFlag;
	int _RightThdFlag;
};

//创建二叉树(先序):
template<class T>
class BinaryTreeThd
{
public:
	BinaryTreeThd()//构造函数(创建一个空树)
		:_pRoot(NULL)
	{}
	BinaryTreeThd(const T& arr[], size_t size)//构造函数
	{
		size_t index = 0;
		_CreatTree(_pRoot, arr, size, index);
	}
	
	~BinaryTreeThd() //析构函数
	{
		_DestoryTree(_pRoot);
	}


	//二叉树的线索化(先序) 根 左 右
	void preTheading()
	{
		BinaryTreeThdNode<T>* prev = NULL;
		_preTheading(_pRoot, prev);
	}
	void _preTheading(BinaryTreeThdNode<T>* pRoot, BinaryTreeThdNode<T>* prev)
	{
		//prev = NULL;
		if (pRoot)
		{
			if ( NULL==pRoot->_pLeft )//没有左孩子
			{
				pRoot->_LeftThdFlag = THREAD;
				pRoot->_pLeft = prev;
			}
			if (prev&&NULL == prev->_pRight)
			{
				prev->_RightThdFlag = THREAD;
				prev->_pRight = prev;
			}
			prev = pRoot;
			if (LINK == _pRoot->_LeftThdFlag)
			{
                _preTheading(pRoot->_pLeft, perv);
			}
			if (LINK == _pRoot->_RightThdFlag)
			{
                _preTheading(pRoot->_pRight, prev);
			}
			
		}
   }
	
	//(先序)遍历一个(先序)线索化后的二叉树  根 左 右
	void PreOrder()
	{
		_PreOrder(_pRoot);
	}
	void _PreOrder(BinaryTreeThdNode<T>* pRoot)
	{
		BinaryTreeThdNode<T>* pCur = pRoot;
		
		while (pCur)
		{
			///方法一:
			//while (THREAD == pCur->_RightThdFlag)//没有右孩子,访问前一个的后继,访问连续的后继
			//{
			//	pCur = pCur->_pRight;
			//	cout << pCur->_data << " ";
			//}
			//if (LINK == pCur->_LeftFlag)
			//{
			//	pCur = pCur->_pLeft;
			//}
			//else
			//{
			//	pCur = pCur->_pRight;
			//}
			///方法二:
			//if (THREAD == pCur->_LeftThdFlag)
			//{
			//	pCur = pCur->_pRight;
			//	cout << pCur->_data << " ";
			//}
			//if (LINK == pCur->_LeftThdFlag)
			//{
			//	pCur = pCur->_pLeft
			//}
			//else
			//{
			//	pCur = pCur->_pRight;
			//}


			/方法三:(最优的方法)
			//找最左边的节点,并访问路径上所经过的节点
			while (LINK == pCur->_LeftThdFlag)//有左孩子
			{
				cout << pCur->_data << " ";
				pCur = pCur->_pLeft;
			}
			cout << pCur->_data << " ";
			pCur = pCur->_pRight;
		}	
	}


	//(中序)遍历一个(中序)线索化后的二叉树(二叉树已经创建,第一步:中序线索化二叉树,中序访问线索化后的二叉树)
	
	//中序线索化二叉树 左 根 右 
	void InThreading()
	{
		BinaryTreeThdNode<T>* prev = NULL;
		_InThreading(_pRoot, prev);
	}
	void _InThreading(BinaryTreeThdNode<T>* pRoot, BinaryTreeThdNode<T>* prev)//标记上次访问的节点
	{
		if (pRoot)
		{
			_InThreading(pRoot->_pLeft, prev);//找最左边的孩子
			if (NULL == pRoot->_pLeft)//没有左孩子,就线索化。(处理当前孩子的左指针域)
			{
				pRoot->_LeftThdFlag = THREAD;
				pRoot->_pLeft = prev;
			}
			if (prev&&NULL == prev->_pRight)//Q没有右孩子,就线索化。(处理上一个节点的右指针域)
			{
				pRoot->_RightThdFlag = THREAD;
				prev->_pRight = pRoot;
			}
			prev = pRoot;
			if (LINK == pRoot->_RightThdFlag)
			{
				_InThreading(pRoot->_pRight, prev);
			}
		}
	}
	
	//中序访问线索化后的二叉树  左 根 右 
	void InOrder()
	{
		_InOrder(_pRoot);
	}
	void _InOrder(BinaryTreeThdNode<T>* pRoot)
	{
		BinaryTreeThdNode<T>* pCur = pRoot; //指向当前节点
		while (pCur)
		{
			while (LINK == pRoot->_LeftThdFlag)//有左孩子,找最左边的孩子,并访问最左边的孩子
			{
				pCur = pCur->_pLeft;
			}
			cout << pCur->_data << " ";
			while (THREAD==pCur->_RightThdFlag)//当前节点已经是最左边的节点。当前节点没有右孩子,访问它的后继以及后继的后继
			{
				pCur = pCur->_pRight;
				pCur = pCur->_pRight;
				cout << pCur->_data << " ";
				
			}
			else//当前节点有右孩子
			{
               pCur = pCur->_pRight;
			}
		}
	}
	//后序线索化二叉树:
	void PostTheading()
	{
		BinaryTreeThdNode<T>* prev = NULL;//线索化的时候用来标记刚刚访问过的节点
		_PostTheading(_pRoot, prev);
	}
	void _PostThreading(BinaryTreeThdNode<T>* pRoot, BinaryTreeThdNode<T>* prev)
	{
		if (pRoot)
		{
			_PostThreading(pRoot->_pLeft, prev);
			_PostThreading(pRoot->_pRight, prev);
			if (NULL == pRoot->_Left)
			{
				pRoot->_pLeft = prev;
				pRoot->_LeftThdFlag = THREAD;
			}
			if (prev&&NULL == pRoot->_pRight)
			{
				prev->_pRight = pRoot;
				prev->_RightThdFlag = THREAD;
			}
			prev = pRoot;
		}
	}
	
	
	//后序访问后序线索化的二叉树:
	void PostOrder()
	{
		BinaryTreeThdNode<T>* prev=NULL;
		_PostOrder(_pRoot,prev);
	}
	void _PostOrder(BinaryTreeThdNode<T>* pRoot,BinaryTreeThdNode<T>* prev)
	{
		BinaryTreeThdNode<T>* pCur = pRoot;//用来标记当前结点
		while (pCur)
		{
			while (LINK == pCur->_LeftThdFlag)//有左孩子,找最左边的
			{
				pCur = pCur->_pLeft;
			}
			while (pCur&&THREAD == pCur->_RightThdFlag)
			{
				cout << pCur->_data << " ";
				prev = pCur;
				pCur = pCur->_pRight;
			}
			if (pCur == pRoot)
			{
				cout << pCur->_data << " ";
                return;
			}
			while (pCur && pCur->pRight == prev)
			{
				cout << pCur->_data << " ";
				prev = pCur;
				pCur = pCur->_pParent;
			}
			if (pCur && pCur->RightFlag == Link)
			{
				pCur = pCur->pRight;
			}
		}
	}
	

private:
	void _CreatTree(BinaryTreeThdNode<T>*& pRoot,const T& arr[],size_t size,size_t& index)
	{
		if (index < size && arr[index] != "#")
		{
			pRoot = new BinaryTreeThdNode<T>* (arr[index])//开辟空间,先创建好根节点
			_CreatTree(pRoot->_pLeft ,arr,size,++index);
			_CreatTree(pRoot->_pRight , arr, size, ++index);
		}
	}
	void _DestroyTree(BinaryTreeThdNode<T>*& pRoot)
	{
		if (pRoot)
		{
			_DestroyTree(pRoot->_pLeft);
			_DestroyTree(pRoot->_pRight);
			delete(pRoot);
			pRoot = NULL;
		}
	}
	//拷贝构造函数
	BinaryTreeThd(const BinaryTreeThd<T>& t);

	//赋值运算符重载 
	BinaryTreeThd<T>& operator=(const BinaryTreeThd<T>& t);

private:
    BinaryTreeThdNode<T>* _pRoot;

};
int main()
{
	char arr[] = { '0', '1', '3', '#', '#', '4', '#', '#', '2', '5' };
	BinaryTreeThd<char> t(arr, sizeof(arr) / sizeof(arr[0]));
	//t._preTheading();
	t.PostOrder();
	return 0;
}



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值