struct threadBinaryTreeNode
{
elementType number;
dataType data;
threadBinaryTreeNode *lchild,*rchild;
int leftThread,rightThread;
threadBinaryTreeNode():lchild(0),rchild(0),leftThread(0),rightThread(0){}
threadBinaryTreeNode(elementType a,dataType b):number(a),data(b),lchild(0),rchild(0),leftThread(0),rightThread(0){}
};
我们这样规定,左孩子线索化后为指向该结点的前驱结点,右孩子线索化后为指向该结点的后继结点。那么,就有了,对于拥有空指针的二叉树结点,我们将其线索化使得空指针指向该结点的前驱或后继。
下面给出一个类thread binary tree
template
class threadBinaryTree
{
private:
threadBinaryTreeNode *root;
threadBinaryTreeNode *headNode;
threadBinaryTreeNode *pre;
int n;
public:
threadBinaryTree();
~threadBinaryTree();
void insertNode(const elementType &number,const dataType &data);
void createBT();
void threadBinaryTree_inorder(threadBinaryTreeNode *const tn);
void threadBinaryTree_inorder();
void preorder();
void inorder();
//void postorder();
};
其中,root指向根结点,headNode为一个头结点,pre被用来保存当前结点的双亲结点,n是记录线索二叉树一共有多少结点。
构造函数和析构函数
//构造函数
template
threadBinaryTree::threadBinaryTree():root(0),pre(0),n(0)
{
headNode=new threadBinaryTreeNode[1];
headNode->lchild=0;
headNode->rchild=0;
headNode->leftThread=0;
headNode->rightThread=0;
}
//析构函数
template
threadBinaryTree::~threadBinaryTree()
{
struct sta{threadBinaryTreeNode *node;int flag;};
sta s[n];
int top=-1;
threadBinaryTreeNode *p=root;
while(top>-1 || p==root)
{
while(p!=0)
{
top++;
s[top].node=p;
s[top].flag=0;
p=p->lchild;
}
while(top>-1 && s[top].flag==1)
delete s[top--].node;
if(top>-1)
{
s[top].flag=1;
p=s[top].node->rchild;
}
}
delete headNode;
}
析构函数用非递归的后序遍历来删除树的结点。
下面开始线索化二叉树,这儿用中序线索化。
首先,要构造一颗二叉树
//插入函数
template
void threadBinaryTree::insertNode(const elementType &number,const dataType &data)
{
threadBinaryTreeNode *p;
p=new threadBinaryTreeNode[1];
p->data=data;
p->number=number;
p->lchild=0;
p->rchild=0;
p->leftThread=0;
p->rightThread=0;
if(root==0)
root=p;
else
{
pre=root;
threadBinaryTreeNode *q=root;
while(q)
{
pre=q;
if(p->data>q->data)
q=q->rchild;
else
q=q->lchild;
}
if(p->data>pre->data)
pre->rchild=p;
else
pre->lchild=p;
}
}
//创建二叉树
template
void threadBinaryTree::createBT()
{
cout<<"输入二叉树的结点个数: ";
cin>>n;
cout<<"输入结点编号和信息,如 number data. \n";
for(int i=0;i>number>>data;
insertNode(number,data);
}
cout<<"\n----------二叉树创建完成...\n";
}
有了二叉树就可以对其中序线索化,
template
void threadBinaryTree::threadBinaryTree_inorder(threadBinaryTreeNode *const p)
{
if(p->lchild)
{
p->leftThread=0;
threadBinaryTree_inorder(p->lchild);
}
else
{
p->lchild=pre;
p->leftThread=1;
}
if(pre->rchild)
pre->rightThread=0;
else
{
pre->rchild=p;
pre->rightThread=1;
}
pre=p;
if(p->rchild) threadBinaryTree_inorder(p->rchild);
}
template
void threadBinaryTree::threadBinaryTree_inorder()
{
if(root==0) return;
cout<<"\n----------中序线索化...\n";
headNode=new threadBinaryTreeNode [1];//头结点
headNode->lchild=root;
headNode->rchild=0;
headNode->leftThread=0;
headNode->rightThread=0;
pre=headNode;
threadBinaryTree_inorder(root);
headNode->rchild=pre;
headNode->rightThread=1;
pre->rchild=headNode;
pre->rightThread=1;
cout<<"\n----------中序线索二叉树创建完成...\n";
}
先看第一个函数,使用到了数据成员pre,在线索化的过程中,pre所起的作用等同于教材中的全局变量,其作用是用来记录当前结点的前驱结点。线索化的思路是,从根结点开始,若左孩子非空则对其进行递归调用,若左孩子为空,则使其指向当前结点的前驱pre;判断完左孩子,接着看pre指针,由于pre为前驱结点,那么判断pre所指结点的右孩子是否为空,若为空,则令右孩子指针指向当前结点;最后再对右孩子递归;
可以看到,中序线索化的大致方法就是,对当前结点判断是否有前驱,对前驱结点判断是否有后继。需要说明的是,这儿pre指针初始化时时指向headNode结点(见第二个函数),中序线索化后,headNode结点的左孩子指向root结点,右孩子指向中序遍历的最后一个结点。这样,就可以从第一个结点开始沿着后继结点开始遍历,或从最后一个结点开始沿着前驱开始遍历。
下面是非递归的前序和中序遍历线索二叉树,和二叉树相比简短很多
template
void threadBinaryTree::preorder()
{
pre=root;
while(pre!=headNode)
{
while(pre->leftThread==0)
{
cout<number<<" ";
pre=pre->lchild;
}
cout<number<<" ";
while(pre->rightThread==1 && pre->rchild!=headNode)
pre=pre->rchild;
pre=pre->rchild;
}
}
template
void threadBinaryTree::inorder()
{
pre=root;
while(pre!=headNode)
{
while(pre->leftThread==0)
pre=pre->lchild;
cout<number<<" ";
while(pre->rightThread==1 && pre->rchild!=headNode)
{
pre=pre->rchild;
cout<number<<" ";
}
pre=pre->rchild;
}
}
线索化二叉树后,寻找某结点的前驱和后继十分便利,以后许多博文进行了说明,这儿就不提了。