数据结构学习之线索二叉树(java/c++版)

#include <iostream>
#include <windows.h>
using namespace std;
#pragma warning(disable:4996)

//可不可以利用c++的泛化编程的思想去改边它
typedef int Tree_type; //树中的存储结构的类型(先去掉---用泛化的思想)
//注意枚举和结构体一样的命名和定以方式
typedef enum PoitnterTag
{
    link,
    Thread
} PointerTag;
//注意----当二叉树遇到中序遍历时就自动排好序了
//template<class Tree_type>
typedef struct Tree
{
    Tree_type data;  //要存储的结构
    Tree *Left;     //左孩子
    Tree *Right;    //右孩子
    PointerTag LTag; //左标志位
    PointerTag RTag; //右标志位
} Tree, *PTree;

//template <class Tree_type>
//标志位的定义应该在树形成之后去定义---根据是否有左右结点指针去为其赋值
PTree pre = NULL; //在此定义一个全局变量去保留上一个的结点
bool Threading(PTree &T)
{
    if (T) //还是利用中序遍历去查看是否需要用到标志位
    {
        Threading(T->Left);
        if (!T->Left) //是否T没有左孩子
        {
            T->Left = pre;    //如果它没有左孩子则让它的左指针指向它的上一个结点
            T->LTag = Thread; //并把它的左标志位赋值为1
        }
        if (!pre->Right)
        {
            pre->Right = T;    //如果上一个结点没有右孩子则让它的右指针指向它的下一个结点即T
            pre->RTag = Thread; //并且把它的右标志位赋值为1
        }
        pre = T; //改变上一个结点的值
        Threading(T->Right);
    }
    //写到这里可能会有个疑问--pre一开始还只是个空将T->Left指向空--不是错了吗?
    //因此在这之前我们还需要对pre进行赋值。。。。。。。。。。。。。。
    //而在这里应该把这个pre定义为谁呢?仔细一想就会发现这个pre必须指向一个其他的不属于原来这个树的结点否则就会对其他的结点的线索化造成影响
    return true;
}

//因此在这里必须重新定义一个结点

bool Init_pre(PTree &P, PTree &T)
{
    if (T) //T必须存在才行
    {
        P = new Tree;
        P->Left = T;    //让它指向头结点
        P->LTag = link;  //link表示这个指针有指向
        P->RTag = link;  //有指向的标志位
        P->Right = NULL; //并且将它的值赋值位空
        //在这里pre的初始化就完成了--是不是很简单
        //之后就可以直接调用Threading了
        pre = P;
        Threading(T);
        //在这里有一点要注意当pre的值不断的改变并且到达最后一个结点时要手动对它进行线索化
        pre->Right = P;
        pre->RTag = Thread;
        P->Right = pre; //构成一个相互的指向
    }
    return true;
}

template<class Tree_type>
bool Insert_Tree(PTree &T, Tree_type &elem)
{
    if (T == NULL) //如果树为空就要重新构造
    {
        PTree temp = new Tree;
        temp->data = elem;
        temp->LTag = link; //在这里先假设他们都有结点连接--如果没有到时候在改变它的值
        temp->RTag = link; //初始化=====
        temp->Left = NULL;
        temp->Right = NULL;
        T = temp;
    }
    else if (elem < T->data)
    {
        Insert_Tree(T->Left, elem); //如果elem要小于某个节点的值则让它插入到该节点的左边
    }
    else if (elem > T->data)
    {
        Insert_Tree(T->Right, elem); //如果elem要大于某个节点的值则让它插入到该节点的右边
    }
    //如此的重复的进行知道T为空
    return true;
}

//树的遍历---递归的形式
/* bool Traverse_Tree(PTree& T)
{
    if(T)//如果T不指向空则则继续递归
    {
        Traverse_Tree(T->Left);
        cout << T->data << " ";
        Traverse_Tree(T->Right);//在这里有个巧妙的地方--当从这个点向右遍历时右为空因此又会回到上一个结点
    }
    return true;
} */

//树的遍历---非递归实现中序遍历

bool Traverse_Tree(PTree &T)
{
    PTree temp = T->Left; //T为Root
    while(temp!=T){
        while (temp->LTag == link) //与当初的一直往下走一样,一直走到它的最左边,不同的时此时的我们已经可以回到上一个结点了
        {
            temp = temp->Left;
        }
        cout << temp->data << " "; //循环出来后temp就是最左边的一个结点了--此时应该往上走了
        //temp->RTag的意思是只有当其不能在继续往下走时要利用它的标志位的特点
        while (temp->RTag == Thread && temp->Right != T)//可能在这一部有所疑问--temp->Right!=T的意思是当结点为最后的一个结点时我们不能再继续往下执行了
        {
            temp = temp->Right;
            cout << temp->data << " ";
        }
        //当循环不满足条件的时候会跳出循环,为了让程序继续走完---应该手动的使它跳到下一个结点上去
        temp = temp->Right;
        //此时T在最左边的结点上//发现好像剩下的左右结点指针都没什么用且到达最右端时无发再回到上一个结点了
        //那么可不可以用那两个没用的结点指针使他们指向上一个结点--变废为宝呢?--线索二叉树的来源
    }
    cout << endl;
    return true;
}

int main()
{
    PTree space = NULL;
    PTree Root = NULL;
    Tree_type data;
    do
    {
        cout << "请输入要进入树的数据:";
        cin >> data;
        if (data == 0)
            break;
        Insert_Tree(space, data);
    } while (1);
    Init_pre(Root, space);
    Traverse_Tree(Root);
    system("pause");
    return 0;
}

java下面展示的是java版本的可供java学习者参考思想是一样的因此不写注释了
package com.acm;

import java.util.Scanner;

class Solution
{
   private class node
   {
       private int val;
       private node left;
       private node right;
       private int Ltag;
       private int Rtag;
       public node(int val)
       {
           this.val=val;
           Ltag=Rtag=0;
       }
       public boolean addNode(int data)
       {
           if(data>this.val)
           {
               if(this.right==null)
                   this.right=new node(data);
               else
               {
                   this.right.addNode(data);
               }

           }
           else if(data<this.val)
           {
               if(this.left==null)
                   this.left=new node(data);
               else
               {
                   this.left.addNode(data);
               }
           }
           return true;
       }

       public boolean showNode()
       {
           if(this.left!=null)
               this.left.showNode();
           System.out.print(this.val);
           if(this.right!=null)
               this.right.showNode();
           return true;
       }
       public boolean ThreadTravel()
       {
           if(this.left!=null)
               this.left.ThreadTravel();
           if(this.left==null)
           {
               this.left=pre;
               this.Ltag=1;
           }
           if(pre.right==null)
           {
               pre.right=this;
               pre.Rtag=1;
           }
           pre=this;
           if(this.right!=null)
               this.right.ThreadTravel();
           return true;
       }
   }

   private node root;
   private node pre;
   private node head;

   public boolean add(int data)
   {
       if(root==null)
       {
           root=new node(data);
           root.left=null;
           root.right=null;
       }
       else
       {

           root.addNode(data);
       }
       return true;
   }
   public boolean show()
   {
       if(root!=null)
       {
           root.showNode();
       }
       return true;
   }

   public boolean showThreading()
   {
       if(root!=null)
           root.ThreadTravel();
       return true;
   }

   public boolean Threading()
   {
       if(root!=null)
       {
           head=new node(0);
           head.left=root;
           pre=head;
           root.ThreadTravel();
           pre.right=head;
           head.right=pre;
           pre.Rtag=1;
       }
       return true;
   }

   public boolean TravelNode()
   {

       if(root==null)return true;
       node temp=root;
       while(temp!=head)
       {
           while(temp.Ltag==0)temp=temp.left;
           System.out.print(temp.val);
           while(temp.Rtag==1&&temp.right!=head)
           {
               temp=temp.right;
               System.out.print(temp.val);
           }
           temp=temp.right;
       }
       return true;
   }
}

public class Main {

    public static void main(String[] args) {

        Solution space=new Solution();
        int val=0;
        Scanner iner=new Scanner(System.in);
        do
        {
            System.out.println("请输入要进入树的节点:");
            val=iner.nextInt();

        }while(val!=666&&space.add(val));
        System.out.println("输入结束!");
        space.Threading();
        //space.show();  //此时会进入死循环
        space.TravelNode();
        iner.close();
    }

}

原文地址:https://www.cnblogs.com/z2529827226/p/11802574.html

时间: 2024-10-31 18:42:34

数据结构学习之线索二叉树(java/c++版)的相关文章

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

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

数据结构与算法(八)-二叉树(斜二叉树、满二叉树、完全二叉树、线索二叉树)

前言:前面了解了树的概念和基本的存储结构类型及树的分类,而在树中应用最广泛的种类是二叉树 一.简介 在树型结构中,如果每个父节点只有两个子节点,那么这样的树被称为二叉树(Binary tree).其中,一个父结点的两个字节点分别叫做“左子节点”和“右子节点”.不过也不是所有父节点都有两个子节点,只有左子节点或者只有右子节点的情况也存在.另外,也会存在叶子结点,也就是一个子节点都没有的节点,唯一的限制就是每一个节点的子节点不能超过两个. 之前谈过的单向链表,是一种通过“指向下一个元素的指针”来连接

数据结构(三):非线性逻辑结构-特殊的二叉树结构:堆、哈夫曼树、二叉搜索树、平衡二叉搜索树、红黑树、线索二叉树

在上一篇数据结构的博文<数据结构(三):非线性逻辑结构-二叉树>中已经对二叉树的概念.遍历等基本的概念和操作进行了介绍.本篇博文主要介绍几个特殊的二叉树,堆.哈夫曼树.二叉搜索树.平衡二叉搜索树.红黑树.线索二叉树,它们在解决实际问题中有着非常重要的应用.本文主要从概念和一些基本操作上进行分类和总结. 一.概念总揽 (1) 堆 堆(heap order)是一种特殊的表,如果将它看做是一颗完全二叉树的层次序列,那么它具有如下的性质:每个节点的值都不大于其孩子的值,或每个节点的值都不小于其孩子的值

线索二叉树C+Java

1.线索二叉树的原理: 为什么会有线索二叉树呢?我们观察一个普通的二叉树: 这正是我们平常见到的二叉树,可以发现A,B,C,D这四个节点它们的左孩子和有孩子都有节点,而E,F,G,H,I,J它们都有空闲的指针,这些空闲的指针不存储任何事物 白白浪费了内存的资源. 当我们中序遍历二叉树:HDIBJEAFCG .遍历完之后可以指到每个节点的前驱和后继,如D的前驱是H,后继是I. 可是这是建立在已经遍历完之后的基础上,这里需要注意的是每一种遍历的结果前驱和后继都会不一样的. 在二叉链表创建完后,我们只

数据结构学习之第7章 树和二叉树

数据结构学习之第7章 树和二叉树 0x7.1.1 树的基本概念 ?1.树的定义 ? 树是由n(n>=0)个结点(或元素)组成的有限集合(记为T) ? 如果n>0,这n个结点中有且仅有一个结点作为树的根结点,简称为根,其余结点可分为m(m>=0)个互不相交的有限集\[T_{1}T_{2}\cdots T_{m}\],其中每个子集又是一棵符合定义的子树,称为根结点的子树. 知识点:由树的定义我们可以看出来树的结构是递归的 ?2.树的逻辑表示法 ? 1.树形表示法 ? 2.文氏图表示法 ? 3

数据结构与算法 3:二叉树,遍历,创建,释放,拷贝,求高度,面试,线索树

[本文谢绝转载,原文来自http://990487026.blog.51cto.com] 树 数据结构与算法 3:二叉树,遍历,创建,释放,拷贝,求高度,面试,线索树 二叉树的创建,关系建立 二叉树的创建,关系建立2 三叉链表法 双亲链表: 二叉树的遍历 遍历的分析PPT 计算二叉树中叶子节点的数目:使用全局变量计数器 计算二叉树中叶子节点的数目:不使用全局变量计数器 无论是先序遍历,中序遍历,后序遍历,求叶子的数字都不变;因为本质都是一样的,任何一个节点都会遍历3趟 求二叉树的高度 二叉树的拷

数据结构学习笔记(树、二叉树)

树(一对多的数据结构) 树(Tree)是n(n>=0)个结点的有限集.n=0时称为空树.在任意一颗非空树种: (1)有且仅有一个特定的称为根(Root)的结点: (2)当n>1时,其余结点可分为m(m>0)个互不相交的有限集T1.T2........Tn,其中每一个集合本身又是一棵树,并且称为根的子树. 对于树的定义还需要强调两点:1.n>0时根结点是唯一的,不可能存在多个根结点,数据结构中的树只能有一个根结点.2.m>0时,子树的个数没有限制,但它们一定是互不相交的. 结点

javascript实现数据结构:线索二叉树

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

Java数据结构之树和二叉树

从这里开始将要进行Java数据结构的相关讲解,Are you ready?Let's go~~ Java中的数据结构模型可以分为一下几部分: 1.线性结构 2.树形结构 3.图形或者网状结构 接下来的几章,我们将会分别讲解这几种数据结构,主要也是通过Java代码的方式来讲解相应的数据结构. 今天要讲解的是:Java线性结构 Java数据结构之树形结构 之前我们前几章学习的都是Java数据结构的线性结构,都是一对一的,从现在开始我们将要学习Java的树形结构. 树对于我们来普通Java程序员而言,