• 数据结构 线索二叉树 c++


    以二叉链表来作为储存结构的时候,只能找到左右孩子的信息,不能直接得到结点的前驱和后继信息,这种信息只有在遍历的过程中才能实现。在n个结点的二叉链表中必定存在n+1个空链域。可以用这些空链域来保存这些信息;做以下规定:若结点有左子树,则lchild指向其左孩子,若没有左孩子则指向其前驱;若结点有右子树,则rchild指向其右子树,否则指向其后继;为了标志指针是线索还是指针,需要添加两个标志位来表示指针的性质

    lchild LTag rchild RTag

    LTag,RTag是枚举类对象, 值为0的时候表示指针, 值为1表示线索

     1 typedef enum PointerTag{Link, Thread}; 

    当我们能知道结点的前驱和后继信息的时候,只要找到序列中的第一个结点,然后依次查找后继结点,直到后继为空时而止;

    怎么在二叉树中求结点的后继信息?

    对于右孩子为空的结点来说,其右孩子指针指向其后继。但是当结点的右孩子不为空的时候,怎么找到其后继信息?根据后序遍历的规定可以直到,结点的后继结点应该是遍历其右子树最左下角的结点。当结点的左孩子为空的时候,左孩子指针指向其前驱, 当左孩子不为空的时候, 结点的前驱是遍历左子树的最后一个结点,也就是左子树最右下角的结点;(这些都是针对中序遍历而言)

    对线索树的结点做一下定义, 为了简便,结点类型用int类型

    1 typedef struct BiThrNode{
    2     int val;
    3     struct BiThrNode *lchild, *rchild;
    4     PointerTag LTag, RTag;
    5 }BiThrNode, *BiThrTree;

    对二叉树进行线索化:pre是上一个访问的结点,pre的初始值指向表头结点(即后面的Thrt,其左孩子指向根节点); 线索化的过程是一个从叶子结点到根节点的过程,从左子树到右子树的过程;根据上面的分析可以知道,当一个结点的孩子为null的时候,就指向前驱或者后继。 调用这个函数后除最后一个结点,结点的指针都指向孩子结点或者前驱后继。

     1 void InThreading(BiThrTree p, BiThrTree& pre){
     2     if(p){
     3         InThreading(p->lchild, pre);    //左子树线索化
     4         if(!p->lchild){                    //p左孩子为null,则指向pre,指向p的前驱
     5             p->LTag = Thread;
     6             p->lchild = pre;
     7         }else p->LTag = Link;
     8         if(!pre->rchild){                //pre右孩子为null,则指向p,表示p是pre的后驱
     9             pre->RTag = Thread;
    10             pre->rchild = p;
    11         }else pre->RTag = Link;
    12         pre = p;                        //更新p的位置
    13         InThreading(p->rchild, pre);    //右子树线索化
    14     }
    15 }

    建立一个循环到线索树:Thrt是一个头结点,左孩子指向根节点, 右孩子指向树的最后一个结点。又让树的最后一个结点右孩子指向Thrt。这样就构成一个循环的线索树。当对树进行中序遍历的时候,若某一个结点的右孩子指向Thrt,则表示遍历完成

     1 void InOrderThreading(BiThrTree& Thrt, BiThrTree& T){
     2     Thrt = (BiThrNode*) malloc(sizeof(BiThrNode));
     3     Thrt->LTag = Link; Thrt->RTag = Thread;
     4     Thrt->rchild = Thrt;
     5     BiThrTree pre;
     6     if(!T) Thrt->lchild = Thrt;
     7     else{
     8         Thrt->lchild = T;
     9         pre = Thrt;
    10         InThreading(T, pre);                    //中序遍历线索化
    11         pre->rchild = Thrt; pre->RTag = Thread; //最后一个结点线索化
    12         Thrt->rchild = pre;
    13     }
    14 }

    根据线索树进行中序遍历:由上面的分析可以写出下面的中序遍历程序,先找到要遍历的第一个结点,在中序遍历中,即数的最左下角的结点。找到第一个结点后就根据当前节点的后继结点来进行遍历;当结点的右孩子指针不指向后继节点的时候,这继续访问当前结点的右子树(由中序遍历先遍历左子树可以知道, 当访问的结点有右孩子的时候,其右孩子一定已经访问完成),重复上面的过程就遍历所有的结点

     1 void InOrderTraverse(BiThrTree Thrt){
     2     BiThrNode *p = Thrt->lchild;    //让p指向树的根节点
     3     while(p!=Thrt){
     4         while(p->LTag==Link) p = p->lchild;    //指向树的最左下方
     5         cout<<p->val<<" ";
     6         while(p->RTag==Thread && p->rchild!=Thrt){  //根据后继结点进行遍历
     7             p = p->rchild;
     8             cout<<p->val<<" ";
     9         } //接待右孩子不为空的时候,退出循环,继续访问当前结点的右子树
    10         p = p->rchild;  //
    11     }
    12     cout<<endl;
    13 }

    在将二叉树线索化之前,需要建立一个二叉树,这里通过递归的方式来建立一棵树

     1 BiThrTree CreateBiTree(BiThrTree T, int val){
     2     if(!T){
     3         T = (BiThrNode*) malloc(sizeof(BiThrNode));
     4         T->val = val;
     5         T->lchild = T->rchild = NULL;
     6         return T;
     7     }
     8     if(val<T->val) T->lchild = CreateBiTree(T->lchild, val);
     9     if(val>T->val) T->rchild = CreateBiTree(T->rchild, val);
    10     return T;
    11 }

    完整代码

    把数的结点信息按照,层序遍历的顺序储存在数组t之中,建立通过CreateBiTree()建立二叉树, 再通过inorder()来验证二叉树建立是否正确,这里给出的例子,如果建立二叉树正确,二叉遍历的结果应该是一个从1到7的升序数列; 然后验证上面的线索二叉树的构造过程是否正确,先通过InorderThreading来将二叉树线索化, 然后再通过InorderTrverse()来验证;

     1 #include<iostream>
     2 using namespace std;
     3 /*
     4     author: Lai XingYu
     5       date: 2018/5/18
     6   describe: threaded binary tree
     7 */
     8 
     9 typedef enum PointerTag{Link, Thread}; //标志指针类型,前者表示指针, 后者表示线索
    10 typedef struct BiThrNode{
    11     int val;
    12     struct BiThrNode *lchild, *rchild;
    13     PointerTag LTag, RTag;
    14 }BiThrNode, *BiThrTree;
    15 
    16 /*
    17     对二叉树线索化, pre表示上一个访问的结点
    18 */
    19 void InThreading(BiThrTree p, BiThrTree& pre){
    20     if(p){
    21         InThreading(p->lchild, pre);    //左子树线索化
    22         if(!p->lchild){                    //p左孩子为null,则指向pre,指向p的前驱
    23             p->LTag = Thread;
    24             p->lchild = pre;
    25         }else p->LTag = Link;
    26         if(!pre->rchild){                //pre右孩子为null,则指向p,表示p是pre的后驱
    27             pre->RTag = Thread;
    28             pre->rchild = p;
    29         }else pre->RTag = Link;
    30         pre = p;                        //更新p的位置
    31         InThreading(p->rchild, pre);    //右子树线索化
    32     }
    33 }
    34 
    35 /*
    36     Thr是头结点, 其左孩子指向根节点, 右孩子指向树的最后一个结点
    37     树的最后一个结点的右孩子指向THr,构成一个回环
    38 */
    39 void InOrderThreading(BiThrTree& Thrt, BiThrTree& T){
    40     Thrt = (BiThrNode*) malloc(sizeof(BiThrNode));
    41     Thrt->LTag = Link; Thrt->RTag = Thread;
    42     Thrt->rchild = Thrt;
    43     BiThrTree pre;
    44     if(!T) Thrt->lchild = Thrt;
    45     else{
    46         Thrt->lchild = T;
    47         pre = Thrt;
    48         InThreading(T, pre);                    //中序遍历线索化
    49         pre->rchild = Thrt; pre->RTag = Thread; //最后一个结点线索化
    50         Thrt->rchild = pre;
    51     }
    52 }
    53 
    54 void InOrderTraverse(BiThrTree Thrt){
    55     BiThrNode *p = Thrt->lchild;    //让p指向树的根节点
    56     while(p!=Thrt){
    57         while(p->LTag==Link) p = p->lchild;    //指向树的最左下方
    58         cout<<p->val<<" ";
    59         while(p->RTag==Thread && p->rchild!=Thrt){
    60             p = p->rchild;
    61             cout<<p->val<<" ";
    62         }
    63         p = p->rchild;
    64     }
    65     cout<<endl;
    66 }
    67 
    68 BiThrTree CreateBiTree(BiThrTree T, int val){
    69     if(!T){
    70         T = (BiThrNode*) malloc(sizeof(BiThrNode));
    71         T->val = val;
    72         T->lchild = T->rchild = NULL;
    73         return T;
    74     }
    75     if(val<T->val) T->lchild = CreateBiTree(T->lchild, val);
    76     if(val>T->val) T->rchild = CreateBiTree(T->rchild, val);
    77     return T;
    78 }
    79 
    80 void inorder(BiThrTree T){
    81     if(!T) return;
    82     if(T->lchild) inorder(T->lchild);
    83     cout<<T->val<<" ";
    84     if(T->rchild) inorder(T->rchild);
    85 }
    86 
    87 int main(){
    88     int t[] = {4,2,5,1,3,6,7}, i;
    89     BiThrTree T = NULL, Thrt;
    90     for(i=0; i<7; i++) T = CreateBiTree(T, t[i]);
    91     inorder(T);
    92     cout<<endl;
    93     InOrderThreading(Thrt, T);
    94     InOrderTraverse(Thrt);
    95 return 0;}

     在准备408考试的时候接触到这个线索二叉树,理解还是有些吃力;不能理解的是,这样的储存结构的意义在哪里?就算保存了前驱后继信息,也到先找到该节点, 此外,根据实现的过程来看, 当二叉树建立之后, 若要插入新的节点又要重新对二叉树进行线索化, 这个开销也是不小的

    后序线索树的构造

    后序线索树的构造比,中序线索树更为复杂,可以分为以下四种情况

    1. 如果节点为根节点,则后继结点为空
    2. 结点是双亲的右孩子,或者是左孩子且没有兄弟结点, 则后继结点是双亲结点
    3. 结点为双清结点的左孩子,且有兄弟结点,则后继结点双亲右子树按照后序遍历的第一个结点。
    4. 结点为双亲的右孩子,后继为双亲结点
    有疑惑或者更好的解决方法的朋友,可以联系我,大家一起探讨。qq:1546431565
  • 相关阅读:
    STL中的string
    STL中的map
    STL中的set和multiset
    C++基础知识
    希尔排序
    桶排序
    归并排序
    堆排序
    数组左边奇数右边偶数算法O(n)
    背包问题 洛谷P1164 小A点菜
  • 原文地址:https://www.cnblogs.com/mr-stn/p/9058000.html
Copyright © 2020-2023  润新知