线索化二叉树


线索化二叉树,顾名思义,跟“线索”二字有关。一般的,n各结点的二叉链表共有2n个链域,非空链域为n-1个,但其中的空链域却有n+1个,造成了存储空间的浪费,因此,我们设想利用原来的空链域存放指针,指向树中其他结点。这种指针称为线索。原来的二叉链表就变成了下图结构:


(一)结构体定义

class bintree{
/*
	lefttag:0代表指向左孩子,1代表指向前驱
	righttag:0代表指向右孩子,1代表指向后继 
*/ 
public:
	char data;
	bintree *leftchild,*rightchild;
	int lefttag,righttag;		 
}; 

建立线索也有一定的规则:(考虑中序遍历)

    (1)如果pTree->lchild为空,则存放指向中序遍历序列中该结点的前驱结点。这个结点称为pTree的中序前驱;


    (2)如果pTree->rchild为空,则存放指向中序遍历序列中该结点的后继结点。这个结点称为pTree的中序后继;


当然,二叉树的线索化是为了方便遍历的,因此要记得和双向链表结点一样,在二叉树链表上添加一 个头结点,如下图所示,并令其leftchild域的指针指向二叉树的根结点(【1】),其rightchild域的指针指向中序遍历访问时的最后一个结点(【2】)。反之,令二叉树的中序序列中第一个结点中,leftchild域指针和最后一个结点的rightchild域指针均指向头结点(【3】【4】)。这样我们既可以从第一个结点起顺后继进行遍历,也可以从最后一个结点起顺前驱进行遍历。



(二)完整测试代码

#include<iostream>
#include<cstdlib>
#include<string>
#include<cstdio>
using namespace std;
class bintree{
/*
	lefttag:0代表指向左孩子,1代表指向前驱
	righttag:0代表指向右孩子,1代表指向后继 
*/ 
public:
	char data;
	bintree *leftchild,*rightchild;
	int lefttag,righttag;		 
}; 

bintree *pre;			//用来记录上一个节点,即当前节点的前驱,当前节点为pre的后继

bintree *find(bintree *root,char in_data){
    bintree *t = root;
    bintree *node;
    if(t==NULL)return NULL;
    if(t->data == in_data)return t;
    else{
        node = find(t->leftchild,in_data);
        if(node) return node;
        else return find(t->rightchild,in_data);
    }
}

bintree *createtree(){
	bintree *root=new bintree;
	bintree *parent;
	string in_string;
	while(cin>>in_string){		//按照A(0,0)类似输入 
		if(in_string[2]-'0'==0){	//输入为根节点
			root->data=in_string[0];
			root->leftchild=root->rightchild=NULL;
			root->lefttag=root->righttag=0;
			continue;
		}
		parent=find(root,in_string[2]);		//返回第一次出现的位置 
		if(in_string[4]-'0'==1){		//输入左孩子 
			bintree *newnode=new bintree;
			newnode->data=in_string[0]; 
			newnode->leftchild=newnode->rightchild=NULL;
			newnode->lefttag=newnode->righttag=0;
			parent->leftchild=newnode;
		}else if(in_string[4]-'0'==2){		//输入右孩子 
			bintree *newnode=new bintree;
			newnode->data=in_string[0];
			newnode->leftchild=newnode->rightchild=NULL;
			newnode->lefttag=newnode->righttag=0;
			parent->rightchild=newnode; 
		}
		if(getchar()=='\n')
			break;
	}
	return root;
}

//编写中序线索化过程的函数,再添加头结点的 
 bintree *inthreading(bintree *root){
	bintree *t=root;
	if(t){
		inthreading(t->leftchild);	//线索化左子树
		
		//!!
		if(t->leftchild==NULL){		//左子树为空,则将其线索化,使其指向它的前驱 
			t->lefttag=1;
			t->leftchild=pre; 
		}
		
		//因为要线索化当前节点的后继不方便,所以用pre记录节点t的前一个节点
		//可以确定t为pre的后继,那么,每次都是确定当前节点的前驱和该节点的上一个节点pre的后继 
		if(pre->rightchild==NULL){		//前一个节点的右子树为空则将其线索化,使其指向他的后继 
			pre->righttag=1;
			pre->rightchild=t; 
		}
		pre=t;		//把当前节点p赋值给pre,以便下次利用 
		
		//!!!
		inthreading(t->rightchild);		//线索化右子树 
	}
	return root; 
}

//通过添加头结点的方式将其线索化
bintree *addheadthread(bintree *root,bintree *head){
	bintree *t=root;
	head->righttag=0;
	head->rightchild=head;
	if(t==NULL){
		head->lefttag=0;
		head->leftchild=head;
	}else{
		pre=head;
		head->lefttag=0;
		head->leftchild=t;	//完成步骤【1】
		inthreading(t);		//在该过程中就已完成了线索化和图中所示的步骤3 
		pre->rightchild=head;	//完成步骤【4】
		pre->righttag=1;
		head->rightchild=pre;	//完成步骤【2】 
	}
}

//中序遍历 
void inorder(bintree *head){
	bintree *t=head->leftchild;		//通过头结点进入根节点
	while(t!=head){		//表示已遍历完成,指针t已经回到了头节点 
		while(t->lefttag==0)	//循环找到中序遍历的第一个节点
			t=t->leftchild;
	cout<<t->data<<" "; 
	while(t->righttag==1&&t->rightchild!=head){	//不断地输出后继,直到某个节点rightchild指的不是后继,即righttag=1 
		t=t->rightchild;
		cout<<t->data<<" ";
	} 
	t=t->rightchild;//进入右子树 
	} 
}

int main(){
	bintree *root=createtree();
	bintree *head=new bintree;
	addheadthread(root,head);
	inorder(head);
	return 0;
}

测试代码输入:

输入为多个空格分隔开的A(X,y)

字母代表插入的节点名

X为当前该节点的父节点

y=0代表为根节点、1为左孩子、2为右孩子



如果对二叉树有疑问:二叉树的存储与基本操作实现

参考:中序线索二叉树创建及其遍历

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值