测试环境:VS2010
一、线索二叉树的引入
二叉树是一种非线性结构,遍历二叉树几乎都是通过递归或者用栈辅助实现非递归的遍历。用二叉树作为存储结构时,取到一个节点,只能获取节点的左孩子和右孩子,不能直接得到节点的任一遍历序列的前驱或者后继。
为了保存这种在遍历中需要的信息,我们利用二叉树中指向左右子树的空指针来存放节点的前驱和后继信息。
二、线索二叉树的构造
线索二叉树的节点结构:
线索二叉树的节点定义:
enum PointTag
{
LINK,//子树
THREAD,//线索
};
template <class T>
struct BinaryTreeThdNode
{
T _data;//节点数据
BinaryTreeThdNode<T>* _left;//当前节点的左孩子
BinaryTreeThdNode<T>* _right;//当前节点的右孩子
PointTag _leftTag;//当前节点的左孩子线索标志
PointTag _rightTag;//当前节点的右孩子线索标志
};
当_leftTag == LINK,则表示_left为指向节点的左孩子,_leftTag == THEAD,则表示_left为线索,指向节点的直接前驱。
当_rightTag == LINK,则表示_right为指向节点的右孩子,_rightTag == THEAD,则表示_right为线索,指向节点的直接后继。
三、代码实现二叉树的线索化
线索化二叉树可分为前序线索二叉树、中序线索二叉树、后序线索二叉树。
1、前序线索二叉树
代码实现:
1>前序遍历的线索化
void _PrveOrderThreading(Node* cur,Node*& prev)//前序遍历的线索化
{
if (NULL == cur)
{
return;
}
//线索化根节点
if (NULL == cur->_left)//找到最左节点,
{
cur->_left = prev; //将其左指针指向该节点的直接前驱节点
cur->_leftTag = THREAD; //将其标志位置为THEAD
}
if (prev && NULL == prev->_right)//找到最右节点,
{
prev->_right = cur; //将其右指针指向该节点的直接后继节点
prev->_rightTag = THREAD; //将其标志位置为THEAD
}
prev = cur; //前一个节点后移为当前节点
//线索化左子树
if (cur->_leftTag == LINK)
{
_PrveOrderThreading(cur->_left,prev);
}
//线索化右子树
if (cur->_rightTag == LINK) //递归访问右子树
{
_PrveOrderThreading(cur->_right,prev);
}
}
2>通过线索化前序遍历二叉树
void PrevOrderThd()//通过线索化前序遍历二叉树
{
Node* cur = _node;
while (cur)
{
while(cur->_leftTag == LINK)
{
cout<<cur->_data<<" ";//访问当前节点
cur = cur->_left;
}
cout<<cur->_data<<" ";//访问最左节点
//访问整棵树的右子树(下面的方法也可以)
cur = cur->_right;
//访问当前节点的右子树
//while (cur && cur->_rightTag == THREAD//当前节点的右指针为线索
//{
// cur = cur->_right;
// if(cur)
// {
// cout<<cur->_data<<" ";
// }
//}
子问题,右是子树,子问题处理
//if (cur)//cur->_rightTag == LINK(当前节点的右指针为子树)
//{
// cur = cur->_right;
//}
}
cout<<endl;
}
2、中序线索二叉树
代码实现:1>中序遍历的线索化
void _InOrderThreading(Node* cur,Node*& prev)//中序遍历的线索化
{
if (NULL == cur)
{
return;
}
//线索化左子树
if (cur->_leftTag == LINK)
{
_InOrderThreading(cur->_left,prev);
}
//线索化根节点
if (NULL == cur->_left)
{
cur->_left = prev;
cur->_leftTag = THREAD;
}
if (prev && NULL == prev->_right)
{
prev->_right = cur;
prev->_rightTag = THREAD;
}
prev = cur;
//线索化右子树
if (cur->_rightTag == LINK)
{
_InOrderThreading(cur->_right,prev);
}
}
void InOrderThd()//通过线索化中序遍历二叉树
{
Node* cur = _node;
while (cur)
{
while(cur->_leftTag == LINK)//寻找最左节点
{
cur = cur->_left;
}
cout<<cur->_data<<" ";//访问最左节点
while (cur && cur->_rightTag == THREAD)//当前节点的右指针为线索
{
cur = cur->_right;
if(cur)
{
cout<<cur->_data<<" ";
}
}
//子问题,右是子树,子问题处理
if (cur)//cur->_rightTag == LINK(当前节点的右指针为子树)
{
cur = cur->_right;
}
}
cout<<endl;
}
3、后序线索二叉树
思路分析:与前面两种遍历算法思想类似,只不过我们遇到一个节点之后不能立即访问,必须首先访问其左右子树,然后返回父节点进行访问,可是查找当前节点的父亲还是挺麻烦的的,在这提供两种思路:
1>封装一个查找函数(Find),专门用来查找当前节点的父亲,如此一来,访问到每个节点我们都得递归查找它的父亲节点,线索化的目的就是为了快速遍历,减少递归带来的效率低下的问题,所以说得不偿失,在这我只给出其
基本思想,有兴趣的下去可以自己实现,重点实现一下第二种思路。
2>将树构建为三叉链,增加一个指向节点双亲的指针。
1>封装一个查找函数(Find),专门用来查找当前节点的父亲,如此一来,访问到每个节点我们都得递归查找它的父亲节点,线索化的目的就是为了快速遍历,减少递归带来的效率低下的问题,所以说得不偿失,在这我只给出其
基本思想,有兴趣的下去可以自己实现,重点实现一下第二种思路。
2>将树构建为三叉链,增加一个指向节点双亲的指针。
节点结构:
template <class T>
struct BinaryTreeThdNode
{
BinaryTreeThdNode(const T& x)
:_data(x)
,_left(NULL)
,_right(NULL)
,_parent(NULL)
,_leftTag(LINK)
,_rightTag(LINK)
{}
T _data;//节点数据
BinaryTreeThdNode<T>* _left;//当前节点的左孩子
BinaryTreeThdNode<T>* _right;//当前节点的右孩子
BinaryTreeThdNode<T>* _parent;//当亲节点的双亲
PointTag _leftTag;//当前节点的左孩子线索标志
PointTag _rightTag;//当前节点的右孩子线索标志
};
构造二叉树:
Node* _CreatBinaryTreeThd(const T* a,size_t n,const T& invalid,size_t& index,Node*& parent)
{
Node* root = NULL;
if (a[index] != invalid && index < n)
{
root = new Node(a[index]);
root->_parent = parent;
root->_left = _CreatBinaryTreeThd(a,n,invalid,++index,root);
root->_right = _CreatBinaryTreeThd(a,n,invalid,++index,root);
}
return root;
}
后序遍历的线索化:
void _PostOrderTheading(Node* cur,Node*& prev)//后序遍历的线索化
{
if (NULL == cur)
{
return;
}
//线索化左子树
if (cur->_leftTag == LINK) //防止栈溢出
{
_PostOrderTheading(cur->_left,prev);
}
//线索化右子树
if (cur->_rightTag == LINK) //防止栈溢出
{
_PostOrderTheading(cur->_right,prev);
}
//线索化根节点
if (NULL == cur->_left)//找到最左节点,
{
cur->_left = prev; //将其左指针指向该节点的直接前驱节点
cur->_leftTag = THREAD; //将其标志位置为THEAD
}
if (prev && NULL == prev->_right)//找到最右节点,
{
prev->_right = cur; //将其右指针指向该节点的直接后继节点
prev->_rightTag = THREAD; //将其标志位置为THEAD
}
prev = cur; //前一个节点后移为当前节点
}
通过线索化后序遍历二叉树:
void PostOrderThd()//通过线索化后序遍历二叉树
{
Node* cur = _node;
Node* prev = NULL;
while (cur)
{
//寻找最左节点
while(cur->_left != prev && cur->_leftTag == LINK)
{
cur = cur->_left;
}//cur==_node或NULL结束循环
//访问后继
while (cur && cur->_rightTag == THREAD)//当前节点的右指针为线索
{
cout<<cur->_data<<" ";
prev = cur;
cur = cur->_right;
}
if (cur == _node)//判断节点是否指向了根节点
{
cout<<cur->_data<<endl;
return;
}
while (cur && cur->_right == prev)
{
cout<<cur->_data<<" ";
prev = cur;
cur = cur->_parent;//向上一层走
}
if (cur && cur->_rightTag == LINK)//cur->_rightTag == LINK(当前节点的右指针为子树)
{
cur = cur->_right;
}
}
}
四、通过迭代器完成对线索二叉树的访问与测试
中序迭代器的实现代码:
template <class T,class Ref,class Ptr>
struct BinaryTreeIterator
{
typedef BinaryTreeThdNode<T> Node;
typedef BinaryTreeIterator<T,Ref,Ptr> Self;
BinaryTreeIterator(Node* node)
:_node(node)
{}
Ref operator*()
{
return _node->_data;
}
Ptr operator->()
{
return &(operator*());
}
Self& operator++()
{
_node = _Next(_node);
return *this;
}
Self operator++(int)
{
Self tmp(*this);
_node = _Next(_node);
return tmp;
}
Self& operator--()
{
_node = _Prev(_node);
return *this;
}
Self operator--(int)
{
Self tmp(*this);
_node = _Prev(_node);
return tmp;
}
bool operator!=(const Self& s)const
{
return _node != s._node;
}
bool operator==(const Self& s)const
{
return _node == s._node;
}
protected:
Node* _Next(Node* node)//找中序遍历的下一个节点
{
assert(node);
if (node->_rightTag == THREAD)
{
return node->_right;
}
else
{
Node* cur = node->_right;
while (cur && cur->_leftTag == LINK)
{
cur = cur->_left;
}
return cur;
}
}
Node* _Prev(Node* node)//找中序遍历的前一个节点
{
if (node->_leftTag == THREAD)
{
return node->_left;
}
else
{
Node* cur = node->_left;
while (cur && cur->_rightTag == LINK)
{
cur = cur->_right;
}
return cur;
}
}
protected:
Node* _node;
};
五、完整代码
#pragma once enum PointTag { LINK,//子树 THREAD,//线索 }; template <class T> struct BinaryTreeThdNode { BinaryTreeThdNode(const T& x) :_data(x) ,_left(NULL) ,_right(NULL) ,_parent(NULL) ,_leftTag(LINK) ,_rightTag(LINK) {} T _data;//节点数据 BinaryTreeThdNode<T>* _left;//当前节点的左孩子 BinaryTreeThdNode<T>* _right;//当前节点的右孩子 BinaryTreeThdNode<T>* _parent;//当亲节点的双亲 PointTag _leftTag;//当前节点的左孩子线索标志 PointTag _rightTag;//当前节点的右孩子线索标志 }; template <class T,class Ref,class Ptr> struct BinaryTreeIterator { typedef BinaryTreeThdNode<T> Node; typedef BinaryTreeIterator<T,Ref,Ptr> Self; BinaryTreeIterator(Node* node) :_node(node) {} Ref operator*() { return _node->_data; } Ptr operator->() { return &(operator*()); } Self& operator++() { _node = _Next(_node); return *this; } Self operator++(int) { Self tmp(*this); _node = _Next(_node); return tmp; } Self& operator--() { _node = _Prev(_node); return *this; } Self operator--(int) { Self tmp(*this); _node = _Prev(_node); return tmp; } bool operator!=(const Self& s)const { return _node != s._node; } bool operator==(const Self& s)const { return _node == s._node; } protected: Node* _Next(Node* node)//找中序遍历的下一个节点 { assert(node); if (node->_rightTag == THREAD) { return node->_right; } else { Node* cur = node->_right; while (cur && cur->_leftTag == LINK) { cur = cur->_left; } return cur; } } Node* _Prev(Node* node)//找中序遍历的前一个节点 { if (node->_leftTag == THREAD) { return node->_left; } else { Node* cur = node->_left; while (cur && cur->_rightTag == LINK) { cur = cur->_right; } return cur; } } protected: Node* _node; }; template <class T> class BinaryTreeThd { public: typedef BinaryTreeThdNode<T> Node; typedef BinaryTreeIterator<T,T&,T*> Iterator; typedef BinaryTreeIterator<const T,const T&,const T*> ConstIterator; Iterator Begin() { Node* cur = _node; while(cur->_leftTag == LINK) { cur = cur->_left; } return Iterator(cur); } Iterator End() { return NULL; } public: BinaryTreeThd()//无参构造函数 :_node(NULL) {} BinaryTreeThd(T* a,size_t n,const T& invalid)//构造函数 { size_t index = 0; Node* parent = NULL; _node = _CreatBinaryTreeThd(a,n,invalid,index,parent); } void PrveOrderThread()//前序遍历的线索化 { Node* prev = NULL; _PrveOrderThreading(_node,prev); } void PrevOrderThd()//通过线索化前序遍历二叉树 { Node* cur = _node; while (cur) { while(cur->_leftTag == LINK) { cout<<cur->_data<<" ";//访问当前节点 cur = cur->_left; } cout<<cur->_data<<" ";//访问最左节点 //访问整棵树的右子树(下面的方法也可以) cur = cur->_right; //访问当前节点的右子树 //while (cur && cur->_rightTag == THREAD//当前节点的右指针为线索 //{ // cur = cur->_right; // if(cur) // { // cout<<cur->_data<<" "; // } //} 子问题,右是子树,子问题处理 //if (cur)//cur->_rightTag == LINK(当前节点的右指针为子树) //{ // cur = cur->_right; //} } cout<<endl; } void InOrderThread()//中序遍历的线索化 { Node* prev = NULL; _InOrderThreading(_node,prev); } void InOrderThd()//通过线索化中序遍历二叉树 { Node* cur = _node; while (cur) { while(cur->_leftTag == LINK)//寻找最左节点 { cur = cur->_left; } cout<<cur->_data<<" ";//访问最左节点 while (cur && cur->_rightTag == THREAD)//当前节点的右指针为线索 { cur = cur->_right; if(cur) { cout<<cur->_data<<" "; } } //子问题,右是子树,子问题处理 if (cur)//cur->_rightTag == LINK(当前节点的右指针为子树) { cur = cur->_right; } } cout<<endl; } void PostOrderThead()//后序遍历的线索化 { Node* prev = NULL; _PostOrderTheading(_node,prev); } void PostOrderThd()//通过线索化后序遍历二叉树 { Node* cur = _node; Node* prev = NULL; while (cur) { //寻找最左节点 while(cur->_left != prev && cur->_leftTag == LINK) { cur = cur->_left; }//cur==_node或NULL结束循环 //访问后继 while (cur && cur->_rightTag == THREAD)//当前节点的右指针为线索 { cout<<cur->_data<<" "; prev = cur; cur = cur->_right; } if (cur == _node)//判断节点是否指向了根节点 { cout<<cur->_data<<endl; return; } while (cur && cur->_right == prev) { cout<<cur->_data<<" "; prev = cur; cur = cur->_parent;//向上一层走 } if (cur && cur->_rightTag == LINK)//cur->_rightTag == LINK(当前节点的右指针为子树) { cur = cur->_right; } } } protected: //按照先序遍历创建二叉树 Node* _CreatBinaryTreeThd(const T* a,size_t n,const T& invalid,size_t& index,Node*& parent) { Node* root = NULL; if (a[index] != invalid && index < n) { root = new Node(a[index]); root->_parent = parent; root->_left = _CreatBinaryTreeThd(a,n,invalid,++index,root); root->_right = _CreatBinaryTreeThd(a,n,invalid,++index,root); } return root; } void _PrveOrderThreading(Node* cur,Node*& prev)//前序遍历的线索化 { if (NULL == cur) { return; } //线索化根节点 if (NULL == cur->_left)//找到最左节点, { cur->_left = prev; //将其左指针指向该节点的直接前驱节点 cur->_leftTag = THREAD; //将其标志位置为THEAD } if (prev && NULL == prev->_right)//找到最右节点, { prev->_right = cur; //将其右指针指向该节点的直接后继节点 prev->_rightTag = THREAD; //将其标志位置为THEAD } prev = cur; //前一个节点后移为当前节点 //线索化左子树 if (cur->_leftTag == LINK) //防止栈溢出 { _PrveOrderThreading(cur->_left,prev); } //线索化右子树 if (cur->_rightTag == LINK) //防止栈溢出 { _PrveOrderThreading(cur->_right,prev); } } void _InOrderThreading(Node* cur,Node*& prev)//中序遍历的线索化 { if (NULL == cur) { return; } //线索化左子树 if (cur->_leftTag == LINK) { _InOrderThreading(cur->_left,prev); } //线索化根节点 if (NULL == cur->_left) { cur->_left = prev; cur->_leftTag = THREAD; } if (prev && NULL == prev->_right) { prev->_right = cur; prev->_rightTag = THREAD; } prev = cur; //线索化右子树 if (cur->_rightTag == LINK) { _InOrderThreading(cur->_right,prev); } } void _PostOrderTheading(Node* cur,Node*& prev)//后序遍历的线索化 { if (NULL == cur) { return; } //线索化左子树 if (cur->_leftTag == LINK) //防止栈溢出 { _PostOrderTheading(cur->_left,prev); } //线索化右子树 if (cur->_rightTag == LINK) //防止栈溢出 { _PostOrderTheading(cur->_right,prev); } //线索化根节点 if (NULL == cur->_left)//找到最左节点, { cur->_left = prev; //将其左指针指向该节点的直接前驱节点 cur->_leftTag = THREAD; //将其标志位置为THEAD } if (prev && NULL == prev->_right)//找到最右节点, { prev->_right = cur; //将其右指针指向该节点的直接后继节点 prev->_rightTag = THREAD; //将其标志位置为THEAD } prev = cur; //前一个节点后移为当前节点 } protected: Node* _node; }; void TestBinaryTreeThd() { int array [10] = {1, 2, 3, '#', '#', 4, '#' , '#', 5, 6}; size_t size = sizeof(array)/sizeof(array[0]); BinaryTreeThd<int> bt(array,size,'#'); bt.InOrderThread(); bt.InOrderThd(); size_t size1 = sizeof(array)/sizeof(array[0]); BinaryTreeThd<int> bt1(array,size1,'#'); bt1.PrveOrderThread(); bt1.PrevOrderThd(); size_t size2 = sizeof(array)/sizeof(array[0]); BinaryTreeThd<int> bt2(array,size2,'#'); bt2.PostOrderThead(); bt2.PostOrderThd(); BinaryTreeThd<int>::Iterator it = bt.Begin();//注意这里我写的迭代器只是针对中序遍历 while (it != bt.End()) { cout<<*it<<" "; ++it; } cout<<endl; }
运行结果: