二叉树的线索化,花了点时间去整理。整个递归过程还是有些抽象的。为什么要进行二叉树的线索化,目的就是为了节省使用二叉链表实现过程中,太多的NULL指针。如果把这些空指针都利用起来,串起来一个循环双向链表,那么对于查找是非常方便的。对于查找而言,如中序遍历,我们关心的是一个节点的后继节点和前驱节点。
因为对于未线索化的二叉树而言,只能从某个节点来获取它的左右孩子节点,也就说只能置顶向下,而不能同过节点快速找到其前驱节点,如果要获取的话,又要遍历一次。
所以,线索化,就能清楚的知道某个节点的前驱和后继节点,这样将那些空余的节点利用起来了,而且查找也方便了。
下面就是代码的实现:
(1) 线索二叉树的数据结构
(2) 创建二叉树,并且线索化
(3)线索化的二叉树的遍历,注意不能使用递归了。
(4)线索化的二叉树的查找。
/***********************************************************************
二叉树的线索化
(1)创建二叉树(按照带括号的字符串)
(2)二叉树的线索化
(3)二叉树线索化后遍历
(4)查找某个值的 先驱节点
(5)查找某个值的 后继节点
************************************************************************/
#include <cstdio>
typedef enum {Link,Thread} PointFlag; //定义枚举量 pointflag为二者之一
typedef struct Node
{
char ch; // 数据域
struct Node *plchild,*prchild; // 左右节点指针
PointFlag lflag,rflag; // 标志位 link为 连接 thread为线索标志
}BiTreeNode;
const int MAX=20;
//按照字符串创建 A(B(,D(G)),C(E(,H),F)) 16:30
void CreateBiTree_ByString(BiTreeNode * &T,char str[])
{
BiTreeNode *p=NULL;
int flag=0,top=0;//flag=1左孩子,flag=2右孩子 top为栈量
BiTreeNode *Stack[MAX]; //栈 用于存父节点
while(*str)
{
switch(*str)
{
case '(': //则刚创建的节点为 父节点入栈
Stack[top++]=p;
flag=1;
break;
case ',':
flag=2;
break;
case ')':
top--; //构造完毕了栈顶节点的左右孩子 出栈
break;
default:
p=new BiTreeNode ;
if(NULL==p) return ;
p->ch=*str;
p->plchild=p->prchild=NULL;
p->lflag=p->rflag=Link;
if(T==NULL) //创建根节点
T=p;
else
{
switch(flag)
{
case 1:
Stack[top-1]->plchild=p; //p为左孩子
break;
case 2:
Stack[top-1]->prchild=p; //p为右孩子
break;
}
/* if(Stack[top-1]->plchild)
Stack[top-1]->lflag=Link;
if(Stack[top-1]->prchild)
Stack[top-1]->rflag=Link;*/
}
break;
}
++str;
}
}
void LDR_Traverse(BiTreeNode *T)
{
if(T)
{
LDR_Traverse(T->plchild);
printf("%2c",T->ch);
LDR_Traverse(T->prchild);
}
}
BiTreeNode *g_pre; //全局
/***********************************************************************
二叉树进行中序遍历线索化
难点:写法上和中序遍历一样。先不断的访问左子树的最左节点(中序的思路)
那么如何进行前驱和后继节点的连接呢?
前驱: 是完成当前指针p的前驱
后继:是完成之前节点的后继
***********************************************************************/
void LDR_Traverse_Thread(BiTreeNode *p)
{
if(p)
{
LDR_Traverse_Thread(p->plchild); //将左子树线索化
if(p->plchild==NULL) //一直遍历到最左子节点 叶子节点或者只有右子树
{ //p更新 前驱节点
p->lflag=Thread; //p的左孩子节点 标志为 前驱
p->plchild=g_pre; // g_pre保存为上一个访问的节点(按照中序遍历顺序)
}
if(g_pre->prchild==NULL) //
{
g_pre->rflag=Thread;
g_pre->prchild=p;
}
g_pre=p; //p已经遍历过 pre指向刚遍历过的节点
LDR_Traverse_Thread(p->prchild);
}
}
//二叉树的线索化
BiTreeNode* BiTreeThread(BiTreeNode* &T)
{
//构造头结点
BiTreeNode *p=new BiTreeNode;
if(!p || !T) return NULL;
//头节点处理
p->lflag=Link;
p->rflag=Thread;
p->plchild=T; //头结点左孩子 指向根节点
p->prchild=p; //头结点右孩子 指向自己
g_pre=p; //g_pre初始化 指向头节点
LDR_Traverse_Thread(T); //将T树进行线索化
//最后一个节点处理
g_pre->rflag=Thread; //最后一个节点 标志位
g_pre->prchild=p; //最后一个节点 后继指向头节点
p->prchild=g_pre; //头节点后继为 最后一个节点
return p; //返回头节点
}
//中序非递归 遍历线索化 后
void LDR_Travese_AfterThread(BiTreeNode *T)
{
BiTreeNode *p=T->plchild; //p为根节点
while(p!=T)
{
while(p->lflag==Link)
p=p->plchild;
printf("%2d %2c %2d \n",p->lflag,p->ch,p->rflag);
while(p->rflag==Thread && p->prchild !=T) //如果 p有后继节点则 直接通过后继节点访问下一个 节点
{
p=p->prchild;
printf("%2d %2c %2d \n",p->lflag,p->ch,p->rflag);
}
p=p->prchild;
}
}
//找到指定节点的前驱节点
BiTreeNode *FindPointNode(BiTreeNode *Head,char key)
{
BiTreeNode *p=Head->plchild; //获取根节点
while(p!=Head)
{
while(p->lflag==Link)
p=p->plchild;
if(p->ch== key)
return p;
while(p->rflag == Thread && p->prchild !=Head )
{
p=p->prchild;
if(p->ch == key)
return p;
}
p=p->prchild;
}
return NULL;
}
//找到指定节点的后继节点
//就是根据 rflag==1直接找到,后者去找该节点右子树最左节点
BiTreeNode *FindNextNode(BiTreeNode *p)
{
BiTreeNode *pre=NULL;
if(p->rflag==Thread)
return p->prchild;
else {
pre=p->prchild;
while(pre->lflag==Link)
pre=pre->plchild;
return pre;
}
}
//找指定节点的前驱节点
//如果该节点的lflag==1直接返回,否则就找到该节点的左子树的最右节点
BiTreeNode *FindPreNode(BiTreeNode *p)
{
BiTreeNode *pre=NULL;
if(p->lflag==Thread)
return p->plchild;
else {
pre=p->plchild;
while(pre->rflag==Link)
pre=pre->prchild;
return pre;
}
}
int main()
{
BiTreeNode *T=NULL;
CreateBiTree_ByString(T,"A(B(,D(G)),C(E(,H),F))");
puts("中序递归遍历为:");
LDR_Traverse(T);
printf("\n");
BiTreeNode *head=BiTreeThread(T);
puts("中序遍历线索化后");
LDR_Travese_AfterThread(head);
BiTreeNode *pos=FindPointNode(head,'D');
printf("D的后继节点为\t%2c\n",FindNextNode(pos)->ch);
pos=FindPointNode(head,'C');
printf("C的前驱节点为\t%2c\n",FindPreNode(pos)->ch);
printf("\n");
return 0;
}