在n个结点的二叉链表中,有(n+1)个空指针域,利用这些空指针域来存放当前结点的直接前驱和后继的线索,可以加快查找速度。
普通二叉树只能找到结点的左右孩子信息,而结点的直接前驱和直接后继只能在遍历中获得,若将遍历后对应的前驱和后继预存起来,从第一个结点开始就可以顺藤摸瓜而遍历整棵树,树实际变成了线性序列。线索化二叉树实际上就是把二叉树转化成了线性排列,具有唯一前趋和唯一后继。
线索二叉树结构声明:
typedef struct node{
struct node* left;
struct node* right;
int ltag = 0; //tag标志位标志该指针是指向左/右孩子还是线索
int rtag = 0; //若tag=1则为线索
char val;
}TreeNode,*Tree;
TreeNode *pre = NULL;
中序建立线索二叉树:
算法思想:
一个pre指针一直指向t遍历的前一个结点,每次遍历到一个结点,如果可以做线索的话,刚好做的是t 的左线索和 pre 的右线索。
这样的话当t遍历完时候pre 的右线索还没有处理,此时 pre 指向遍历完之后的最后一个结点,我们给pre 的右线索置1收尾。
//中序遍历构建线索二叉树
void InThread(Tree& t,TreeNode* &pre)
{
if(t == NULL) return;
InThread(t->left,pre);
if(t->left == NULL)
{
t->left = pre;
ltag = 1;
}
if(pre && pre->right = NULL)
{
pre->right = t;
pre->rtag = 1;
}
pre = t;
InThread(t->right,pre);
}
void creatThread(Tree &t)
{
TreeNode *p = NULL;
if(t != NULL){
InThread(t,pre);
t->rtag = 1; //最后一个右标志位置1
}
}
寻找中序序列下的后继结点:
算法思想:
如果该结点p的 p->rtag = 1,则直接返回 p->right(该结点的右孩子)为该结点的后继结点;
如果该结点p的 p->rtag = 0,则该结点具有右子树,p的后继结点的应该为它右子树中序遍历中的第一个结点也就是最左边的结点;
//中序序列的后继结点
TreeNode* successor(TreeNode* p)
{
if(p->rtag == 1) return p->right;
p = p->right; //访问该结点的右子树寻找其中序遍历的第一个结点
while(p->ltag == 0) p = p->left; //找到第一个没有右孩子的结点
return p;
}
中序遍历中序线索二叉树:
算法思想:
先找到该树中序遍历的第一个结点(最左下方的结点),然后从第一个结点开始依次找每个结点的中序后继,最后输出即可。
//中序遍历中序线索二叉树
void InOrder(Tree &t)
{
TreeNode *p = t;
while(p->ltag == 0)
p = p->left; //找到中序遍历的第一个结点(左下角的结点)
for(p; p!=NULL; p=successor(p))
cout << p->val << " ";
}
中序序列下的前驱结点:
算法思想:
如果结点的 ltag == 1 ,那么直接返回 p->left;
如果该结点有左孩子,那么该结点的前驱结点应该是其左子树的最右下角的结点;
//中序序列下的前驱结点 TreeNode* prior(TreeNode* p) { if(p->right == 1) return p->right; p = p->left; //访问左子树 while(p->rtag == 0) p = p->right; //找到第一个没有右孩子的结点 return p; }