线索二叉树

一、简介

二叉树是一种非线性结构,遍历二叉树几乎都是通过递归或者用栈辅助实现非递归的遍历。用二叉树作为存储结构时,取到一个节点,只能获取节点的左孩子和右孩子,不能直接得到节点的任一遍历序列的前驱或者后继。为了保存这种在遍历中需要的信息,我们利用二叉树中指向左右子树的空指针来存放节点的前驱和后继信息。那么这样的话就会将一个二叉树中所有的结点的指针域都利用起来;如果一个结点的左右指针有指向的左右孩子,则不动,如果一个结点的左指针为空,则将该指针指向该节点的前序,如果一个结点的右指针为空,则将该指针指向该节点的后继;

N个结点的二叉树,一共有N+1个空的指针域;当我们将这些空的指针域利用起来,将他们指向任意遍历序列中该节点的前驱或者后继,这样的话,就将二叉树的任意遍历序列的连接起来,以后再遍历的时候,就不用通过递归压栈的方式来遍历,避免的栈的大量开销,容器导致栈溢出的问题,同时没有了压栈的时间开销,使遍历更高效;

线索化的目的就是为了快速遍历,减少递归带来的效率低下的问题

具体是这样做的,我们可以给每个二叉树的结点添加两个标志位;一个标志该节点左孩子指针的标志情况;另一个标志该节点右孩子指针的标志情况;当该节点指针指向孩子时,则该指针的标志位为0;当该节点的指针指向前驱或者后继时,该指针的标志位为1;

首先通过前序方式建一颗普通二叉树,然后对这颗普通二叉树进行线索化,最后通过线索化遍历的方式遍历该线索化以后的二叉树;

在线索化二叉树中,根据遍历方式的不同分为前序线索化二叉树,中序线索化二叉树,后序线索化二叉树;

线索二叉树时有一个总的原则,就是,在线索化的时候,需要两个结点指针;一个指向当前结点,一个指向当前结点的上一个遍历结点;还有就是,每次线索化时,其实是线索上一个结点的后继和当前结点的前驱;即就是判断上一个结点的右指针是否为空,如果为空,则上一个结点的右指针即是线索,当前结点就是其右指针的后继;然后还要处理当前结点的左指针是否为空,如果为空,则当前结点的左指针就是线索,指向前一个结点;

二、三种线索化及其遍历线索二叉树

(1)前序线索化&&前线索化遍历

//1.前序线索化
void _PreOrderThread(Node* root,Node*& prev)
{
   if(root)
   {
   //1.线索根结点
      if(root->LeftChild==NULL)//1.1线索化当前结点的前驱
      {
         root->LeftTage=THREAD;//当前结点的左指针变为线索
         root->LeftChild=prev;//指向前驱
      }
      if(prev&&prev->RightTage==NULL)//1.2线索化前一个结点的后继
      {
         prev->RightTage=THREAD;//上一个结点的右指针变为线索
         prev->RightChild=root;//指向后继
      }
      prev=root;//1.3更新上一个结点
      //2.线索左子树
      if(root->LeftTage==LINK)
      {
            _PreOrderThread(root->LeftChild,prev);
      }
      //3.线索右子树
      if(root->RightTage==LINK)
      {
            _PreOrderThread(root->RightChild,prev);
      }
   }
}
//2.前序线索化遍历
void PreOrder(Node* root)
{
    Node* cur=root;
    while(cur)
    {
        while(cur->LeftTage==Link)
        {
            cout<<cur->_data<<" ";
            cur=cur->LeftChild;
        }
        cout<<cur->_data<<" ";
        cur=cur->_Rightchild;       
    }
}

(2)中序线索化&&中序线索化遍历

//1.中序线索化
void _InOrderThead(Node* root,Node*& prev)//中序线索化
{
    if (root)
    {
        //1.线索化左子树
        if(root->LeftTage==LINK)
        {
            _InOrderThead(root->_LeftChild,prev);
        }
        //2.线索化根结点
           //2.1线索化当前结点的前驱
        if (root->_LeftChild==NULL)
        {
            root->_LeftTage=THREAD;//当前结点的左指针为线索
            root->_LeftChild=prev;//指向前一个结点(前驱)
        }
           //2.2线索化前一个结点的后继
        if (prev&&prev->_RightChild==NULL)
        {
            prev->RightTage=THREAD;//前一个结点的右指针为线索
            prev->RightChild=root;//指向当前结点(后继)
        }
            //2.3更新前一个结点
        prev=root;
        //3.线索化右子树
        if(root->RightTage==LINK)
        {
             _InOrderThead(root->_RightChild,prev);
        }
    }     
}
//2.中序线索化遍历
void InOrder(Node* root)
{
   Node* cur=root;
   while (cur)
   {
       while (cur->_LeftTage==Link)
       {
           cur=cur->_LeftChild;
       }
       cout<<cur->_data<<" ";
       while (cur->_RightTage==THREAD&&cur->_RightChild)
       {
           cur=cur->_RightChild;
           cout<<cur->_data<<" ";
       }
       cur=cur->_RightChild;
   }
}

(3)后序线索化&&后序线索化遍历

//1.后序线索化
   void _EndOrderThead(Node* root,Node*& prev)
   {
       if (root)
       {
           if (root->_LeftTage==LINK)
           {
               _EndOrderThead(root->_LeftChild,prev);
           }
            if (root->_RightTage==LINK)
            {
                _EndOrderThead(root->_RightChild,prev);
            }
           if (root->_LeftChild==NULL)
           {
               root->_LeftTage=THREAD;
               root->_LeftChild=prev;
           }
           if (prev&&prev->_RightChild==NULL)
           {
               prev->_RightTage=THREAD;
               prev->_RightChild=root;
           }
           prev=root;
       }
   }
//2.后序线索化的遍历
后序线索化的遍历,是先将左右子树遍历,在遍历父节点,这样的话,必须记录该父节点,才能访问父节点,此时需要用到三叉链的结点建树,在此不深究;

//2.1三叉链结点结构
struct ThreadTreeNode
{
    T _data;
    ThreadTreeNode<T>* _LeftChild;
    ThreadTreeNode<T>* _RightChild;
    ThreadTreeNode<T>* _father;

    Type _LeftTage;
    Type _RightTage;

    ThreadTreeNode(const T& data)
        :_data(data)
        ,_LeftChild(NULL)
        ,_RightChild(NULL)
        ,_LeftTage(LINK)
        ,_RightTage(LINK)
        ,_father(NULL)
    {}
};

//2.2三叉链建树
Node* _CreatBinaryTreeThd(const T* a,size_t n,const T& invalid,size_t& index,Node*& parent)
    {
        Node* root = NULL;
        if ( index < n&&a[index] != invalid )
        {
            root = new Node(a[index]);
            root->_parent = parent;

            root->_left = _CreatBinaryTreeThd(a,n,invalid,++index,root);
            root->_right = _CreatBinaryTreeThd(a,n,invalid,++index,root);
        }
        return root;
    }

三、完整代码:


#include<iostream>
using namespace std;
#include<cassert>
enum Type//结点指针类型
{
    LINK,//指针为孩子
    THREAD//指针为线索
};
template<typename T>
struct ThreadTreeNode
{
    T _data;
    ThreadTreeNode<T>* _LeftChild;
    ThreadTreeNode<T>* _RightChild;

    Type _LeftTage;
    Type _RightTage;

    ThreadTreeNode(const T& data)
        :_data(data)
        ,_LeftChild(NULL)
        ,_RightChild(NULL)
        ,_LeftTage(LINK)
        ,_RightTage(LINK)
    {}
};
template<typename T>
class TheadTree
{
    typedef ThreadTreeNode<T> Node;
public:
   TheadTree(T* arr,size_t sz,const T& invalid)
   {
       size_t index=0;
       _root=CreatTree(arr,sz,invalid,index);//前序建树
   }
   void PreOrderThead()//前序线索化
   {
      Node* prev=NULL;
      _PreOrderThead(_root,prev);
   }
   void InOrderThead()//中序线索化
   {
       Node* prev=NULL;
      _InOrderThead(_root,prev);
   }
   void EndOrderThead()//后序线索化
   {
       Node* prev=NULL;
      _EndOrderThead(_root,prev);
   }
   void PreOrder()//前序线索化遍历
   {
       Node* cur=_root;
       while (cur)
       {
           while (cur->_LeftTage==LINK)
           {
               cout<<cur->_data<<" ";
               cur=cur->_LeftChild;
           }
           cout<<cur->_data<<" ";
           cur=cur->_RightChild;
       }
   }
   void InOrder()//中序线索化遍历
   {
       Node* cur=_root;
       while (cur)
       {
          while (cur->_LeftTage==LINK)
          {
              cur=cur->_LeftChild;
          }
          cout<<cur->_data<<" ";
          while (cur->_RightTage==THREAD&&cur->_RightChild)
          {
              cur=cur->_RightChild;
              cout<<cur->_data<<" ";
          }
          cur=cur->_RightChild; 
       } 
   }
   void EndOrder()//后序线索化遍历
   {

   }
private:
   Node* CreatTree(T* arr,size_t sz,const T& invalid,size_t& index)//前序建树
   {
        assert(arr);
        Node* root=NULL;
        if (index<sz&&arr[index]!=invalid)
        {
           root=new Node(arr[index]);
           root->_LeftChild=CreatTree(arr,sz,invalid,++index);
           root->_RightChild=CreatTree(arr,sz,invalid,++index);
        }
        return root;
   }

   void _PreOrderThead(Node* root,Node*& prev)//前序线索化(根-左-右)
   {
       if (root)
       {
           //1.线索化当前结点
           if (root->_LeftChild==NULL)//该节点的左孩子为空,线索该节点的前继
           {
               root->_LeftTage=THREAD;
               root->_LeftChild=prev;
           }
           if (prev&&prev->_RightChild==NULL)//前一个结点的不为空,且该节点的右孩子为空,线索该节点的后继
           {
              prev->_RightTage=THREAD;
              prev->_RightChild=root;
           }
           prev=root;
           //2.递归线索化左子树
           if (root->_LeftTage==LINK)
           {
               _PreOrderThead(root->_LeftChild,prev);
           }
           //3.递归线索化右子树
           if (root->_RightTage==LINK)
           {
               _PreOrderThead(root->_RightChild,prev);
           }
       }
   }
   void _InOrderThead(Node* root,Node*& prev)//中序线索化
   {
       if (root)
       {
           //1.递归线索化左子树
           if (root->_LeftTage==LINK)
           {
               _InOrderThead(root->_LeftChild,prev);//线索化左子树
           }
           //2.线索化根结点
              //2.1线索化当前结点的前继
           if (root->_LeftChild==NULL)
           {
               root->_LeftTage=THREAD;
               root->_LeftChild=prev;
           }
              //2.2线索化前一个结点的后继
           if (prev&&prev->_RightChild==NULL)
           {
               prev->_RightTage=THREAD;
               prev->_RightChild=root;
           }
           prev=root;
           //3.递归线索化右子树
           if (root->_RightTage==LINK)
           {
               _InOrderThead(root->_RightChild,prev);
           }
       }
   }
   //后序线索化
   void _EndOrderThead(Node* root,Node*& prev)
   {
       if (root)
       {
           if (root->_LeftTage==LINK)
           {
               _EndOrderThead(root->_LeftChild,prev);
           }
            if (root->_RightTage==LINK)
            {
                _EndOrderThead(root->_RightChild,prev);
            }
           if (root->_LeftChild==NULL)
           {
               root->_LeftTage=THREAD;
               root->_LeftChild=prev;
           }
           if (prev&&prev->_RightChild==NULL)
           {
               prev->_RightTage=THREAD;
               prev->_RightChild=root;
           }
           prev=root;
       }
   }
private:
   Node* _root;
};
int main()
{
    /*int arr[10] = { 1, 2, 3, '#', '#', 4, '#' , '#', 5, 6 };*/
    /*TheadTree<int> b1(arr,10,'#');*/
    //b1.PreOrderThead();
 //   b1.PreOrder();
    //b1.InOrderThead();
    //b1.InOrder();


    int arr1[15] = { 1,2,'#',3,'#','#',4,5,'#',6,'#',7,'#','#',8 };
    TheadTree<int> b2(arr1,15,'#');
    //b2.PreOrderThead();
    //b2.PreOrder();
    b2.InOrderThead();
    b2.InOrder();
    return 0;
}

四、图解

这里写图片描述
这里写图片描述

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值