二叉线索树

来源:互联网 发布:淘宝店铺简介模板 编辑:程序博客网 时间:2024/06/05 04:22

二叉树的定义:         

         由于在二叉树的链式存储结构中,每个结点存储了指向其左右孩子结点的链,而没有存储指向某种线索性次序下的前驱或者后继结点的链。当需要获得结点在一种遍历序列中的前驱或者后继结点时。有俩中解决方案:

 (1)再进行一次遍历,寻找前驱或后继结点。这需要花费较多的时间,效率较低。

 (2)采用多重链表结构,每个结点增加俩条链,分别指向前驱和后继结点。需要花费较多的存储空间。因此,引入线索二叉树。

         在二叉树的二叉链表表示中,若结点的子树为空,则指向孩子的链为空值。具有N个结点的二树,共2N条链,其中N-1条链表示各结点之间的关系,n+1条链为空。                  线索二叉树利用这些空链存储结点在某种遍历次序子下的前驱和后继关系,即原有非空的链保持不变,仍然指向该结点的左右孩子结点;空left域指向前驱结点,空right域指向后继结点。指向前驱和后继结点的链统称为线索。        为了区分每条链到底指向孩子结点还是线索,每个结点需要增加俩个链标记itaghe rtagitag = 0 left 指向左孩子itag = 1 left  为线索指向前驱结点rtag = 0 right 指向右孩子rtag = 1 right  为线索指向后继结点总之就是有孩子的是0,没孩子是1指向前驱结点或后继结点。

#include<iostream>using namespace std;template <class T>class ThreadBinaryNode   //线索二叉树的二叉链表结点类,T指定结点的元素类型{public:T data;ThreadBinaryNode<T> *left,*right;  //指针域指向左右孩子结点int ltag, rtag;  //左右线索标志ThreadBinaryNode<T> (T data,ThreadBinaryNode<T> *left = NULL, ThreadBinaryNode<T> *right = NULL){  this ->data =data;  this ->left = left;  this ->right = right;  this ->ltag = this -> rtag = 0;}};

中序线索二叉树

二叉树中的中序线索化:     对一棵二叉树进行中序线索化,就是在遍历二叉树时为每个结点添加线索和链标记。
(1)已知一棵二叉树采用具有线索二叉树结点的二叉链表存储,root指向根结点,每个结的left、、right域指向左右孩子结点,线索标记为0。
 (2)设p指向某个结点,front指向p的前驱结点,front初值为NULL。P从根root开始,当p非空时执行以下操作:
a.中序线索化p结点的左子树
b.若P的左子树为空,则设置p的left链为指向前驱结点front的线索,设置左线索标志为p_>ltag为1.
c.若p的右子树为空,则设置p的右线索标记p->rtag为1;若前驱front的右线索标记front->rtag为1,则设置前驱front的right链为指向后继p的线索。
d.使front指向结点P。
中序线索化结点p的右子树。

中序线索二叉树类:先构造一颗二叉链表存储二叉树,再调用inThreadTree(p)函数时以p为根的子树进行中序线索化。

#include"ThreadBinaryTree.h"using namespace std;template <class T>class ThreadBinaryTree  //中序连锁二叉树类{public:ThreadBinaryNode<T> *root;  //指向根结点ThreadBinaryTree();   //构造空线索二叉树ThreadBinaryTree(T prelist [],int n); //以标明空子树的先根序列构造一颗中序线索二叉树~ThreadBinaryTree();bool isEmpty();  //判断是否空二叉树void isOrder();   //中根次序遍历中序线索二叉树void preOrder();  //先根次序遍历中序线索二叉树void inOrderPrevious();  //中根次序(反序)遍历中序线索二叉树void postOrderPrevious();  // 后根次序(反序)遍历中序线索二叉树private:ThreadBinaryNode<T> *create (T prelist [],int n,int &i)  //标明空子树的先根次序遍历序列创建一棵二叉树void inThread (ThreadBinaryNode<T> *p,ThreadBinaryNode<T> *&front)  //中序线索化以p结点为根的子树ThreadBinaryNode<T> *inNext(ThreadBinaryNode<T> *p);  //返回p在中根次序下的后继结点ThreadBinaryNode<T> *preNext(ThreadBinaryNode<T> *p);  //返回p在先根次序下的后继结点    ThreadBinaryNode<T> *inPrev(ThreadBinaryNode<T> *p);    //返回p在中根次序下的前驱结点ThreadBinaryNode<T> *postPrev(ThreadBinaryNode<T> *p);  //返回p在后根次序下的前驱结点void destroy (TreadBinaryNode<T> *p);  // 撤销以p结点为根的子树};template <class T>ThreadBinaryTree<T>::ThreadBinaryTree()  //构造空线索二叉树{   root = NULL;}template<class T>bool ThreadBinaryTree<T>::isEmpty();{   return root ==NULL;}template<class T>ThreadBinaryTree<T>::ThreadBinaryTree(T prelist [], int n)   //以标明空子树的先根序列构造一颗中序线索二叉树{   int i = 0;   root = create(prelist, n,i);   ThreadBinaryNode<T> *front = NULL;   inThread(root,front);  //中序线索化二叉树}template<class T>ThreadBinaryNode<T> *ThreadBinaryTree<T>::create(T prelist[], int n,int &i)  //标明空子树的先根次序遍历序列创建一棵二叉树{ThreadBinaryNode<T> *p= NULL;if(i<n){   T elem = prelist[i];   i++;   if(elem!=NULL)   {      p = new ThreadBinaryNode<T> (elem);  p->left =create(prelist,n,i);  p->right = create(prelist,n,i);   }}return p;}template<class T>void ThreadBinaryTree<T>::inTread(ThreadBinaryNode<T> *p,ThreadBinaryNode<T> *&front)  //中序线索化以p结点为根的子树{   if(p!=NULL)   {     inThread(p->left,front);  //中序线索化p的左子树      if(p->left == NULL)  //若p的左子树为空 {   p->ltag = 1;  //设置左线索标志   p->left = front; //设置p的Left为指向前驱front的线索 } if(p->right == NULL)  //若p的右子树为空 p->rtag = 1; if(front!=NULL&&front->rtag == 1) front ->right = p;     front =p; inTread(p->right,front);  //中序线索化P的右子树   }}


中根次序遍历中序线索二叉树:

       建立线索二叉树的目的是为了直接找到某结点在某种遍历次序下的前驱或后继结点。在中序线索二叉树中,不仅能够直接求得任意一个结点在中根次序下的前驱和后继结点,还能够很方便地求得其在先根次序下的后继结点和后根次序下的前驱结点,使得二叉树的先根、中根、后根次序遍历算法都是非递归的,并且不使用栈,这就是线索化的好处。

(1)求结点中根次序下的后继结点:一个结点p的后继结点是p右子树的第一个访问结点,也就是p右孩子的最左边的后代结点。在中序线索二叉树中一个结点p在中根次序下的后继结点仅与p->right有关:
(1)如果p->rtag==1.则p->right是指向p的后继结点的线索,或p没有后继结点。
(2)否则p->right指向p的右孩子,p的后继结点是p的右子树在中根次序下的第一个访问结点。

template<class T>  ThreadBinaryNode<T> *ThreadBinaryTree<T>::inNext(ThreadBinaryNode<T> *p) //返回p在中根次序下的后继结点{   if(p->rtag==1)   //若右子树为空       p = p->right;  //则p->right是指向p后继结点的线索   else    {       p = p->right;  //进入右子树   while(p->ltag == 0)  //寻找最左边的后代结点   p = p->left;  }   return p;}按中根次序遍历一棵中序线索二叉树,第一个访问的结点是根的左子树上最左边的后代结点。template<class T> void ThreadBinaryTree<T> ::inOrder()  //中根次序遍历线索化二叉树,非递归算法{    cout<<"中根次序遍历中序线索化二叉树:";ThreadBinaryNode<T>   *p = root;while(p!=NULL&&p->ltag == 0)  //寻找根的最左边的后代结点,即第一个访问结点p = p->left;while(p!=NULL){   cout<<p->data<<" ";   p = inNext(p);  // 返回p在中根次序下的后继结点}cout<<endl;}

求结点在中根次序下的前驱结点:在中序线索二叉树中,一个结点p在中根次序下的前驱结点仅与p->left有关。
(1)如果p->ltag==1,则p->left是指向p的前驱结点的线索,或p没有前驱结点。

(2)否则p->left指向p的左孩子,p的前驱结点是p的左子树在中根次序下的最后一个访问结点。也就是p左孩子的左右边的后代结点


后根次序遍历中序线索二叉树:
(1)如果结点p有右孩子,则p的右孩子即为p的前驱结点

(2)如果结点p没有右孩子有左孩子,则p的左孩子即为p的前驱结点

(3)如果p为叶子结点,则p的左兄弟结点即为p的前驱结点;

        如果p没有左兄弟,则沿着left线索向上遇到p的祖先结点t的左孩子即为p的前驱结点。        后根次序遍历中序线索二叉树,最后一个访问的结点是根,但第一个到达的结点是根。从根开始访问,在反复求得当前访问结点的后根次序的前驱结点,即可遍历一棵二叉树,所得的序列与二叉树的后根次序遍历序列正好相反。
析构函数:撤销结点的次序是层次次序,自最深的子树开始至根

    

template<class T>ThreadBinaryTree<T>::~ThreadBinaryTree(){   cout<<"撤销中序线索二叉树:";   destroy(root);   cout<<endl;}template<class T>void ThreadBinaryTree<T>::destroy(ThreadBinaryNode<T> *p)  //撤销以p结点为根的子树{   if(p!=NULL)   {     if(p->latg == 0) destroy(p->left); if(p->rtag == 0) destroy(p->right); cout<<p->data<<" ";  // 显示撤销结点的次序 delete p;   }}


先根次序中序线索二叉树:

(1)如果结点p有左孩子,则p的左孩子即为p的后继结点
 (2)如果结点P没有左孩子有有孩子,则p的右孩子即为p的后继结点。
(3)如果p是叶子结点,则p的右孩子结点即为p的后继结点;如果p没有右兄弟,则沿着right线索向上遇到P的某个祖先结点t的右孩子即为p的后继结点。因此,此时p是某棵子树最后一个访问结点,沿着right线索向上首先遇到有右孩子的祖先结点t,则p就是t的左孩子树上最后一个访问结点,t的右孩子即是p的后继结点。

template <class T>ThreadBinaryNode<T> *ThreadBinaryTree<T>::preNext(ThreadBinaryNode<T> *p)  //返回p在先根次序下的后继结点{if(p->ltag == 0)p=p->left;  //左孩子是p的后继结点else{while(p->rtag == 1&&p->right!=NULL)  //寻找某个中序祖先p=p->right;p=p->right;//右孩子是后继结点}return p;}template<class T>void ThreadBinaryTree<T>::preOrder()//先根次序遍历中序线索二叉树,非递归算法{    cout<<"先根次序遍历中序线索二叉树:";ThreadBinaryNode<T> *p =root;while(p!=NULL){   cout<<p->data<<"  ";   p=preNext(p);  //返回p在先根次序下的后继结点}
cout<<endl;
}
原创粉丝点击