美文网首页
数据结构之线索二叉树

数据结构之线索二叉树

作者: 一直在路上_求名 | 来源:发表于2021-04-16 11:51 被阅读0次

    整体介绍

    线索二叉树是链表表示的树,它是利用了二叉树未被使用的 n + 1个闲置的指针构成的树;
    根据二叉树的三种遍历方式构成了三种不同的线索二叉树;
    二叉树的遍历只能从根结点开始依次遍历,而构建了线索二叉树后,就可以从二叉树中任何一个结点进行遍历,这就是线索化最大的意义了;
    实际上线索二叉树的应用面是很窄的,但是学习它最重要的意义还是理解它的这种思想;
    就是将闲置的空间充分利用起来,这应该是最重要的意义了;

    线索二叉树的存储结构

    与二叉树结构唯一的不同就是多了是否线索化的标识

    typedef struct ThrBiTreeNode {
      ElemType data;
      struct ThrBiTreeNode *lchild, *rchild;
      //这里是和普通的二叉树的区别,用来标识该结点的左右指针是否被线索化
      //1表示线索化了,0表示未线索化
       int ltag, rtag;
    }ThrBiTreeNode, *ThrBiTree;
    

    前(先)序线索二叉树

    1、创建
    这里是使用递归的方式创建的,非递归方式可以通过参考二叉树的非递归现实

    void createPreTree(ThrBiTree root) {
      ThrBiTree pre = NULL;
      if (NULL == root) {
        return;
      }
      //通过递归的方式进行前序线索化
      preThread(root, pre);
      //如果递归完成最后一个结点的后继结点因为空
      if (NULL == pre -> rchild) {
        pre -> rtag = 1;
      }
    }
    void preThread (ThrBiTree &cur, ThrBiTreeNode &pre) {
      //递归出口
      if (NULL == cur) {
        return;
      }
      //如果当前结点没有左孩子结点就将该指针线索化
      if (NULL == cur -> lchild) {
        cur -> lchild = pre;
        cur -> ltag = 1;
      }
      //如果当前结点没有右孩子结点就线索化
      if (NULL != pre && NULL != pre -> rchild) {
        pre -> rchild = cur;
        pre -> rtag = 1;
      }
      //记录当前结点的前驱结点,方便线索化处理
      pre = p;
      //由于当前结点的左孩子已被线索化,故这里需要判断,只线索化未被线索化的结点
      if (p -> ltag = 0) {
        preThread(p -> lchild, pre);
      }
      //右孩子的线索化
      preThread(p -> rchild, pre);
    }
    

    2、遍历
    由于前序遍历(根左右)和二叉树本身的特点,所以从一棵树的根结点无法找到其前驱,只能找到后继,因此无法通过前驱完成逆向遍历,只能通过后继顺序遍历树;

     //寻找后继结点
    ThrBiTreeNode *findNextNode(ThrBiTreeNode *p) {
      //如果右孩子没有被线索化
      if (0 == p -> rtag) {
        //存在左孩子则,后继为左孩子
        if (0 == p -> ltag && NULL != p -> lchild) {
          return p -> lchild;
        }
         //左孩子不存在,则后继为右孩子
         if (NULL != p -> rchild) {
          return p -> rchild;
        }
      } else {//被线索化了,直接返回
        return p -> rchild;
      }
     }
    //前序遍历
    void preOrder(ThrBiTreeNode *p) {
      if (NULL == p) {
        return;
      }
      //当前结点最先被遍历,然后查找后继结点依次遍历,为空就结束
      for (ThrBiTreeNode *cur = p;  cur != NULL; cur = findNextNode(cur)) {
        visit(cur);
      }
    }
    

    中序线索二叉树

    1、创建

    void createInTree(ThrBiTree root) {
      ThrBiTreeNode *pre = NULL;
      if (NULL == root) {
        return;
      }
      //通过递归的方式进行中序线索化
      inThread(root, pre);
      //如果递归完成最后一个结点的后继结点因为空
      if (NULL == pre -> rchild) {
        pre -> rtag = 1;
      }
    }
    void inThread (ThrBiTree &cur, ThrBiTreeNode &pre) {
      //递归出口
      if (NULL == cur) {
        return;
      }
      //左孩子的线索化
      inThread (cur -> lchild, pre);
      //如果当前结点没有左孩子结点就将该指针线索化
      if (NULL == cur -> lchild) {
        cur -> lchild = pre;
        cur -> ltag = 1;
      }
      //如果当前结点没有右孩子结点就线索化
      if (NULL != pre && NULL != pre -> rchild) {
        pre -> rchild = cur;
        pre -> rtag = 1;
      }
      //记录当前结点的前驱结点,方便线索化处理
      pre = p;
      //右孩子的线索化
      inThread(p -> rchild, pre);
    }
    

    2、遍历
    中序遍历即可以找到前驱结点,也可以找到后继结点,因此可以正向和反向遍历

    a、正向遍历

    //找到当前结点中序遍历的第一个被遍历的结点
    ThrBiTreeNode * findFirstNode(ThrBiTreeNode *cur) {
      //中序遍历(左根右),因此要找到子树中最后一个左孩子结点,即为第一个要遍历的结点
      while(p -> ltag == 0) {
        p = p -> lchild;
      }
      return p;
    }
    //找到当前结点的后继结点
    ThrBiTreeNode * findNextNode(ThrBiTreeNode *cur) {
      //如果当前结点的右指针未被线索化,就调第一个方法寻找
      if (cur -> rtag == 0) {
        return findFirstNode(cur -> rchild);
      } else {//被线索化就直接返回
        return cur -> rchild;
       }
    }
    void inorder(ThrBiTreeNode *p) {
      if (NULL == p) {
        return;
      }
      for (ThrBiTreeNode * cur = findFirstNode(p); cur != NULL; cur = findNextNode(cur)) {
        visit(cur);
      }
    }
    

    b、逆向遍历

    //找到当前结点中序遍历的最后一个被遍历的结点
    ThrBiTreeNode * findLastNode(ThrBiTreeNode *cur) {
      //中序遍历(左根右),因此要找到子树中最后一个右孩子结点结点,即为最后要遍历的结点
      while(p -> rtag == 0) {
        p = p -> rchild;
      }
      return p;
    }
    //找到当前结点的前继结点
    ThrBiTreeNode * findPreNode(ThrBiTreeNode *cur) {
      //如果当前结点的左指针未被线索化,就调第一个方法寻找
      if (cur -> ltag == 0) {
        return findFirstNode(cur -> lchild);
      } else {//被线索化就直接返回
        return cur -> lchild;
       }
    }
    void inorder(ThrBiTreeNode *p) {
      if (NULL == p) {
        return;
      }
      for (ThrBiTreeNode * cur = findLastNode(p); cur != NULL; cur = findPreNode(cur)) {
        visit(cur);
      }
    }
    

    后序线索二叉树

    1、创建

    void createPostTree(ThrBiTree root) {
      ThrBiTreeNode *pre = NULL;
      if (NULL == root) {
        return;
      }
      //通过递归的方式进行后序线索化
      postThread(root, pre);
      //如果递归完成最后一个结点的后继结点因为空
      if (NULL == pre -> rchild) {
        pre -> rtag = 1;
      }
    }
    void postThread (ThrBiTree &cur, ThrBiTreeNode &pre) {
      //递归出口
      if (NULL == cur) {
        return;
      }
      //左孩子的线索化
      postThread (cur -> lchild, pre);
      //右孩子的线索化
      postThread(p -> rchild, pre);
      //如果当前结点没有左孩子结点就将该指针线索化
      if (NULL == cur -> lchild) {
        cur -> lchild = pre;
        cur -> ltag = 1;
      }
      //如果当前结点没有右孩子结点就线索化
      if (NULL != pre && NULL != pre -> rchild) {
        pre -> rchild = cur;
        pre -> rtag = 1;
      }
      //记录当前结点的前驱结点,方便线索化处理
      pre = p;
    }
    

    2、遍历
    由于后序遍历(左右根)和二叉树本身的特点,所以从一棵树的根结点无法找到其后继,只能找到前驱,因此只能通过前驱结点逆序遍历树;

     //寻找前驱结点,要么为左孩子要么为右孩子
    ThrBiTreeNode *findPreNode(ThrBiTreeNode *p) {
      //如果左孩子没有被线索化,左孩子被线索化后就是前驱了
      //这个条件就是如果当前结点无法直接找到前驱应该怎么处理
      if (0 == p -> ltag) {
        //如果有右孩子,则右孩子为前驱
        if (0 == p -> rtag && NULL != p -> rchild) {
          return p -> rchild;
        }
         //如果没有右孩子却有左孩子则前驱为左孩子
         if (NULL != p -> lchild) {
          return p -> lchild;
        }
      } else {//被线索化了,直接返回
        return p -> lchild;
      }
     }
    //后续逆向遍历
    void postOrderReverse(ThrBiTreeNode *p) {
      if (NULL == p) {
        return;
      }
      //当前结点最先被遍历,然后查找前驱结点依次遍历,为空就结束
      for (ThrBiTreeNode *cur = p;  cur != NULL; cur = findNextNode(cur)) {
        visit(cur);
      }
    }
    

    相关文章

      网友评论

          本文标题:数据结构之线索二叉树

          本文链接:https://www.haomeiwen.com/subject/rrvrlltx.html