在上篇博文中我们说到有关二叉树的遍历问题,将树中的所有结点按照某种次序排列在一个线性有序的序列中,然后从某个结点出发就会可以轻易找到在某种次序下的前驱和后继。
然而当我们希望快速找到某一结点的前驱后继,同时又不希望对二叉树进行遍历时,这时就需要一个东西去把每个结点的前驱后继记录保存起来。为了做到这一点,数据结构引出一个概念叫线索。就好比在某种次序下用一根线将这棵树的前驱后继保存起来,使得一个前驱指针指向前驱结点,一个后继指针指向后继结点。
我们可以用一个结构体来对保存这些结点,在这里用枚举对前驱和后继进行标记。
enum Pointer
{
THREAD, //线索化
LINK //连接
};
struct BinaryTreeNodeThd
{
T _data;
BinaryTreeNodeThd<T>* _left;
BinaryTreeNodeThd<T>* _right;
Pointer _leftTag;
Pointer _rightTag;
}
当我们做完这些的时候就可以考虑我们怎么去实现线索化。
1.中序线索二叉树
在中序线索化中,如果_leftTag==LINK,那表示在_left中存储的是左孩子结点的指针;如果_leftTag==THREAD,即_left中存储的是指向前驱的指针。_rightTag也同理。
首先判断当前结点,如果当前结点是根结点,那么就递归找到它的最后一个叶子结点,即图中3这个结点。现在对3进行前驱线索化,使得_leftTag等于THREAD。紧接着对它进行后继线索,因为3没有左孩子和右孩子,所以接下来我们需要对4进行线索化。
在这里我们需要借用一个pre的指针,让它在遍历的过程中总是指向在中序遍历下的前驱结点,即在中序遍历中刚刚访问过的结点。同理,4 的前驱结点为2,后继结点为1……一定要注意:当我们遇到空指针时就根据情况填入前驱或后继线索。
void _InOrderThreading(Node* cur, Node*& prev) //中序线索化
{
if (cur == NULL) return;
_InOrderThreading(cur->_left,prev); //递归,左孩子线索化
if (cur->_left == NULL) //建立当前结点的前驱线索
{
cur->_leftTag = THREAD;
cur->_left = prev;
}
if (prev && prev->_right == NULL) //建立当前结点的后继线索
{
prev->_right = cur;
prev->_rightTag = THREAD;
}
prev = cur; //前驱跟上,当前结点向前遍历
_InOrderThreading(cur->_right, prev); //递归,右孩子线索化
}
2.前序线索二叉树
前序线索在思想上和中序线索是一样的,区别仅是加入前驱和后继的时间不同。线索表示如下图。
首先先考虑根,然后在对它的左孩子和右孩子进行处理。当一个结点是叶子结点时,它需要把前驱链接到它的父结点上,用pre保存上一个访问的结点(在这里也就是父结点),然后再次访问它父结点的右孩子……依此类推,对于每个结点都做这样的处理,把大问题不断变成子问题,然后对其进行处理。代码如下。
void _PrevOrderThreading(Node* root, Node*& prev)
{
if (root == NULL) return;
if (root->_left == NULL)
{
root->_left = prev;
root->_leftTag = THREAD;
}
if (prev && prev->_right == NULL)
{
prev->_right = root;
prev->_rightTag = THREAD;
}
prev = root;
if (root->_leftTag == LINK)
_PrevOrderThreading(root->_left, prev);
if (root->_rightTag == LINK)
_PrevOrderThreading(root->_right, prev);
}
对二叉树的线索化,思路都是从大问题不断细化缩小实现,一定要明白它的线索是怎么个线索法:有孩子结点就存孩子结点的指针,没有孩子结点就指向前驱或后继。
最后附上源代码。
#include<iostream>
using namespace std;
enum Pointer
{
THREAD,
LINK
};
template<class T>
struct BinaryTreeNodeThd
{
T _data;
BinaryTreeNodeThd<T>* _left;
BinaryTreeNodeThd<T>* _right;
Pointer _leftTag;
Pointer _rightTag;
BinaryTreeNodeThd(const T& x)
:_left(NULL)
, _right(NULL)
, _leftTag(LINK)
, _rightTag(LINK)
, _data(x)
{}
};
template<class T>
class BinaryTreeThd
{
typedef BinaryTreeNodeThd<T> Node;
public:
BinaryTreeThd(T* a, size_t n, const T& invalid)
{
size_t index = 0;
_root = _CreatTree(a, n, invalid, index);
}
//leftTag==0 left中存放的是指向左孩子结点的指针
//leftTag==1 left中存放的是该结点中序下的前驱结点指针
void InOrderThreading() //中序线索化
{
Node* prev = NULL;
_InOrderThreading(_root, prev);
}
void InOrderThd() //中序线索化遍历
{
Node* cur = _root;
while (cur)
{
while (cur->_leftTag == LINK)
{
cur = cur->_left;
}
cout << cur->_data << " ";
while (cur->_rightTag == THREAD)
{
cur = cur->_right;
cout << cur->_data << " ";
}
cur = cur->_right;
}
cout << endl;
}
void PrevOrderThreading()
{
Node* prev = NULL;
_PrevOrderThreading(_root, prev);
}
void PrevOrderThd()
{
Node* cur = _root;
while(cur)
{
while (cur->_leftTag == LINK)
{
cout << cur->_data << " ";
cur = cur->_left;
}
cout << cur->_data << " ";
cur = cur->_right;
}
cout << endl;
}
protected:
Node* _CreatTree(T* a, size_t n, const T& invalid, size_t& index)
{
Node* root = NULL;
if (index < n && a[index] != invalid)
{
root = new Node(a[index]);
root->_left = _CreatTree(a, n, invalid, ++index);
root->_right = _CreatTree(a, n, invalid, ++index);
}
return root;
}
void _InOrderThreading(Node* cur, Node*& prev)
{
if (cur == NULL) return;
_InOrderThreading(cur->_left,prev); //递归,左孩子线索化
if (cur->_left == NULL) //建立当前结点的前驱线索
{
cur->_leftTag = THREAD;
cur->_left = prev;
}
if (prev && prev->_right == NULL) //建立当前结点的后继线索
{
prev->_right = cur;
prev->_rightTag = THREAD;
}
prev = cur; //前驱跟上,当前结点向前遍历
_InOrderThreading(cur->_right, prev); //递归,右孩子线索化
}
void _PrevOrderThreading(Node* root, Node*& prev)
{
if (root == NULL) return;
if (root->_left == NULL)
{
root->_left = prev;
root->_leftTag = THREAD;
}
if (prev && prev->_right == NULL)
{
prev->_right = root;
prev->_rightTag = THREAD;
}
prev = root;
if (root->_leftTag == LINK)
_PrevOrderThreading(root->_left, prev);
if (root->_rightTag == LINK)
_PrevOrderThreading(root->_right, prev);
}
protected:
Node* _root;
};