上面的图中会出现空指针的情况,比如中序遍历的3号和5号,这时加上一个新增节点即可,这样就构成了回路。
一个前序,中序线索化的完整例子:
节点信息
typedef struct BiTree_clue{
int data;//数据内容
BiTree_clue *lchild, *rchild;//左右孩子节点的地址
PointerTag ltag, rtag;//标识r/lchild里存的是孩子节点的地址还是前驱后继
}*pBiTree_clue;
创建二叉树
这里遵照前序遍历的规则来创建。
//前序遍历创造树
void CreateBitree( pBiTree_clue &p ) {
int input;
cout << "please input nodedata:\n";
cin>>input;
if( -1==input )//这里默认输入-1表示节点为空
p = NULL;
else{
p = new BiTree_clue;
p->data = input;
p->ltag = link;
p->rtag = link;
CreateBitree( p->lchild );
CreateBitree( p->rchild );
}
}
中序线索化
void In_order_makeclue ( pBiTree_clue root ) {
void main_inorder_makeclue ( pBiTree_clue p );
pre = head;
main_inorder_makeclue ( root );
//算法结束后,pre为最右结点,pre的右线索指向头结点,将它指回去构成回路。
pre->rtag = clue;
pre->rchild = head;
head->rchild = pre;//头结点的右线索指向pre
}
void main_inorder_makeclue ( pBiTree_clue p ) {
if( NULL==p )
return;
main_inorder_makeclue ( p->lchild );//左子树递归线索化
if( NULL==p->lchild ){//如果没有左孩子,那么指针里存放的就是线索而不是孩子节点,并将左孩子赋值为前一个节点。
p->ltag = clue;//给p加上左线索
p->lchild = pre;
}
if( NULL==pre->rchild ){//如果前一个节点没有右孩子,那么。。
pre->rtag = clue;
pre->rchild = p;
}
pre = p;//实时更新前一个节点,保持pre指向p的前驱
main_inorder_makeclue ( p->rchild );//右子树递归线索化
}
全局变量:
pBiTree_clue pre,head; //head相当于头结点,存的是最开始的pre
初始化pre
//将最开始的pre节点的左孩子指向根节点,右孩子可以存放线索
pBiTree_clue init_pre( pBiTree_clue root ) {
pBiTree_clue p = new BiTree_clue;
p->ltag = link;
p->rtag = clue;
p->rchild = NULL;
if( NULL==root )
p->lchild = p;
else
p->lchild = root;
//到此为止,p的左孩子是树的根节点,右孩子是自己,它是独立于树之外的新加入的节点,位于根节点的上面,目的是给pre 一个初始值
return p;
}
中序遍历二叉树
// 中序遍历线索二叉线索树T的非递归算法。t 是根节点上面的那个新增的节点。
void Traverse_by_clue( pBiTree_clue head ) {
pBiTree_clue p = head->lchild;//p指向根结点
while( p!=head ){
while( link==p->ltag )//沿左孩子向下
p = p->lchild;
cout << p->data <<endl;//访问其左子树为空的结点
//沿右线索访问后继结点
while( p->rtag==clue&&p->rchild!=head ){
p = p->rchild;
cout << p->data << endl;
}
p = p->rchild;
}
}
回收空间(中序)
void clearTree( pBiTree_clue root ) {
pBiTree_clue p = root;
pBiTree_clue todel;
while ( p!=head ){
while( link==p->ltag )
p= p->lchild;
//不断的释放,直到某个节点rightchild所指的不是后继
while( clue==p->rtag&&p->rchild!=head ){
todel = p;
p = p->rchild;
cout << "delete is "<<todel->data<<endl;
delete todel;
}
todel = p;//准备释放这个rchild不是后继的节点
p = p->rchild;
cout << "delete is "<<todel->data<<endl;
delete todel;
}
delete p;
}
前序线索化
前序遍历的线索化类似中序遍历的线索化,但是因为前序的顺序是根-左-右,会因为递归太深导致栈溢出的问题,所以在递归线索化左子树和右子树的时候需要判断当前结点的左右标志是否为LINK类型-是才递归线索化左子树和右子树.
//前序线索化
void Pre_order_makeclue( pBiTree_clue p ){
void main_pre_order_makeclue( pBiTree_clue p );
pre = head;
main_pre_order_makeclue(p);
//算法结束后,pre为最右结点,pre的右线索指向头结点,将它指回去构成回路。
pre->rtag = clue;
pre->rchild = head;
head->rchild = pre;
}
void main_pre_order_makeclue( pBiTree_clue p ) {
if( NULL==p )
return;
if( NULL==p->lchild ){
p->ltag = clue;
p->lchild = pre;
}
if( NULL==pre->rchild ){
pre->rtag = clue;
pre->rchild = p;
}
pre = p;
if( link==p->ltag )//防止栈溢出
main_pre_order_makeclue( p->lchild );
if( link==p->rtag )
main_pre_order_makeclue( p->rchild );
}
前序遍历(线索)二叉树
//前序遍历
void preorder_traverse_by_clue( pBiTree_clue head ) {
pBiTree_clue p = head->lchild;
while( p!=head ){
while( link==p->ltag ){
cout << p->data<<endl;
p = p->lchild;
}
cout << p->data<<endl;
//将该树的后继当成子树来访问
p = p->rchild;
}
}
前序,中序线索化及遍历的完整代码:
#include <iostream>
using namespace std;
//标识存放的是孩子节点的地址,还是线索的地址
typedef enum{ link, clue } PointerTag;
typedef struct BiTree_clue{
int data;
BiTree_clue *lchild, *rchild;
PointerTag ltag, rtag;
}*pBiTree_clue;
pBiTree_clue pre,head;//head相当于头结点,存的是最开始的pre
int main()
{
void CreateBitree( pBiTree_clue &p );
pBiTree_clue init_pre( pBiTree_clue root );
void Pre_order_makeclue( pBiTree_clue root );//前序线索化
void In_order_makeclue ( pBiTree_clue root );//中序线索化
void preorder_traverse_by_clue( pBiTree_clue t );//前序遍历(线索)二叉树
void Inorder_traverse_by_clue ( pBiTree_clue t );//中序遍历(线索)二叉树
void Preorder_clearTree( pBiTree_clue head );
void Inorder_clearTree( pBiTree_clue p );
pBiTree_clue tree;
CreateBitree( tree );
head = init_pre( tree );
Pre_order_makeclue( tree );
cout << "ifrtst"<<endl;
preorder_traverse_by_clue ( head );
Preorder_clearTree( tree );
//Inorder_clearTree( treeRoot );
return 0;
}
//前序遍历创造树
void CreateBitree( pBiTree_clue &p ) {
int input;
cout << "please input nodedata:\n";
cin>>input;
if( -1==input )
p = NULL;
else{
p = new BiTree_clue;
p->data = input;
p->ltag = link;
p->rtag = link;
CreateBitree( p->lchild );
CreateBitree( p->rchild );
}
}
//前序线索化
void Pre_order_makeclue( pBiTree_clue p ){
void main_pre_order_makeclue( pBiTree_clue p );
pre = head;
main_pre_order_makeclue(p);
//算法结束后,pre为最右结点,pre的右线索指向头结点,将它指回去构成回路。
pre->rtag = clue;
pre->rchild = head;
head->rchild = pre;
}
void main_pre_order_makeclue( pBiTree_clue p ) {
if( NULL==p )
return;
if( NULL==p->lchild ){
p->ltag = clue;
p->lchild = pre;
}
if( NULL==pre->rchild ){
pre->rtag = clue;
pre->rchild = p;
}
pre = p;
if( link==p->ltag )
main_pre_order_makeclue( p->lchild );
if( link==p->rtag )
main_pre_order_makeclue( p->rchild );
}
//前序遍历
void preorder_traverse_by_clue( pBiTree_clue head ) {
pBiTree_clue p = head->lchild;
while( p!=head ){
while( link==p->ltag ){
cout << p->data<<endl;
p = p->lchild;
}
cout << p->data<<endl;
//将该树的后继当成子树来访问
p = p->rchild;
}
}
void Preorder_clearTree( pBiTree_clue t ) {
pBiTree_clue p = t;
pBiTree_clue todel;
while( p!=head ) {
while( link==p->ltag ) {
todel = p;
p = p->lchild;
cout << "delete is "<<todel->data<<endl;
delete todel;
}
todel = p;
p = p->rchild;
cout << "delete is "<<todel->data<<endl;
delete todel;
}
delete p;
}
//中序遍历将树线索化
void In_order_makeclue ( pBiTree_clue root ) {
void main_inorder_makeclue ( pBiTree_clue p );
pre = head;
main_inorder_makeclue ( root );
//算法结束后,pre为最右结点,pre的右线索指向头结点,将它指回去构成回路。
pre->rtag = clue;
pre->rchild = head;
head->rchild = pre;//头结点的右线索指向pre
}
void main_inorder_makeclue ( pBiTree_clue p ) {
if( NULL==p )
return;
main_inorder_makeclue ( p->lchild );//左子树递归线索化
if( NULL==p->lchild ){//如果没有左孩子,那么指针里存放的就是线索而不是孩子节点,并将左孩子赋值为前一个节点。
p->ltag = clue;//给p加上左线索
p->lchild = pre;
}
if( NULL==pre->rchild ){//如果前一个节点没有右孩子,那么。。
pre->rtag = clue;
pre->rchild = p;
}
pre = p;//实时更新前一个节点,保持pre指向p的前驱
main_inorder_makeclue ( p->rchild );//右子树递归线索化
}
// 中序遍历线索二叉线索树T的非递归算法。t 是根节点上面的那个新增的节点。
void Inorder_traverse_by_clue ( pBiTree_clue head ) {
pBiTree_clue p = head->lchild;//p指向根结点
while( p!=head ){
while( link==p->ltag )//沿左孩子向下
p = p->lchild;
cout << p->data <<endl;//访问其左子树为空的结点
//沿右线索访问后继结点
while( p->rtag==clue&&p->rchild!=head ){
p = p->rchild;
cout << p->data << endl;
}
p = p->rchild;
}
}
//将最开始的pre节点的左孩子指向根节点,右孩子可以存放线索
pBiTree_clue init_pre( pBiTree_clue root ) {
pBiTree_clue p = new BiTree_clue;
p->ltag = link;
p->rtag = clue;
p->rchild = NULL;
if( NULL==root )
p->lchild = p;
else
p->lchild = root;
//到此为止,p的左孩子是树的根节点,右孩子是自己,它是独立于树之外的新加入的节点,位于根节点的上面,目的是给pre 一个初始值
return p;
}
void Inorder_clearTree( pBiTree_clue root ) {
pBiTree_clue p = root;
pBiTree_clue todel;
while ( p!=head ){
while( link==p->ltag )
p= p->lchild;
//不断的释放,直到某个节点rightchild所指的不是后继
while( clue==p->rtag&&p->rchild!=head ){
todel = p;
p = p->rchild;
cout << "delete is "<<todel->data<<endl;
delete todel;
}
todel = p;//准备释放这个rchild不是后继的节点
p = p->rchild;
cout << "delete is "<<todel->data<<endl;
delete todel;
}
delete p;
}