一步一步写数据结构(线索二叉树)

线索二叉树,在原始二叉树的基础上对节点进行“扩容”,使之变成了一颗节点信息更加丰富,利用率更高的二叉树。具体来说增加了两个指示标签,ltag和rtag,每个标签有两个值,1和0,0代表存在孩子,指针指向相应孩子,1代表没有对应的孩子,指针表示线索,指向其前驱或后继。这样虽然节点多占用了空间(其实很少,只是两个枚举常量而已),换来的却是让原来结构中存在的大量空指针利用起来,变成线索,指示前驱后继,从而使得空间利用效率大大提高, 并且有了线索以后,对后续的查找等操作提高很多效率。

下面是代码,本来以为只需要修改一下标签就完事的,结果写起来还要考虑许多问题:1,pre指针(指向前一个节点的全局指针),以及它的初始化。2,二叉树建立时仍然采用前序方式,但是对节点线索化时需要用中序遍历,最后遍历打印时,因为有了标签,所以可以采用迭代的方式打印输出,至于可否用递归方式打印,还没有尝试过。

下面是代码,主要思路:前序方式建立二叉树(令其标签默认初始化为link),线索化,pre指针的初始化,利用迭代中序遍历打印二叉树。

难点是后面两个,直接想可能很难写出来,可以参照书本画个图理解一下,二叉树这部分画图理解还是蛮重要的!

#include<iostream>
using namespace std;

enum Tag{link,thread};  //这里定义一个枚举类型,link(0)表示指向孩子,thread(1)表示指向前驱后继的线索

//定义节点结构
typedef struct BiThreadNode
{
    char data;
    struct BiThreadNode *lchild,*rchild;
    Tag ltag,rtag;

}BiThreadNode,*BiThreadTree;

//定义一个全局变量,始终指向刚刚访问过的节点
BiThreadTree pre;

//前序遍历创建二叉树
void createBtTree (BiThreadTree &T)
{
    char c;
    cin>>c;
    if(c==‘#‘)
    {
        T=NULL;
    }
    else
    {
        T=new BiThreadNode;
        T->data=c;
        T->ltag=T->rtag=link;
        createBtTree(T->lchild);
        createBtTree(T->rchild);
    }
}

//中序遍历二叉线索树并且对节点进行处理.
void midOrderThread(BiThreadTree &T)
{
    if(T)
    {
        midOrderThread(T->lchild);
//对节点进行处理,即把判断为线索的指针域修改为thread。注意修改前驱后继时分别对pre和T两个指针指向的节点进行处理。
        if(!T->lchild)
        {
            T->ltag=thread;
            T->lchild=pre;     //当发现左孩子为空时另ltag=thread,同时让lchild指针(本来为空)指向前驱节点pre
        }
        if(!pre->rchild)
        {
            T->rtag=thread;   //当发现pre节点的右孩子为空时,另其rtag=thread,同时让他的rchild指向其后继节点T
            pre->rchild=T;
        }
        pre=T;
        midOrderThread(T->rchild);
    }
}

//由于以上只是处理了动态的过程的代码,初始化时会因为pre指针没有赋值从而出错,需要在建立个函数解决此问题
//建立头结点,并中序线索二叉树
void inOrderThread(BiThreadTree &p,BiThreadTree &t)
{
   p=new BiThreadNode;
   p->ltag=link;
   p->rtag=thread;
   p->rchild=p;
   if(!t)
   {
       p->lchild=p;
       p->ltag=link;
   }
   else
   {
       p->lchild=t;
       pre=p;
       midOrderThread(t);
       pre->rchild=p;
       pre->rtag=thread;
       p->rchild=pre;

   }

}
//非递归方式遍历二叉树并输出
void inOrderVisit(BiThreadTree p)
{
    BiThreadTree T;
    p->lchild=T;
    while(T!=p)
    {
        while(T->ltag==link)
        {
            T=T->lchild;
        }
        cout<<T->data;
        while(T->rtag==thread&&T->rchild!=p)
        {
            T=T->rchild;
            cout<<T->data;
        }
        T=T->rchild;
    }

}
int main()
{
 BiThreadTree Tree,p;
 createBtTree(Tree);
 inOrderThread(p,Tree);
 inOrderVisit(p);
}
时间: 2024-10-12 09:25:43

一步一步写数据结构(线索二叉树)的相关文章

(原)数据结构——线索二叉树

原文地址:http://www.cnblogs.com/Security-Darren/p/4716082.html 转载务必注明出处! 线索二叉树的思想来源于二叉树的存储结构中,存在一些空的指针域,因此是否能够将这些空间利用起来,存储一些关于节点间先后顺序的信息,由此产生了线索二叉树.线索二叉树中,线索反映前驱.后继的关系,而指针则体现左右子树. 以二叉链表为例,线索二叉树存储结构上的特点是添加标识符,表明左右指针域究竟存的是指向前驱和后继的线索,还是指向左右子树的指针: 线索二叉树的优势是一

数据结构 - 线索二叉树

线索树 遍历二叉树是按一定的规则将树中的结点排列成一个线性序列,即是对非线性结构的线性化操作. 如何找到遍历过程中动态得到的每个结点的直接前驱和直接后继(第一个和最后一个除外)?如何保存这些信息? 问:一棵有n个结点的二叉树,有多少个空闲指针域未用? 若一棵二叉树有n个结点,则有n-1条指针连线 , 而n个结点共有2n个指针域(Lchild和Rchild) ,所以有n+1个空闲指针域未用. 可以利用这些空闲的指针域来存放结点的直接前驱和直接后继信息. 对结点的指针域做如下规定: ◆ 若结点有左孩

数据结构 线索二叉树 原理及实现

通过考察各种二叉链表,不管儿叉树的形态如何,空链域的个数总是多过非空链域的个数.准确的说,n各结点的二叉链表共有2n个链域,非空链域为n-1个,但其中的空链域却有n+1个.如下图所示. 因此,提出了一种方法,利用原来的空链域存放指针,指向树中其他结点.这种指针称为线索. 记ptr指向二叉链表中的一个结点,以下是建立线索的规则: (1)如果ptr->lchild为空,则存放指向中序遍历序列中该结点的前驱结点.这个结点称为ptr的中序前驱: (2)如果ptr->rchild为空,则存放指向中序遍历

重拾算法(2)——线索二叉树

重拾算法(2)——线索二叉树 上一篇我们实现了二叉树的递归和非递归遍历,并为其复用精心设计了遍历方法Traverse(TraverseOrder order, NodeWorker<T> worker);今天就在此基础上实现线索二叉树. 什么是线索二叉树 二叉树中容易找到结点的左右孩子信息,但该结点在某一序列中的直接前驱和直接后继只能在某种遍历过程中动态获得. 先依遍历规则把每个结点某一序列中对应的前驱和后继线索预存起来,这叫做"线索化". 意义:从任一结点出发都能快速找到

一步一步写算法(之排序二叉树线索化)

原文:一步一步写算法(之排序二叉树线索化) [ 声明:版权所有,欢迎转载,请勿用于商业用途.  联系信箱:feixiaoxing @163.com] 前面我们谈到了排序二叉树,还没有熟悉的同学可以看一下这个,二叉树基本操作.二叉树插入.二叉树删除1.删除2.删除3.但是排序二叉树也不是没有缺点,比如说,如果我们想在排序二叉树中删除一段数据的节点怎么办呢?按照现在的结构,我们只能一个一个数据查找验证,首先看看在不在排序二叉树中,如果在那么删除:如果没有这个数据,那么继续查找.那么有没有方法,可以保

一步一步写算法(之排序二叉树)

原文:一步一步写算法(之排序二叉树) [ 声明:版权所有,欢迎转载,请勿用于商业用途.  联系信箱:feixiaoxing @163.com] 前面我们讲过双向链表的数据结构.每一个循环节点有两个指针,一个指向前面一个节点,一个指向后继节点,这样所有的节点像一颗颗珍珠一样被一根线穿在了一起.然而今天我们讨论的数据结构却有一点不同,它有三个节点.它是这样定义的: typedef struct _TREE_NODE { int data; struct _TREE_NODE* parent; str

一步一步写算法(之二叉树广度遍历)

原文:一步一步写算法(之二叉树广度遍历) [ 声明:版权所有,欢迎转载,请勿用于商业用途.  联系信箱:feixiaoxing @163.com] 在二叉树的遍历当中,有一种遍历方法是不常见的,那就是广度遍历.和其他三种遍历方法不同,二叉树的广度遍历需要额外的数据结构来帮助一下?什么数据结构呢?那就是队列.因为队列具有先进先出的特点,这个特点要求我们在遍历新的一层数据之前,必须对上一次的数据全部遍历结束.暂时还没有掌握队列知识的朋友可以看一看我的这一篇博客-队列. a)下面是新添加的队列数据结构

一步一步写算法(之排序二叉树的保存和加载)

原文:一步一步写算法(之排序二叉树的保存和加载) [ 声明:版权所有,欢迎转载,请勿用于商业用途.  联系信箱:feixiaoxing @163.com] 排序二叉树是我们开发中经常使用到的一种数据结构,它具有较好的插入.删除.查找特性.但是由于二叉树的指针较多,所以相比较其他的数据结构而言,二叉树来得比较麻烦些.但是也不是没有办法,下面介绍一下我个人常用的方法. 我们知道,如果一个二叉树是一个满树的话,那么二叉树的节点应该是按照1.2.3.4依次排开的.但是现实情况是这样的,由于排序二叉树自身

一步一步写算法(之哈希二叉树)

原文:一步一步写算法(之哈希二叉树) [ 声明:版权所有,欢迎转载,请勿用于商业用途.  联系信箱:feixiaoxing @163.com] 用过平衡二叉树的朋友都清楚,平衡二叉树的最大优点就是排序.不管是在数据插入的时候还是在数据删除的时候,我们都要考虑到数据的排序情况.但是和数据的添加.删除一样重要的,还有数据的查询.很不幸,平衡二叉树经常由于节点的添加和删除,数据的查询效率会变得非常低下.朋友们可以看看下面这样的一个极端场景,所有分支节点都只有一边存在数据: /* * 7 3 * / *