线索二叉树(Threaded BinaryTree)
一、写在前面
本文主要介绍了中序线索化二叉树及中序线索化二叉树遍历。前序与后序原理相似,但在前、后序线索化二叉树中查找节点的后继较容易,而查找前驱要知道其双亲的信息,前、后序线索化二叉树是不完善的。
对于n个节点的二叉树,在二叉链存储结构中有n+1个空链域,利用这些空链域存放在某种遍历次序下该节点的前驱节点和后继节点的指针,这些指针称为线索,加上线索的二叉树称为线索二叉树(Threaded BinaryTree)。
二、为什么要线索化?
二叉树的遍历本质上是将一个复杂的非线性结构转换为线性结构,使每个节点都有了唯一前驱和后继(第一个节点无前驱,最后一个节点无后继)。对于二叉树的一个节点,查找其左右子女是方便的,其前驱后继只有在遍历中得到。为了容易找到前驱和后继,有两种方法。一是在节点结构中增加向前和向后的指针,这种方法增加了存储开销,不可取;二是利用二叉树的空链指针。现将前一篇文章的节点结构重新定义如下:
其中:_leftTag = 1时_leftchild指向左孩子;
_leftTag = 0时_leftchild指向前驱;
_rightTag = 1时_rightchild指向右孩子;
_rightTag = 0时_rightchild指向后继。
三、线索化二叉树
建立线索二叉树,实质上就是遍历一颗二叉树并修改空指针的过程。在遍历的过程中,访问节点的操作是检查当前的左、右指针是否为空,将它们改为指向前驱节点或后继节点的线索。为实现这一过程,设指针prev始终指向刚刚访问过的节点,即若cur指向当前节点,则prev指向cur的前驱,以便设线索。
节点存储结构定义如下:
enum PointerTag
{
THREAD, //线索化
LINK //子树
};
template
struct BinaryTreeThreadNode
{
T _data; //数据
BinaryTreeThreadNode* _leftchild; //左孩子
BinaryTreeThreadNode* _rightchild; //右孩子
PointerTag _leftTag; //左孩子线索标志
PointerTag _rightTag; //右孩子线索标志
};
1、中序线索化二叉树:若节点的_leftTag = 0,_leftchild指向前驱;否则,该节点的前驱是以该节点为根的左子树上按中序遍历的最后一个节点。若_rightTag = 0,_rightchild指向后继;否则,该节点的后继是以该节点为根的右子树上按中序遍历的第一个节点。
3 2 4 1 6 5
在线索化的时候有一个比较难理解的点,比如上图中,我们已经将3节点的左孩子线索化,现在要判断3的右孩子是否为空,如果为空应该将右孩子指针指向谁?就如我只知道我昨天几点起床,却不知道明天几点起床。虽然我今天不知道明天几点起床,但我可以在后天知道我明天几点起床。这里是同样的道理,线索化3右孩子时,我们不知道右孩子应该指向谁,我们可以先什么都不做,等到递归到2的时候,我们知道3的右孩子应该指向2了,这时再将3的右孩子指向2。
2、前序线索化二叉树
1 2 3 4 5 6
3、后序线索化二叉树
3 4 2 6 5 1
四、代码
#pragma once
#include
using namespace std;
enum PointerTag
{
THREAD, //线索化
LINK //子树
};
template
struct BinaryTreeThreadNode
{
T _data; //数据
BinaryTreeThreadNode* _leftchild; //左孩子
BinaryTreeThreadNode* _rightchild; //右孩子
PointerTag _leftTag; //左孩子线索标志
PointerTag _rightTag; //右孩子线索标志
BinaryTreeThreadNode(const T& x) //构造函数
:_data(x)
, _leftchild(NULL)
, _rightchild(NULL)
, _leftTag(LINK)
, _rightTag(LINK)
{}
};
template //模板
class BinaryTreeThd
{
public:
typedef BinaryTreeThreadNode Node;
//构造二叉树
BinaryTreeThd(T* a, size_t n, const T& invalid)
{
size_t index = 0;
_root = _CreateTree(a, n, invalid, index);
}
//中序线索化二叉树
void InOrderTreeThing()
{
Node* prev = NULL;
_InOrderTreeThing(_root, prev);
}
//中序线索化遍历二叉树
void InOrderTreeTh()
{
Node* cur = _root;
while (cur)
{
while (cur->_leftTag == LINK)
{
cur = cur->_leftchild;
}
cout << cur->_data << " ";
if (cur->_rightTag == THREAD)
{
cur = cur->_rightchild;
cout << cur->_data << " ";
}
cur = cur->_rightchild;
}
cout << endl;
}
protected:
Node* _CreateTree(T* a, size_t n, const T& invalid, size_t& index)
{
Node* root = NULL;
if (index_leftchild = _CreateTree(a, n, invalid, ++index);
root->_rightchild = _CreateTree(a, n, invalid, ++index);
}
return root;
}
void _InOrderTreeThing(Node* cur, Node*& prev)//递归线索化
{
if (cur == NULL)
return;
_InOrderTreeThing(cur->_leftchild, prev);
if (cur->_leftchild == NULL)
{
cur->_leftTag = THREAD;
cur->_leftchild = prev;
}
if (prev && prev->_rightchild == NULL)
{
prev->_rightchild = cur;
prev->_rightTag = THREAD;
}
prev = cur;
_InOrderTreeThing(cur->_rightchild, prev);
}
protected:
Node* _root;
};
void ThreadTest()
{
int a[12] = { 1, 2, 3, '#', '#', 4, '#', '#', 5, 6, '#', '#' };
BinaryTreeThd btt(a, 12, '#');
btt.InOrderTreeThing();
btt.InOrderTreeTh();
}