一,基本概念

线索二叉树,即在二叉链表的基础上,将二叉链表的空指针域指向其前驱节点和后继节点

线索:将二叉链表中的空指针域指向前驱结点和后继结点的指针被称为线索
线索化:使二叉链表中结点的空链域存放其前驱或后继信息的过程称为线索化
线索链表:加上线索的二叉链表称为线索链表(或线索二叉树)

二,线索二叉树的构建(中序)

(1)结点结构

可以发现,相较于原来的二叉链表,这里在数据结构里添加了两个int类型,来规定左右指针的作用

由下图可见,有的左右指针是连接的子树,没有链接左右子树的指针现在就通过红色的虚线指向了该结点的前驱或者后继(中序)(图片参考懒猫老师《数据结构》相关课程笔记)

下图中序遍历:DGBAECF

特别的是,对于D和F,D是中序遍历的开头结点,它没有前驱节点;F是中序遍历的结尾的结点,它没有后继节点,那么这时,D的左指针为NULL,ltag=1;F的右指针为NULL,rtag=1

(2)链表创建过程

首先先初始化建立一个二叉树,具体相关步骤在这篇文章中可以找到~

二叉树的遍历和创建https://blog.csdn.net/m0_63223213/article/details/125803613?spm=1001.2014.3001.5501再通过以下步骤创建一个线索二叉树:(下图是中序线索二叉树的结构示意)

具体过程如下:

p:正在访问的结点

pre:刚访问的结点

按照中序遍历的顺序递归进行上面的步骤,就可以将所有NULL的指针域都赋上前驱或者后继,从而实现线索二叉树的构建

(这里特别记录下,因为要递归调用,普通声明的话,pre指针的值每次调用都会被初始化,从而产生错误;解决方案有两种,一种是使用static类型声明,后续pre的值就是正确变化的,另一种是在函数声明中再添加一个参数BiNode **pre,递归调用时也传地址,就不会出错)

代码实现:

void ThreadBiTree_mid(BiNode *p) { //树的中序遍历,即中序线索二叉树的构建static BiNode *pre = NULL; //递归的过程中不会被重复的初始化if (p == NULL) //递归调用的出口return;ThreadBiTree_mid(p->Lchild);//左子树线索化if (p->Lchild == NULL) { //建立p的前驱线索p->Ltag = 1;p->Lchild = pre;}if (pre != NULL && pre->rchild == NULL) { //建立pre的后继线索pre->rtag = 1;pre->rchild = p;}pre = p;ThreadBiTree_mid(p->rchild);//右子树线索化
}

(3)寻找结点的前驱/后继结点

寻找的规则是,如果结点的Ltag==1代表可以直接找到前驱节点;rtag==1代表可以直接找到后继结点,否则,说明二叉树该结点有左子树或者右子树;遵循以下规则来找前驱/后继:

代码实现:

BiNode *Findnext(BiNode *root) { //中序线索链表中查找传入结点的后继BiNode *q = NULL;if (root == NULL)return NULL;if (root->rtag == 1) { //右标志为1,可直接得到后继结点q = root->rchild;} else { //右标志为0,说明有右子树,则寻找右子树最左下角的结点q = root->rchild;while (q->Ltag == 0) //查找最左下角的位置q = q->Lchild;}return q;//返回后继结点
}BiNode *Findprior(BiNode *root) { //中序线索链表中查找传入结点的前驱BiNode *q;if (root == NULL)return NULL;if (root->Ltag == 1) { //左标志为1.可直接得到前驱结点q = root->Lchild;} else { //左标志为0,说明有左子树,则寻找左子树最右下角的结点q = root->Lchild;while (q->rtag == 0)q = q->rchild;}return q;
}

三,完整代码

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef char Datatype;//测试用例:ABD#F##G##C#E##
typedef struct BiNode {Datatype data;//数据内容struct BiNode  *Lchild;//指向左孩子结点struct BiNode  *rchild;//指向右孩子结点int Ltag;//值为0,则Lchild指向该结点的左孩子;值为1.指向该结点的前驱结点int rtag;//值为0,则rchild指向该结点的右孩子;值为1.指向该结点的后继结点
} BiNode ;BiNode *Creat(char *str, int *i, int len) { //树的创建struct BiNode *bt = NULL;char ch = str[(*i)++];if (ch == '#' || *i >= len) {bt = NULL;} else {bt = (struct BiNode *)malloc(sizeof(BiNode));if (bt != NULL) {bt->data = ch;bt->Ltag = 0;bt->rtag = 0;bt->Lchild = Creat(str, i, len); //这里的递归要赋值,这样才能建立不同域中的连接关系bt->rchild = Creat(str, i, len);}}return bt;//返回的一直是根结点
}void MidOrder(BiNode *bt) { //非线索二叉树的中序遍历if (bt == NULL) //递归调用的出口return;else {MidOrder(bt->Lchild);//前序递归遍历bt的左子树visit(bt->data);//访问根节点bt的数据域MidOrder(bt->rchild);//前序递归遍历bt的右子树}
}void ThreadBiTree_mid(BiNode *p) { //树的中序遍历,即中序线索二叉树的构建static BiNode *pre = NULL; //递归的过程中不会被重复的初始化if (p == NULL) //递归调用的出口return;ThreadBiTree_mid(p->Lchild);//左子树线索化if (p->Lchild == NULL) { //建立p的前驱线索p->Ltag = 1;p->Lchild = pre;}if (pre != NULL && pre->rchild == NULL) { //建立pre的后继线索pre->rtag = 1;pre->rchild = p;}pre = p;ThreadBiTree_mid(p->rchild);//右子树线索化
}BiNode *Findnext(BiNode *root) { //中序线索链表中查找传入结点的后继BiNode *q = NULL;if (root == NULL)return NULL;if (root->rtag == 1) { //右标志为1,可直接得到后继结点q = root->rchild;} else { //右标志为0,说明有右子树,则寻找右子树最左下角的结点q = root->rchild;while (q->Ltag == 0) //查找最左下角的位置q = q->Lchild;}return q;//返回后继结点
}BiNode *Findprior(BiNode *root) { //中序线索链表中查找传入结点的前驱BiNode *q;if (root == NULL)return NULL;if (root->Ltag == 1) { //左标志为1.可直接得到前驱结点q = root->Lchild;} else { //左标志为0,说明有左子树,则寻找左子树最右下角的结点q = root->Lchild;while (q->rtag == 0)q = q->rchild;}return q;
}void visit(Datatype e) {printf("%c ", e);
}void inOrder(BiNode *root) { //中序线索链表的(顺着的)遍历算法BiNode *p;if (root == NULL) //根结点return;p = root;
//  while (p->Ltag == 0) //查找中序遍历的第一个结点pwhile (p->Lchild != NULL)p = p->Lchild;visit(p->data);//访问第一个结点while (p->rchild != NULL) { //当p结点存在后继p = Findnext(p);visit(p->data);}
}void reverseinOrder(BiNode *root) { //中序线索链表的(逆着的)遍历算法BiNode *p;if (root == NULL) //根结点return;p = root;while (p->rchild != NULL) //查找中序遍历的最后一个结点pp = p->rchild;visit(p->data);//访问最后一个结点while (p->Lchild != NULL) { //当p结点存在前驱p = Findprior(p);visit(p->data);}}void freeThread(BiNode *p) {BiNode *pre;if (p == NULL) //根结点return;while (p->Lchild != NULL)p = p->Lchild;//寻找中序遍历第一个结点while (p->rchild != NULL) { //当p结点存在后继pre = Findnext(p);free(p);p = pre;}printf("二叉树释放成功!\n");
}main() {BiNode *bt;int i = 0, len;char str[50];printf("输入一个字符串用于建立二叉树:");scanf("%s", str);len = strlen(str);bt = Creat(str, &i, len);printf("测试输出初始中序二叉树遍历结果:");MidOrder(bt);printf("\n");printf("构建线索二叉树:\n");ThreadBiTree_mid(bt);//构建中序线索二叉树printf("测试顺序输出中序线索二叉树遍历结果:");inOrder(bt);printf("\n");printf("测试逆序输出中序线索二叉树遍历结果:");reverseinOrder(bt);printf("\n");freeThread(bt);
}

测试输出:

初学小白,有错误欢迎指正喔!~

线索二叉树(线索链表遍历,二叉树线索化)相关推荐

  1. sdut 3341数据结构实验之二叉树二:遍历二叉树

    数据结构实验之二叉树二:遍历二叉树 Time Limit: 1000MS Memory Limit: 65536K Problem Description 已知二叉树的一个按先序遍历输入的字符序列,如 ...

  2. java创建二叉树并递归遍历二叉树

    二叉树类代码: package binarytree;import linkqueue.LinkQueue;public class BinaryTree {class Node{public Obj ...

  3. 树与二叉树——后序遍历二叉树的非递归算法

    算法思想:后序非递归遍历二叉树是先访问左子树,再访问右子树,最后访问根节点. ①若根节点有左孩子,则左孩子以此入栈,直到左孩子为空,然后读取栈顶元素,若其右孩子不为空且未被访问过,则对右子树执行①.否 ...

  4. 二叉树的前序遍历,二叉树的中序遍历,二叉树的后序遍历,二叉树的层序遍历

    二叉树的前序遍历 144. 二叉树的前序遍历 - 力扣(LeetCode) (leetcode-cn.com) 给你二叉树的根节点 root ,返回它节点值的 前序 遍历. 示例 1: 输入:root ...

  5. 数据结构实验之二叉树二:遍历二叉树

    Description 已知二叉树的一个按先序遍历输入的字符序列,如abc,de,g,f, (其中,表示空结点).请建立二叉树并按中序和后序的方式遍历该二叉树. Input 连续输入多组数据,每组数据 ...

  6. java层次遍历建立二叉树_java层次遍历二叉树

    思路很简单.通过队列,先将头结点放入队列,再遍历每个节点的左节点和右节点. import java.util.ArrayList; import java.util.LinkedList; /** * ...

  7. 层序创建二叉树,层序遍历二叉树

    在学习树的过程中发现,他们都有一个共同的特点,无论是在创建时还是遍历时,都是需要先父母再左孩子右孩子的顺序如图 层序遍历时顺序为A->B->C->D->E->F-> ...

  8. 数据结构之遍历二叉树

    遍历二叉树 在二叉树的一些应用中,常常要求在树中查找具有某种特征的结点.这就提出遍历二叉树的问题,即如何按某条搜索路径巡访树中每个结点,使得每个结点均被访问一次,而且仅被访问一次.遍历对线性结构来说, ...

  9. 遍历二叉树最全面讲解

    前言 这是我听老师讲课做的笔记. 作者:RodmaChen 关注我的csdn博客,更多数据结构与算法知识还在更新 看这篇文章之前可以先看:树和二叉树的定义 遍历二叉树 一. 遍历二叉树 1.介绍 2. ...

  10. 002 前、中、后序遍历二叉树(递归迭代)

    1.递归法 ① 前序遍历二叉树 ② 中序遍历二叉树 ③ 后序遍历二叉树 2.迭代法 ① 前序遍历二叉树 ② 中序遍历二叉树 ③ 后序遍历二叉树 类似前序遍历,先"中→右→左"遍历二 ...

最新文章

  1. linux查看地址和,UNIX/LINUX平台下查看MAC和WWN地址的方法
  2. Android和JS之间互相调用方法并传递参数
  3. Linux下和Windows下创建Oracle数据库,表空间,Oracle用户
  4. 关于Linux你了解多少?Linux由来!
  5. java继承对象转换_java中类与对象的继承重写,存储以及自动转换和强制转换。...
  6. .php t=,关于php:意外的T_VARIABLE,期望T_FUNCTION
  7. 【我的Android进阶之旅】Android自定义Lint实践
  8. java 80_【JavaWeb】80:js基础详解
  9. 前端学习(2669): vue3.0实战开始建立新项目
  10. 考二级计算机专业哪个科目好,计算机二级考哪个科目比较好?
  11. 又被ESLint 调戏了!!! ESLint:Newline required at end of file but not found. eslint(eol-last) [12, 22]
  12. P ⊆ co-NP的原因
  13. 使用docker运行Mysql客户端
  14. pokemon go 和 虚拟gps
  15. vant修改用户头像
  16. CDR 制作“决战高考”海报
  17. LeetCode 分割整数数组,分割为两部分的和相等
  18. 新买的显示器怎么测试软件,新买的电视如何检测屏幕?记住这个方法
  19. 【AI理论学习】多模态介绍及当前研究方向
  20. 使用HTML语言和CSS开发商业站点

热门文章

  1. 华为数通笔记-路由策略
  2. 2011年1月7日大恒图像面试总结
  3. (CMake) 从下载到构建第一个CMake应用
  4. IE浏览器兼容jsp问题
  5. Python源代码保密、加密、混淆
  6. 【大数据之Hadoop3.x】
  7. [转帖]EventHandler部署工具
  8. Java应用通过wsdl文件调用webservice
  9. 分析建模:数据模型、功能模型、行为模型
  10. bandzip与WinRAR