Definition
线索二叉树实际上就是在遍历二叉树的时候,对二叉树进行线索化:在遍历的过程中,检查当前结点的左右指针是否为空,若为空,则将它们改为指向前驱结点或后继结点的线索,比如,我们可以这样规定:
- 若左子树为空,让左指针指向当前结点的前驱结点
- 若右子树为空,让右指针指向当前结点的后继结点
因此,我们需要在结点结构中添加两个tag
,用来区分两种不同的指针:
typedef struct Node{
ElementType value;
bool ltag;
Node* left;
bool rtag;
Node* right;
// ...
}Node;
标志域含义:
标志域 | 0 | 1 |
---|---|---|
ltag | 指向左孩子 | 指向前驱结点 |
rtag | 指向右孩子 | 指向后继结点 |
构造线索二叉树
对二叉树进行中序遍历,然后根据上述方法对结点进行操作:
void inThreadBuildTree(){
inThreadRecsv(this->root, this->header);
}
// return the last node we have visited
Node* inThreadRecsv(Node* &p, Node* &pre){
if(p == NULL){
return;
}
inThreadRecsv(p->left, pre);
if(p->left != NULL){
p->left = pre;
p->ltag = 1;
}
else{
p->ltag = 0;
}
if(pre->right != NULL){
pre->right = p;
pre->rtag = 1;
}
else{
pre->rtag = 0;
}
pre = p;
// do sth
inThreadRecsv(p->right);
}
而对于前序和后序来说,变化的只是我们的操作的位置,取决于遍历方式
遍历线索二叉树的方法
通过其中一种遍历方式,我们已经顺利构造了一棵线索二叉树,那么我们只需要找到第一个结点,就能通过right
指针完整的遍历整棵二叉树。
例如中序遍历,因为优先访问的是左子树,所以首先,我们需要每次都找到最左边的结点,然后再判断右子树是否为空,为空则指向后继结点,不为空则进入右子树继续判断,直到右标识域为空,这时指向后继结点,返回这个结点,重复上述操作:
Node* findMostLeft(Node* p){
while(ltag == 0){
p = p->left;
}
return p;
}
Node* getNext(Node* p){
if(rtag == 0){
return findMostLeft(p->right);
}
else{
return p->right;
}
}
void inOrder(){
for(Node* p = findMostLeft(this->root);p != NULL; p = getNext(p)){
visit(p);
}
return;
}