数据结构 树的创建(线索化二叉树)

//二叉树的线索化
#include<stdio.h>
#include<stdlib.h>
#include<string.h>

//定义二叉树线索化节点
typedef struct _TreeNode{
    char data;
    char lefttag;//0表示没有线索化,1表示线索化---每次创建节点都会初始化 所以所有节点默认都是0
    char righttag;
    struct _TreeNode * leftchild;
    struct _TreeNode * rightchild;
}TreeNode, *TreeNodePointer;

//定义前驱节点
TreeNodePointer pre = NULL;

/*
线索二叉树的定义
普通二叉树只能找到结点的左右孩子信息,而该结点的直接前驱和直接后继只能在遍历过程中获得。
n个结点的二叉链表中含有n+1(2n-(n-1)=n+1)个空指针域。
利用二叉链表中的空指针域,存放指向结点在某种遍历次序下的前趋和后继结点的指针(这种附加的指针称为"线索")。

备注:二叉树的遍历很复杂,销毁判断也会增加,暂时没看出任何优势所在,虽然花了我2天时间
线索化二叉树并不能通过头节点前驱,后继像链表一样顺序访问,因为原来的双亲节点的后继不是正确的
如图,如果正常的链表 A结点的后继应该是H 结果这里是E   根本无法顺序访问    如果以后有所顿悟  再来修改

*/

//创建树
TreeNodePointer CreateTree(){
    //定义结构体对象
    TreeNodePointer t1 = NULL, t2 = NULL, t3 = NULL, t4 = NULL, t5 = NULL, t6 = NULL, t7 = NULL, t8 = NULL, t9 = NULL;
    t1 = (TreeNodePointer)malloc(sizeof(TreeNode));
    if (t1 == NULL)
    {
        printf("分配内存失败!");
        return NULL;
    }
    //初始化数据
    memset(t1, 0, sizeof(TreeNode));
    t2 = (TreeNodePointer)malloc(sizeof(TreeNode));
    if (t2 == NULL)
    {
        printf("分配内存失败!");
        return NULL;
    }
    //初始化数据
    memset(t2, 0, sizeof(TreeNode));
    t3 = (TreeNodePointer)malloc(sizeof(TreeNode));
    if (t3 == NULL)
    {
        printf("分配内存失败!");
        return NULL;
    }
    //初始化数据
    memset(t3, 0, sizeof(TreeNode));
    t4 = (TreeNodePointer)malloc(sizeof(TreeNode));
    if (t4 == NULL)
    {
        printf("分配内存失败!");
        return NULL;
    }
    //初始化数据
    memset(t4, 0, sizeof(TreeNode));
    t5 = (TreeNodePointer)malloc(sizeof(TreeNode));
    if (t5 == NULL)
    {
        printf("分配内存失败!");
        return NULL;
    }
    //初始化数据
    memset(t5, 0, sizeof(TreeNode));
    t6 = (TreeNodePointer)malloc(sizeof(TreeNode));
    if (t6 == NULL)
    {
        printf("分配内存失败!");
        return NULL;
    }
    //初始化数据
    memset(t6, 0, sizeof(TreeNode));
    t7 = (TreeNodePointer)malloc(sizeof(TreeNode));
    if (t7 == NULL)
    {
        printf("分配内存失败!");
        return NULL;
    }
    //初始化数据
    memset(t7, 0, sizeof(TreeNode));
    t8 = (TreeNodePointer)malloc(sizeof(TreeNode));
    if (t8 == NULL)
    {
        printf("分配内存失败!");
        return NULL;
    }
    //初始化数据
    memset(t8, 0, sizeof(TreeNode));
    t9 = (TreeNodePointer)malloc(sizeof(TreeNode));
    if (t9 == NULL)
    {
        printf("分配内存失败!");
        return NULL;
    }
    //初始化数据
    memset(t9, 0, sizeof(TreeNode));
    //填充数据域
    t1->data = ‘A‘;
    t2->data = ‘B‘;
    t3->data = ‘C‘;
    t4->data = ‘D‘;
    t5->data = ‘E‘;
    t6->data = ‘F‘;
    t7->data = ‘G‘;
    t8->data = ‘H‘;
    t9->data = ‘I‘;

    //建立树之间的关系
    t1->leftchild = t2;
    t1->rightchild = t5;

    t2->leftchild = NULL;
    t2->rightchild = t3;

    t3->leftchild = t4;
    t3->rightchild = NULL;

    // t5是t4的左孩子
    t4->leftchild = NULL;
    t4->rightchild = NULL;

    //t5没有孩子节点
    t5->leftchild = NULL;
    t5->rightchild = t6;

    t6->leftchild = t7;
    t6->rightchild = NULL;

    t7->leftchild = t8;
    t7->rightchild = t9;

    t8->leftchild = NULL;
    t8->rightchild = NULL;

    t9->leftchild = NULL;
    t9->rightchild = NULL;

    return t1;

}

//销毁树
void Destroy(TreeNodePointer * root){
    if (root == NULL)
    {
        printf("传入参数不可以为空!\n");
        return;
    }
    TreeNodePointer temptree = *root;
    //遍历左子树
    if (temptree->lefttag == 0)
    {
        Destroy(&temptree->leftchild);
    }
    //遍历右子树
    if (temptree->righttag ==0)
    {
        Destroy(&temptree->rightchild);
    }
    //访问根节点
    if (temptree != NULL)
    {
        free(temptree);
        temptree = NULL;
        *root = NULL;
    }
}

//中序线索化树
void InorderThreading(TreeNodePointer root){
    //中序法线索化
    if (root != NULL)
    {
        //线索化左子树
        InorderThreading(root->leftchild);
        if (!root->leftchild)
        {
            //如果该结点的左子树为空,需要线索化
            root->lefttag = 1;
            //该节点的前驱指向前一个节点
            root->leftchild = pre;
        }
        //前驱节点的后继指向该结点
        if (!pre->rightchild)
        {
            //如果前驱结点的右子树为空,需要线索化
            pre->righttag = 1;
            pre->rightchild = root;
        }
        pre = root;
        //线索化右子树
        InorderThreading(root->rightchild);
    }
}

//遍历线索化二叉树
void ForeachTree(TreeNodePointer head){
    if (head==NULL)
    {
        printf("传入参数不可以为空!\n");
        return;
    }
    //获取根节点
    TreeNodePointer root = head->leftchild;
    while (root != head){
        //一直向左遍历  找到最左边的叶子
        while (root->lefttag == 0){
            root = root->leftchild;
        }
        printf("%c", root->data);
        //判断该节点的右孩子是不是线索 是线索  直接遍历  (遍历所有的右孩子是线索的结点)
        while (root->righttag == 1 && root->rightchild!=head)
        {
            root = root->rightchild;
            printf("%c", root->data);
        }
        //遍历该节点的右孩子
        root = root->rightchild;
    }
}

void Test(){
    //创建头结点
    TreeNodePointer head = (TreeNodePointer)malloc(sizeof(TreeNode));
    if (head == NULL)
    {
        printf("分配内存失败!\n");
        return;
    }
    //初始化
    memset(head, 0, sizeof(TreeNode));
    //定义树的根节点
    TreeNodePointer root = NULL;
    root = CreateTree();
    //根据线索化二叉树定义
    //----头结点的前驱指向根节点 线索化标识为0
    //----头节点的后继指向中序结果的最后一个元素 线索化标识为1
    head->leftchild = root;
    head->lefttag = 0;
    //为了防止头结点的后继指向中序的起点 先为头结点的后继赋值
    head->rightchild = head;
    head->righttag = 1;
    //此时前驱节点指向头结点
    pre = head;
    //线索化树
    InorderThreading(root);
    //此时pre指向的是中序遍历的最后一个节点
    pre->rightchild = head;
    pre->righttag = 1;
    //头结点的右孩子指向中序遍历的最后一个节点
    head->rightchild = pre;
    //遍历线索化二叉树
    ForeachTree(head);
    //销毁树
    Destroy(&root);
    //释放头节点
    if (head!=NULL)
    {
        free(head);
        head = NULL;
    }
}

void main(){
    Test();
    system("pause");
}

时间: 2024-10-05 17:05:47

数据结构 树的创建(线索化二叉树)的相关文章

树五:线索化二叉树

线索化二叉树: 线索化二叉树指的是将二叉树中的结点进行逻辑意义上的“重排列”,使其可以线性的方式访问每一个结点. 二叉树线索化后每个结点都有一个线性下标,通过这个下标可以快速访问结点,而不需要遍历二叉树.

数据结构例程——线索化二叉树(中序)

本文是数据结构基础系列(6):树和二叉树中第14课时线索二叉树的例程. #include <stdio.h> #include <malloc.h> #define MaxSize 100 typedef char ElemType; typedef struct node { ElemType data; int ltag,rtag; //增加的线索标记 struct node *lchild; struct node *rchild; } TBTNode; void Creat

数据结构与算法---线索化二叉树(Threaded BinaryTree)

先看一个问题 将数列 {1, 3, 6, 8, 10, 14  } 构建成一颗二叉树 问题分析: 当我们对上面的二叉树进行中序遍历时,数列为 {8, 3, 10, 1, 6, 14 } 但是 6, 8, 10, 14 这几个节点的 左右指针,并没有完全的利用上. 如果我们希望充分的利用 各个节点的左右指针, 让各个节点可以指向自己的前后节点,怎么办? 解决方案-线索二叉树 线索二叉树基本介绍 1.n个结点的二叉链表中含有n+1  [公式 2n-(n-1)=n+1] 个空指针域.利用二叉链表中的空

【数据结构】线索化二叉树中序线索化的递归写法和非递归写法

二叉树是一种非线性结构,遍历二叉树几乎都是通过递归或者用栈辅助实现非递归的遍历.用二叉树作为存储结构时,取到一个节点,只能获取节点的左孩子和右孩子,不能直接得到节点的任一遍历序列的前驱或者后继. 为了保存这种在遍历中需要的信息,我们利用二叉树中指向左右子树的空指针来存放节点的前驱和后继信息.所以引入了线索化二叉树.下面我们讲一下线索化二叉树中序线索化的两种实现方法: (1).递归实现中序线索化二叉树 首先我们先看一下线索化二叉树的结构 enum PointerTag{ THREAD, LINK 

JAVA递归实现线索化二叉树

JAVA递归实现线索化二叉树 基础理论 首先,二叉树递归遍历分为先序遍历.中序遍历和后序遍历. 先序遍历为:根节点+左子树+右子树 中序遍历为:左子树+根节点+右子树 后序遍历为:左子树+右子树+根节点 (只要记住根节点在哪里就是什么遍历,且都是先左再右) 线索化 现在有这么一棵二叉树,它的数据结构由左节点+权+右节点构成. 可以看到4,5,6,7这几个节点的左右节点空间被浪费了.因此,线索化是指有效利用这些空间. 中序遍历的顺序为:4 2 5 1 6 3 7 现在引入前驱节点以及后继节点. 前

线索化二叉树的构建与先序,中序遍历(C++版)

贴出学习C++数据结构线索化二叉树的过程, 方便和我一样的新手进行测试和学习 同时欢迎各位大神纠正. 不同与普通二叉树的地方会用背景色填充 //BinTreeNode_Thr.h 1 enum PointTag 2 {Link,Thread}; 3 4 template<typename ElemType> 5 struct BinTreeNode 6 { 7 ElemType data; //数据元素 8 PointTag LTag,RTag; //左标志,右标志 9 BinTreeNode

线索化二叉树

二叉树的遍历运算是将二叉树中节点按一定规律线性化的过程,当二叉链表作为存储结构时,只能找到节点的左.右孩子信息,而不能直接得到节点在遍历序列中的前驱和后继信息.线索化二叉树能够解决这样的问题,将二叉链表中的空指针域填上相应节点的遍历前驱或后继节点的地址,而前驱和后继的地址只能在动态的遍历过程中才能得到.可以按照不同的遍历次序进行线索化,先序遍历.遍历过程中的节点的前驱.后继信息保存下来.下面是线索化二叉树节点结构: 其中,_left和_right表示节点的左.右孩子,_leftTag和_righ

中序线索化二叉树

中序线索化二叉树 1 void Tree::_inTree(Node * root, Node * &pre) { 2 if (root == NULL) { // 结点为空, 1:二叉树为空 2:已到达右子树的最后一个右结点的 rchild 3 return; 4 } 5 _inTree(root->lchild, pre); // 到达当前结点的左子树的底部左结点 6 if (root->lchild == NULL) { 7 root->ltag = nChild; //

数据--第37课 - 线索化二叉树

第37课 - 线索化二叉树 1. 问题 在一些项目中需要频繁的遍历二叉树,但是二叉树的遍历比单链表的遍历复杂多了,并且递归总是会后额外的开销. 2. 线索化二叉树 线索化二叉树指的是将二叉树中的结点进行逻辑意义上的“重新排列”,使其可以线性的方式访问每一个结点. 二叉树线索化后每个结点都有一个线性下标,用过这个下标可以快速访问结点,而不需要遍历二叉树. 3. 方法1 利用结点中的空指针,使其指向后继结点. 算法思想: 初始化位置指针:p = NULL: 前序遍历二叉树: 若p不为空,将p->le