文章目录

  • 前言
  • 一、中序遍历的特点:投影
  • 二、中序线索二叉树
  • 三、代码思路
  • 三、代码

前言

在N个节点的二叉树中,每个节点有2个指针,所以一共有2N个指针,除了根节点以外,每一个节点都有一个指针从它的父节点指向它,所以一共使用了N-1个指针,所以剩下2N-(N-1)也就是N+1个空指
针;

如果能利用这些空指针域来存放指向该节点的直接前驱或是直接后继的指针,则可由此信息直接找到在该遍历次序下的前驱节点或后继节点,从而比递归遍历提高了遍历速度,节省了建立系统递归栈所使用的存储空间;

这些被重新利用起来的空指针就被称为线索(Thread),加上了线索的二叉树就是线索二叉树。

实现思路:按某种次序遍历二叉树,在遍历过程中用线索取代空指针即可。以中序遍历为例,首先找到中序遍历的开始节点,然后利用线索依次查找后继节点即可。

由于它充分利用了空指针域的空间(等于节省了空间),又保证了创建时的一次遍历就可以终生受用前驱、后继的信息(这意味着节省了时间),所以在实际问题中,如果所使用的二叉树需要经常遍历或查找节点时需要某种遍历中的前驱和后继,那么采用线索二叉链表的存储结构就是不错的选择。


一、中序遍历的特点:投影

对于上面的一棵二叉树,我们进行中序遍历,即先左节点,根节点,再右节点。

可以得到中序遍历的次序为:3,6,8,28,29,36,38,41,49,52,78,93.

可以看到,二叉树的投影序列,就是中序遍历的序列。


同时我们还看到一个现象,如下图。

根据投影的定义,根节点(73)的前驱节点,应该是根节点左边最靠近它的节点。

可以看到,垂线2相比垂线3,垂线2肯定比垂线3更靠近根节点(73),因为垂线2的节点(70)是垂线3的节点(62)的右节点;并且,节点(54)永远只能无线逼近节点(62)。因此,节点(70)的投影永远是最靠近根节点(73),节点(70)就是根节点(73)的前驱节点。


二、中序线索二叉树

根据中序遍历的定义,中序线索二叉树应该是这样的:

中序遍历下,找某个节点的前驱节点,就是某个节点的左子节点不断向右查找。当前节点的左节点的最右节点,就是当前节点的前驱节点,示意图如下。


看标记①,它的前驱节点是,从它的左节点不断向右查找,一直找到标记②,此时②就是①的前驱节点。

其他的类似标记③,它的前驱节点从它的左节点向右查找,由于标记④右节点为空,因此④就是③的前驱节点。


三、代码思路

主要思路其实就是两个:建立线索,拆掉线索。

因为建立线索的过程中,改变了原有二叉树的结构,叶子节点的空指针指向了其他节点。所以要记得,建立线索后,还要拆掉建立的线索。

怎么记住建立的线索,并且把建立的线索拆掉?

方法就是,每次拿到当前节点(curr_node),就去寻找当前节点(curr_node)的前驱节点(即当前节点的左节点,不断向右查找)。如果在找前驱节点的过程中,一直找到NULL空节点,说明当前节点(curr_node)还没有建立线索,因此就找到了当前节点(curr_node)的前驱节点;如果查找前驱节点的过程中,发现前驱节点指向了当前节点(curr_node),此时就应该断开指向当前节点(curr_node)的线索。于是当前节点(curr_node)的右节点就成为新的当前节点(curr_node)。


举例,如下图。

因为我们是中序线索二叉树,当前节点(curr_node)为root,我们先找当前节点(curr_node)的前驱节点,找到了前驱节点(29),此时前驱节点(29)指向当前节点(curr_node)。于是当前节点(curr_node)的左节点(6)成为新的当前节点(curr_node)。

当前节点(curr_node)(6)继续找前驱节点,找到了前驱节点(3)。于是节点(3)成为新的当前节点(curr_node)。

当前节点(curr_node)(3)找前驱节点,它的左节点为空,因此没有前驱节点。于是当前节点(curr_node)(3)的右节点成为新的当前节点(curr_node)(6)。

当前节点(curr_node)(6)找前驱节点,发现节点(3)的右节点与当前节点(curr_node)相同,因此此时应该拆掉这个“线索”,节点(3)的右节点变为空,然后当前节点(curr_node)(6)的右节点(29)成为新的当前节点(curr_node)。

以此类推。


三、代码

本质上,因为对二叉树的遍历都是采用中序线索二叉树,所以建立线索二叉树的代码都是一样,只是不同的打印处理才有了不同的前序/中序/后序遍历顺序。

前序遍历(根节点,左节点,右节点)的时机发生在:
1)当前节点找到它的前驱节点时,当前节点就打印;
2)当前节点的左节点为空,当前节点就打印;

中序遍历(左节点,根节点,右节点)的时机发生在:
1)当前节点的左节点为空,当前节点就打印;
2)当前节点断开线索时,当前节点就打印;


前序遍历:

//前序遍历
int* preorderTraversal(struct TreeNode* cur, int* returnSize)
{*returnSize = 0;if (cur == NULL){return NULL;}int *array = (int*)malloc(sizeof(int) * 100);struct TreeNode* mostRight = NULL;while (cur != NULL){//cur表示当前节点,mostRight 表示 cur 的左孩子的最右节点,就是当前节点的前驱节点mostRight = cur->left;if (mostRight != NULL){// cur 有左孩子,找到 cur 左子树的最右节点while (mostRight->right != NULL && mostRight->right != cur){mostRight = mostRight->right;}//建立线索//mostRight的右孩子指向空,让其指向 cur,cur 向左移动if (mostRight->right == NULL){mostRight->right = cur;array[(*returnSize)++] = cur->val;cur = cur->left;continue;}else{  //删除线索//mostRight 的右孩子指向 cur,让其指向空,cur 向右移动mostRight->right = NULL;}}else{array[(*returnSize)++] = cur->val;}cur = cur->right;}return array;
}

中序遍历:

//中序遍历
int* inorderTraversal(struct TreeNode* cur, int* returnSize)
{*returnSize = 0;if (cur == NULL){return NULL;}int *array = (int*)malloc(sizeof(int) * 100);struct TreeNode* mostRight = NULL;while(cur != NULL){if (mostRight = cur->left){while (mostRight->right != NULL && mostRight->right != cur){mostRight = mostRight->right;}// 建立线索if (mostRight->right == NULL){mostRight->right = cur;cur = cur->left;continue;}else{//删除线索mostRight->right = NULL;array[(*returnSize)++] = cur->val;}}else{array[(*returnSize)++] = cur->val;}cur = cur->right;}return array;
}

后序遍历:

//翻转链表
struct TreeNode* reverseNode(struct TreeNode* cur)
{struct TreeNode* head = NULL;struct TreeNode* new_head = NULL;while (cur){head = cur->right;cur->right = new_head;new_head = cur;cur = head;}return new_head;
}//打印
void printNode(struct TreeNode* head, int* returnSize, int *array)
{struct TreeNode* tail = reverseNode(head);struct TreeNode* cur = tail;while (cur){array[(*returnSize)++] = cur->val;cur = cur->right;}reverseNode(tail);
}int* postorderTraversal(struct TreeNode* cur, int* returnSize)
{*returnSize = 0;if (cur == NULL){return NULL;}int *array = (int*)malloc(sizeof(int) * 100);struct TreeNode* head = cur;struct TreeNode* mostRight = NULL;while(cur != NULL){if (mostRight = cur->left){while (mostRight->right != NULL && mostRight->right != cur){mostRight = mostRight->right;}// 建立线索if (mostRight->right == NULL){mostRight->right = cur;cur = cur->left;continue;}else{//删除线索mostRight->right = NULL;printNode(cur->left, returnSize, array);}}cur = cur->right;}printNode(head, returnSize, array);return array;
}

二叉树的遍历 中序线索二叉树相关推荐

  1. C语言实现二叉树的中序线索化及遍历中序线索二叉树

    C语言实现二叉树的线索化以及如何遍历线索二叉树! 文章目录 线索二叉树的结构及数据类型定义 根据输入结点初始化二叉树 中序遍历二叉树并线索化 遍历中序线索二叉树 项目完整代码 项目完整代码(改进版) ...

  2. 中序线索二叉树(C语言实现)

    目录 1.线索二叉树的定义: 2.线索二叉树的存储结构: 3.创建中序线索二叉树 4.遍历中序线索二叉树 5.在中序线索二叉树上查找任意结点的中序前驱结点 6.在中序线索二叉树上查找任意结点的中序后继 ...

  3. 中序线索二叉树的创建、线索化和遍历

    [问题描述]创建一棵二叉树,接着中序线索化该二叉树,然后编写相应函数遍历该中序线索二叉树 [编码要求]线索二叉树遍历过程中不能使用递归.不能使用栈. [输入形式]二叉树拓展的前序遍历序列 [输出形式] ...

  4. 线索二叉树:中序线索二叉树的遍历

    线索二叉树:中序线索二叉树的遍历 作者: 冯向阳时间限制: 1S章节: DS:树 截止日期: 2022-06-30 23:55:00 问题描述 : 目的:使用C++模板设计中序线索二叉树的抽象数据类型 ...

  5. 通过中序线索二叉树找某节点的后续前驱☆

    题目:写出在中序线索二叉树里查找指定节点在后序的前驱结点的算法 分析:         在后序序列中,若节点p有右子女,则右子女是其前驱,若无右子女而有左子女,则左子女是其前驱.若节点p左右子女均无, ...

  6. 线索二叉树 C语言 数据结构 先序线索二叉树 中序线索二叉树 后序线索二叉树

    在二叉树的结点上加上线索的二叉树称为线索二叉树,对二叉树以某种遍历方式(如先序.中序.后序或层次等)进行遍历,使其变为线索二叉树的过程称为对二叉树进行线索化. 文章目录 一.c语言实现先序线索.中序线 ...

  7. 为什么先序/中序线索二叉树不需要栈的支持,而后序线索二叉树需要栈的支持?

    为什么先序/中序线索二叉树不需要栈的支持,而后序线索二叉树需要栈的支持? 首先要明确两点 先序线索二叉树的缺点:无法找到先序序列中某结点的前驱 后序线索二叉树的缺点:无法找到后序序列中某结点的后继 中 ...

  8. C/C++面试题—重建二叉树【前序 + 中序- 重建二叉树 和 后序 + 中序 - 重建二叉树】

    题目介绍 题目:输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树.假设输入的前序遍历和中序遍历的结果中都不含重复的数字. 例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{ ...

  9. 先序abdfcegh 中序bfdagehc 后序线索二叉树_二叉树的遍历和线索二叉树

    二叉树的遍历是指按某条搜索路径访问树中的每个结点,使得每个结点均被访问一次,且只被访问一次. 先序遍历(NLR) 若二叉树为空,则什么也不做:否则, (1)访问根结点. (2)先序遍历左子树. (3) ...

最新文章

  1. Git使用4:Git分支
  2. 查看Oracle中存储过程长时间被卡住的原因
  3. continue和break的区别
  4. 连线IBM大数据案例 让大数据接地气
  5. nuxt引用static或者assets目录下资源注意事项
  6. C++离航篇——内存的申请释放
  7. c语言程序设计华北电力大学,2016年华北电力大学电气与电子工程学院C语言程序设计(同等学力加试)考研复试题库...
  8. synchronized 修饰static方法
  9. Python机器学习:KNN算法05f超参数
  10. 支付宝首页新增商家服务进度卡片 目前正在灰度测试中
  11. 列表生成式的复习以及生成器的练习, 杨辉三角实例(非常巧妙)
  12. .Net与 WebAssembly 随笔
  13. 工业机器人cloos_半年报点评:并表cloos,国内工业机器人龙头蓄势待发
  14. 虚拟机ubuntu与真实机实现实现复制粘贴、传输文件
  15. 张正友标定法代码解释
  16. java 美元符号_使用单个美元符号“$”作为java类名称的任何风险?
  17. Linux 系统编程 --文件IO-write()、read()、lseek()函数
  18. SSH远程控制与访问
  19. 卡罗拉更换变速油教程
  20. 服务器CPU占用过高处理方案

热门文章

  1. java 阴历阳历转换
  2. 如何测量两组汇编指令的执行效率
  3. JavaScript 是如何工作的:模块的构建以及对应的打包工具
  4. NDK交叉编译qemu报错:undefined symbol: memfd_create
  5. 各大数据库厂商怎样看竞争对手
  6. 程序员常用远程工具有哪些?
  7. echarts tooltips 自定义 formatter 显示图例颜色
  8. vue-element-admin入坑之切换中文版
  9. 浅谈幼儿园计算机论文,浅谈幼儿园科学教育活动中,计算机的辅助教学
  10. 物联网通信技术第7章 自组织网络(非常全,持续更新)