线索二叉树

1 线索二叉树的基本概念

n个结点的二叉链表中含有n+1个空指针域。利用二叉链表中的空指针域,存放指向结点在某种遍历次序下的前驱和后继结点的指针(这种附加的指针称为"线索")。



1)ltag为0时指向该结点的左孩子,为1时指向该结点的前驱;
2)rtag为0时指向该结点的右孩子,为1时指向该结点的后继;
线索二叉树的存储结构描述如下:

typedef int ElemType;
typedef struct ThreadNode {
    ElemType data;
    struct ThreadNode *lchild, *rchild;
    int ltag, rtag;
} ThreadNode, *ThreadTree;

以这种结点结构构成的二叉链表作为二叉树的存储结构,叫做线索链表,其中指向结点前驱和后继的指针,叫做线索。
加上线索的二叉树称为线索二叉树。
由于序列可由不同的遍历方法得到,因此,线索树有先序线索二叉树、中序线索二叉树和后序线索二叉树三种。
对二叉树以某种次序遍历使其变为线索二叉树的过程叫做线索化。

2 线索二叉树的构造

对二叉树的线索化,实际上就是遍历一次二叉树,只是在遍历的过程中,检查当前结点左右指针域是否为空,若为空,将它们改为指向前驱结点或后继结点的线索。
添加一个头结点head
lchild域:指向二叉树的根结点;
rchild域:指向中序遍历时的最后一个结点
二叉树中序序列中的第一个结点lchild指针域和最后一个结点的rchild指针域均指向头结点head

(1)通过前序遍历对二叉树线索化的算法如下:
可类比二叉树的先序遍历:https://www.jianshu.com/p/2b4a58bf48ad

void preOrderThread(ThreadTree T) {
    ThreadNode *stack[MAXSIZE] = {NULL};
    ThreadNode *pre = NULL, *p;//pre为上一个结点
    int top = -1;
    if (T != NULL) {
        stack[++top] = T;
        while (top > -1) {
            p = stack[top--];
            if (p->lchild != NULL) {
                p->ltag = 0;
            } else {
                p->ltag = 1;
                p->lchild = pre;
            }
            if (pre != NULL) {
                if (pre->rchild != NULL) {
                    pre->rtag = 0;
                } else {
                    pre->rtag = 1;
                    pre->rchild = p;
                }
            }
            pre = p;
            if (p->rchild != NULL) {
                stack[++top] = p->rchild;
            }
            if (p->lchild != NULL && p->ltag == 0) {//注意p有左子树时(ltag=0)才进栈
                stack[++top] = p->lchild;
            }
        }
        pre->rtag = 1;//最后一个结点是叶子结点
    }
}

(2)通过中序遍历对二叉树线索化:

void inOrderThread(ThreadTree T) {
    ThreadNode *stack[MAXSIZE] = {NULL};
    ThreadNode *pre = NULL, *p = T;
    int top = -1;
    while (p != NULL || top > -1) {
        if (p != NULL) {
            stack[++top] = p;
            p = p->lchild;
        } else {
            p = stack[top--];
            //--------分界线,其余代码和中序遍历二叉树一样
            if (p->lchild != NULL) {
                p->ltag = 0;
            } else {
                p->ltag = 1;
                p->lchild = pre;
            }
            if (pre != NULL) {
                if (pre->rchild != NULL) {
                    pre->rtag = 0;
                } else {
                    pre->rtag = 1;
                    pre->rchild = p;
                }
            }
            pre = p;
            //----------
            p = p->rchild;
        }
    }
    pre->rtag = 1;//处理最后一个结点
}

3 线索二叉树的遍历

在线索二叉树中,由于有线索存在,在某些情况下可以方便地找到指定结点在某种遍历 序列中的直接前驱或直接后继。在线索二叉树上进行某种遍历比在一般的二叉树上进行这种 遍历要容易得多,不需要设置堆栈,且算法十分简洁。
(1)先序线索二叉树的先序遍历

void preOrderThreadTraversal(ThreadTree T) {
    ThreadTree p = T;
    while (p != NULL) {
        visit(p);
        if (p->ltag == 0) {
            p = p->lchild;
        } else {//如果ltag为1,说明没有左子树,直接访问rchild,rchild要么是直接后继要么是右子树,都符合先序遍历的顺序
            p = p->rchild;
        }
    }
}

(2)中序线索二叉树的中序遍历

void inOrderThreadTraversal(ThreadTree T) {
    ThreadTree p = T;
    while (p->ltag == 0) {//寻找最左边结点
        p = p->lchild;
    }
    while (p != NULL) {
        visit(p);
        if (p->rtag == 1) {
            p = p->rchild;//通过右线索找到后继
        } else {//否则右子树的最左结点为后继
            p = p->rchild;
            while (p->lchild == 0) {
                p = p->lchild;
            }
        }
    }
}

测试代码:

void visit(ThreadTree T) {
    printf("%d ", T->data);
}

int main() {
    ThreadTree A = (ThreadNode *) malloc(sizeof(ThreadNode));
    ThreadTree B = (ThreadNode *) malloc(sizeof(ThreadNode));
    ThreadTree C = (ThreadNode *) malloc(sizeof(ThreadNode));
    ThreadTree D = (ThreadNode *) malloc(sizeof(ThreadNode));
    ThreadTree E = (ThreadNode *) malloc(sizeof(ThreadNode));
    A->data = 1;
    A->lchild = B;
    A->rchild = C;
    B->data = 2;
    B->lchild = D;
    C->data = 3;
    B->rchild = NULL;
    C->lchild = NULL;
    C->rchild = E;
    D->data = 4;
    D->lchild = NULL;
    D->rchild = NULL;
    E->data = 5;
    E->lchild = NULL;
    E->rchild = NULL;
//    preOrderThread(A);
//    inOrderThread(A);
//    preOrderThreadTraversal(A);
//    inOrderThreadTraversal(A);
    return 0;
}

你可能感兴趣的:(线索二叉树)