*****一、线索化二叉树:
遍历二叉树是以一定的规则将二叉树中结点排列成一个线性序列,得到二叉树中结点的先序序列或中序序列或后序序列。
这实质上是对一个非线性结构进行线索化操作,使得每个结点(除第一个和最后一个外)在这些线性序列中有且仅有一个直接前驱和直接后继。
但是,当以二叉链表作为存储结构时,只能找到结点的左、右孩子信息,而不能直接得到结点在任意序列中的前驱和后继信息。
规定:若结点有左子树,左指针指向其左孩子,若没有左子树,则指向其前驱;同样的,若结点有右子树,指向其右子树,否则,指向其后继。
以这种结点结构构成的二叉链表作为二叉树的存储结构,叫做线索链表。其中指向结点前驱和后继的指针,叫做线索。
加上线索的二叉树称之为线索二叉树。
为了防止混淆,添加两个标志域,区分当前结点是否有左右子树,是否需要线索化。
线索化其实也就是:在遍历访问的过程中,修改它的空指针(其中第一个结点的前驱和最后一个结点的后继指向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;
}