适合需要频繁遍历或查找时需要获取某个节点在某种遍历序列中的前驱和后继
一般二叉树空间缺点: 对于一般的二叉树,假设有n个节点,就有n-1条边,即n-1个有效指针域,一个节点有左右两个指针域,整棵树空的指针域就有2* n-(n-1)=n+1个,浪费了大量空间
定义
线索:指向某种遍历序列中的前驱或后继的指针
线索二叉树:将原来一般的二叉树的空指针指向这个节点的前驱或后继,整棵树没有空指针被浪费,一个节点的一个指针域指向其儿子或前驱或后继
结构
一个节点一个data域,两个指针域,一个指针域可能是其左右儿子,也可能是其前驱后继 需要加一个tag区分,当tag为false,指向左右儿子,当tag为ture,指向前驱后继
tag初始应该为false,可在建树的时候初始化
typedef int DataType;
struct TreeNode{
DataType Data;
TreeNode *left,*right;
bool tagL,tagR;
}
线索化
以中序遍历线索化为例:
在==一次遍历==中修改空指针的过程
设置指示指针p指向当前访问的节点,指针pre指向上一个访问的节点
- 处理前驱(空的左儿子):p的前驱就是pre,让p的左儿子指针指向pre,更改tag为ture
- 处理后驱(空的右儿子):由于访问当时的节点时并不知道下一个(后驱)是谁,所以应该对pre处理后驱,因为pre的后驱就是p,即让pre的右儿子指向p,更改tag为ture
递归过程与中序遍历的递归遍历相似
为了方便后面的遍历,添加一个头节点==head==,right域指向遍历序列的第一个节点,left域指向遍历序列最后一个节点,遍历序列的第一个节点的前驱和最后一个节点的后继指向头节点head,设置head的==好处==是既可以从head的right出发向后遍历,也可以从head的left出发向前遍历,pre初始化为head
对head来说,tag就不重要了
//头节点
TreeNode* head;
head->right=head->left=nullptr;
TreeNode* pre=head;
void InThreading(TreeNode* p){
if(p){
InTreading(p->left);
if(!p->left){
p->left=pre;
p->tagL=ture;
}
if(!pre->right){
pre->right=p;
pre->tagR=ture;
}
pre=p; //别忘了维护pre
InTreading(p->right);
}
}
//递归结束后pre就指向遍历序列的最后一个节点
if(head->right){ //若树不为空
pre->right=head;
pre->tagR=ture;
head->left=pre;
}
==先序和后序==遍历线索化基本思路也相同,先序要把中间的if语句放在递归调用左子树和右子树之前,后序遍历同理
遍历
类似于沿着线性表遍历
向后遍历
设置指示指针p指向当前访问的节点,初始化为head的right,若p为空(空树)直接退出,大循环的条件是p不等于head,当p等于head时遍历回到了原点,此时退出循环
内部第一个小循环也是让p走到左链的最末端(p的第一次大循环开始已经在最左端了),第二个小循环是当节点有后驱且后驱不为head的时候,p沿着后驱走,当某个节点没有后驱退出循环,然后让p指向其右儿子,因为若tagR为false了,即便没有后驱,根据中序遍历规则可知,若p有右儿子则p的后驱一定是其右儿子,特殊的是线索二叉树没有空指针域,所以其右儿子一定是其后驱
void InorderTraversal(TreeNode* head){
TreeNode* p=head->right; //此时p是起点
if(!p)return;
while(p!=head){
while(!p->tagL){ //当p没有前驱
p=p->left;
}
cout<<p->data;
while(p->tagR&&p->right!=head){ //沿着后继走
p=p->right;
cout<<p->data;
}
p=p->right;
}
}
向前遍历
与向前遍历相反,最开始需要将p初始化为head的left,即终点,因为最开始就是终点所以不需要开小循环找终点,每次遇到没有前驱的节点,就让p指向其左儿子,然后沿着右链走到底
void ReverseInorderTraversal(TreeNode* head){
TreeNode* p=head->left; //此时p是终点
if(!p)return;
while(p!=head){
while(!p->tagR){
p=p->right;
}
cout<<p->data;
while(p->tagL&&p->left!=head){ //沿着前驱走
p=p->left;
cout<<p->data;
}
p=p->left;
}
}