今天看了一下严奶奶的数据结构,看到线索二叉树这一块,仅仅记录一下自己的学习体会,,第一次写,主要希望能理清楚自己的思绪
首先来看一下二叉树遍历的实质,其实是对非线性结构进行线性化操作,使这些线性序列中有且仅有一个前驱和后继(这里指直接前驱和直接后继),如
a+b*c-d-e/f 中c的前驱是*,后继是-, 当使用二叉链表时只能找到节点的左孩子和右孩子的信息,而不能直接得到该节点在任意一种序列的前驱和后继信息,这里可以在每个节点的存储结构中添加上指向前驱和后继的两个指针,但是考虑到存储密度,以及在有n个节点的二叉树里面必定存在n+1个空指针域,我们在结构中添加两个变量ltag和rtag 当他们为0时代表着指向的是左右孩子节点,为1代表着指向前驱、后继 接下来主要看代码实现
先来看结构定义,没啥好说的
typedef enum pointer{
tlink,thread
}fingurePointer;
typedef struct binNode{
struct binNode *lchild,*rchild;
fingurePointer ltag,rtag;
elemtype data;
}thrNode,*orderTree;
再来看生成树的方法,采用队列生成,输入‘,’表示的虚节点,即没有该节点,当然也可以用前序遍历的递归方法输入,这都无关紧要。
orderTree creatthrTree()
{
orderTree s[100],p,root;
int front=1,rear = 0;
char ch = getchar();
while(ch!='#')
{
p = NULL;
//非虚节点
if(ch!=',')
{
p = (struct binNode *)malloc(sizeof(struct binNode));
p->data = ch;
p->ltag = tlink;
p->rtag = tlink;
p->lchild = NULL;
p->rchild = NULL;
}
s[++rear] = p;
if(rear==1)
root=p;
else{
if(p!=NULL&&s[front]!=NULL)
{
if(rear%2==0)
s[front]->lchild = p;
else
s[front]->rchild = p;
}
if(rear%2==1)
front++;
}
ch = getchar();
}
return root;
}
既然想到了,就记录一下,使用前序遍历递归算法建立二叉树
void CreateBiTree(orderTree *T)
{
char ch;
scanf("%c",&ch);
if(ch==' ')
*T=NULL;
else
{
*T=(struct binNode *)malloc(sizeof(thrNode));
if(!*T)
exit(-1);
(*T)->data=ch;
(*T)->ltag = tlink;
(*T)->rtag = tlink;
CreateBiTree(&(*T)->lchild);
CreateBiTree(&(*T)->rchild);
}
}
然后随便写一个遍历算法,递归非递归都行,我用的是中序遍历递归形式
void visit(orderTree node)
{
orderTree p;
p = node;
if(p!=NULL)
{
visit(p->lchild);
printf("%d %c %d\n",p->ltag,p->data,p->rtag);
visit(p->rchild);
}
}
既然有递归了,那也顺便记录一下非递归的写法,唉,自己记性实在是差到爆,不想以后手忙脚乱去翻
void visitNotRecusion(orderTree node)
{
orderTree p,s[100];
int top = 0;
p = node;
while(p!=NULL||top>0)
{
//左子树
while(p!=NULL)
{
s[++top] = p;
p = p->lchild;
}
p = s[top--];
printf("%c ",p->data);
p = p->rchild;
}
printf("\n");
}
好了,万事俱备,开始进行二叉树---->线索二叉树,首先,假定我们已经有一个变量pre指向前一个节点,当前节点为p,则我们要构建中序线索二叉树,容易让我们先写出中序遍历的框架出来
deal(t->lchild)
中间真正处理
deal(t->rchild)
然后来看二叉树线索化的实质,就是将那些空指针利用起来指向前驱或后继,所以,在当前节点p,如果p的左指针为空,我们将p的ltag改为thread,p->lchild指向pre,然后处理pre的右节点,为什么处理pre的右节点,自己也不太理解,姑且浅显以为因为pre保存的是前一个节点,本节点的右节点留待下一轮进行修改或保持,而且无法直接找到当前节点的后继,具体算法如下
void inThreading(orderTree node)
{
if(node)
{
inThreading(node->lchild);//线索化左子树
if(!node->lchild){//左子树为空,改变ltag,指定lchild为pre
node->ltag = thread;
node->lchild = pre;
}
if(!pre->rchild)//右子树为空,改变rtag,指定pre的 rchild
{
pre->rtag = thread;
pre->rchild = node;
}
pre= node;
inThreading(node->rchild);
}
}
然后模拟循环链表,创建一个头结点,将它的lchild指向根节点,rchild域指向最后一个节点我们将二叉树串起来,这样做的好处就是无论给定哪个节点我们都能从该节点访问完全所有节点
Status InOrderThreading(orderTree &head,orderTree root)
{
if(!(head=(struct binNode *)malloc(sizeof(struct binNode))))
return ERROR;
/*头结点左孩子指向根节点,右节点指向直接前驱*/
head->ltag = tlink; head->rtag = thread;
head->rchild = head;//右指针回指
if(!root) head->lchild = head;
else{
head->lchild = root;
pre = head;
inThreading(root);
pre->rchild = head;
pre->rtag = thread;
head->rchild = pre;
}
return OK;
}
然后,给出中序线索二叉树的遍历代码
Status InOderTranverse(orderTree head)
{
orderTree p;
p = head->lchild;//p指向根节点
while(p!=head)
{
//左子树
while(p->ltag==tlink)
p = p->lchild;
visitNode(p);
//右子树
while(p->rtag ==thread&&p->rchild!=head)
{
p = p->rchild;
visitNode(p);
}
p = p->rchild;
}
return OK;
}
好了,所有代码
#define elemtype char
#define Status int
const int ERROR = 0;
const int OK = 1;
#include "stdlib.h"
#include "stdio.h"
typedef enum pointer{
tlink,thread
}fingurePointer;
typedef struct binNode{
struct binNode *lchild,*rchild;
fingurePointer ltag,rtag;
elemtype data;
}thrNode,*orderTree;
orderTree pre;
orderTree creatthrTree()
{
orderTree s[100],p,root;
int front=1,rear = 0;
char ch = getchar();
while(ch!='#')
{
p = NULL;
//非虚节点
if(ch!=',')
{
p = (struct binNode *)malloc(sizeof(struct binNode));
p->data = ch;
p->ltag = tlink;
p->rtag = tlink;
p->lchild = NULL;
p->rchild = NULL;
}
s[++rear] = p;
if(rear==1)
root=p;
else{
if(p!=NULL&&s[front]!=NULL)
{
if(rear%2==0)
s[front]->lchild = p;
else
s[front]->rchild = p;
}
if(rear%2==1)
front++;
}
ch = getchar();
}
return root;
}
/*前序遍历的递归创建树*/
void CreateBiTree(orderTree *T)
{
char ch;
scanf("%c",&ch);
if(ch=='#')
*T=NULL;
else
{
*T=(struct binNode *)malloc(sizeof(thrNode));
if(!*T)
exit(-1);
(*T)->data=ch;
(*T)->ltag = tlink;
(*T)->rtag = tlink;
CreateBiTree(&(*T)->lchild);
CreateBiTree(&(*T)->rchild);
}
}
void visit(orderTree node)
{
orderTree p;
p = node;
if(p!=NULL)
{
visit(p->lchild);
printf("%d %c %d\n",p->ltag,p->data,p->rtag);
visit(p->rchild);
}
}
void visitNotRecusion(orderTree node)
{
orderTree p,s[100];
int top = 0;
p = node;
while(p!=NULL||top>0)
{
//左子树
while(p!=NULL)
{
s[++top] = p;
p = p->lchild;
}
p = s[top--];
printf("%c ",p->data);
p = p->rchild;
}
printf("\n");
}
//中序线索二叉树
void inThreading(orderTree node)
{
if(node)
{
inThreading(node->lchild);//线索化左子树
if(!node->lchild){//左子树为空,改变ltag,指定lchild为pre
node->ltag = thread;
node->lchild = pre;
}
if(!pre->rchild)//右子树为空,改变rtag,指定pre的 rchild
{
pre->rtag = thread;
pre->rchild = node;
}
pre= node;
inThreading(node->rchild);
}
}
Status InOrderThreading(orderTree &head,orderTree root)
{
if(!(head=(struct binNode *)malloc(sizeof(struct binNode))))
return ERROR;
/*头结点左孩子指向根节点,右节点指向直接前驱*/
head->ltag = tlink; head->rtag = thread;
head->rchild = head;//右指针回指
if(!root) head->lchild = head;
else{
head->lchild = root;
pre = head;
inThreading(root);
pre->rchild = head;
pre->rtag = thread;
head->rchild = pre;
}
return OK;
}
void visitNode(orderTree p)
{
if(p!=NULL)
printf("%c ",p->data);
}
Status InOderTranverse(orderTree head)
{
orderTree p;
p = head->lchild;//p指向根节点
while(p!=head)
{
//左子树
while(p->ltag==tlink)
p = p->lchild;
visitNode(p);
//右子树
while(p->rtag ==thread&&p->rchild!=head)
{
p = p->rchild;
visitNode(p);
}
p = p->rchild;
}
return OK;
}
int main()
{
orderTree root;
CreateBiTree(&root); //先序遍历的递归构建
//root = creatthrTree();//按行构建
visit(root);
printf("\n-------\n");
visitNotRecusion(root);
orderTree head;
Status x = InOrderThreading(head,root);
InOderTranverse(head);
}
输入范例:ABC##D##E#F## //采用递归构建树
逐行构建 -+/a*ef,,b-,,,,,,,,,,cd# 采用visit和InorderTranverse得到相同的结果,a+b*c-d-e/f
如果有写的不对的地方希望大家多多批评,指正。毕竟自己小菜鸡一枚.