body, table{font-family: 微软雅黑; font-size: 13.5pt}
table{border-collapse: collapse; border: solid gray; border-width: 2px 0 2px 0;}
th{border: 1px solid gray; padding: 4px; background-color: #DDD;}
td{border: 1px solid gray; padding: 4px;}
tr:nth-child(2n){background-color: #f8f8f8;}

对于一个有n个结点的二叉链表,每个结点有指向左右孩子的两个指针域,所以一共是2n个指针域,而n个结点的二叉树一共有n-1条分支线路,也就是说,存在2n-(n-1)= n+1个空指针域。
考虑利用那些空地址,存放指向结点在某种遍历次序下的前驱和后继结点的地址。  我们把这种指向前驱和后继的指针称为线索,加上线索的二叉链表称为线索链表,相应的二叉树就称为线索二叉树(Threaded Binary Tree)
对二叉树进行中序遍历后,将所有空指针域中的rchild改为指向它们的后继结点。
  ① H的后继是D
   ...
  ⑥ G的后继是NULL,中序遍历最后一个节点
此时共有6个空指针域被利用
将所有空指针域中的lchild改为指向当前节点的前驱。
  ① H的前驱是NULL,中序遍历的第一个结点
   ...
  ⑥ G的前驱是C
此时共有5个空指针被利用
最终,空心箭头实线为前驱,虚线黑箭头为后继。线索二叉树,等于是把一棵二叉树变成了一个双向链表,这样插入一个结点、查找一个结点都很方便。对二叉树以某种次序遍历使其变为线索二叉树的过程称为是线索化
为了区分lchild是指向左孩子还是前驱,rchild是指向右孩子还是后继,需要用一个标志位来区分
■ ltag为0时指向该结点的左孩子,为1时指向该结点的前驱
■ rtag为0时指向该结点的右孩子,为1时指向该结点的后继

二叉树输入过程:ABDH##I##EJ###CF##G## 中序遍历:HDIBJEAFCG
中序遍历: bac 中序遍历:CBAEDF
#include<iostream>
using namespace std;
enum TBT{child=0,thread};  //线索二叉树结点的指针是指向孩子还是前驱后继
typedef struct tbt
{
        struct tbt* lchild;
        enum TBT ltag;
        char data;
        enum TBT rtag;
        struct tbt* rchild;
}TBTreeNode,*pTBTree;
int createThreadedBinaryTree(pTBTree& root);
void inorderThreadingBinaryTree(const pTBTree& root);  //中序线索化二叉树
//在中序遍历的同时就线索化二叉树
void inorderThreadedBinaryTreeTraversal(pTBTree root); //中序线索化二叉树遍历
int main()
{
        TBTreeNode* root = nullptr;
        int ret = createThreadedBinaryTree(root);
        {
                if(0==ret)
                {
                        inorderThreadingBinaryTree(root);
                        cout<<endl;
                        inorderThreadingBinaryTreeTraversal(root);
                        cout<<endl;
                }
        }
        system("pause");
}
int createThreadedBinaryTree(pTBTree& root)
{
        char data;
        if(cin>>data)
        {
                if('#'==data)
                {
                        root = nullptr;
                        return -1;
                }
                else
                { //用data数据来初始化root结点,然后递归建立左子树和右子树
                        root = new TBTreeNode();  //创建结点的时候就把结点全部赋值为空
                        root->data = data;
                        createThreadedBinaryTree(root->lchild);
                        createThreadedBinaryTree(root->rchild);
                }
        }
        return 0;
}
static TBTreeNode* pre = nullptr;  //定义一个指针指向中序遍历当前访问结点的前一个访问结点
//线索化结点的后继要用到,因为中序遍历顺序:左子树,根结点,右子树
//前驱可以用刚刚访问过的结点直接赋值,后继还没有访问,这时候当前结点就是上一个访问结点pre的后继
//当然,前提条件是pre的右子树为空
//pre初始值为nullptr,因为从根结点开始访问,前一个访问结点就只能是空了
void inorderThreadingBinaryTree(const pTBTree& root)
{
        if(nullptr==root)
                return ;
        /*  参考中序遍历
        inorderTraversal(root->lchild);
        cout<<root->data<<" ";
        inorderTraversal(root->rchild);
        */
        inorderThreadingBinaryTree(root->lchild);  //中序遍历左子树
        //判断结点指针域可不可以线索化
        if(nullptr==root->lchild)  //如果左子树为空,就可以把指针域拿来线索化,指向前驱
        {
                root->lchild = pre;
                root->ltag = thread;
        }
        if(nullptr!=pre&&nullptr==pre->rchild)  //如果当前访问的根结点不为空,并且前面访问的结点pre右子树为空,线索化前一个结点的后继
        {
                pre->rchild = root;
                pre->rtag = thread;
        }
        //访问根结点就变成修改前一个访问结点指针pre
        pre = root;  //之后要访问右子树,当前结点自然就是pre
        inorderThreadingBinaryTree(root->rchild);  //中序遍历右子树
}
void inorderThreadedBinaryTreeTraversal(pTBTree root)
{
        if(nullptr==root)
                return;
        while(nullptr!=root)
        {
                while(nullptr!=root->lchild&&child==root->ltag)  //两个条件,区别中序遍历第一个结点的前驱是nullptr
                {//搜寻从根结点开始的左子树的最后一个节点
                        root = root->lchild;
                }
                cout<<root->data<<" ";  //输出根结点
                while(thread==root->rtag)  //该结点有后继,意味着没有右子树
                {
                        cout<<root->rchild->data<<" ";  //直接输出后继,也就是中序遍历当前结点下一个要访问的结点的值
                        root = root->rchild;  //根结点回溯到后继
                }
                //该结点有右子树,root->rtag==child,左子树已经遍历完了,这里进入右子树
                root = root->rchild;  //重复上面的操作
        }
}

图解中序遍历线索化二叉树,中序线索二叉树遍历,C\C++描述的更多相关文章

  1. 图解前序遍历线索化二叉树,前序线索二叉树遍历,C\C++描述

    body, table{font-family: 微软雅黑; font-size: 13.5pt} table{border-collapse: collapse; border: solid gra ...

  2. YTU 3026: 中序线索化二叉树

    原文链接:https://www.dreamwings.cn/ytu3026/2896.html 3026: 中序线索化二叉树 时间限制: 1 Sec  内存限制: 128 MB 提交: 9  解决: ...

  3. JAVA递归实现线索化二叉树

    JAVA递归实现线索化二叉树 基础理论 首先,二叉树递归遍历分为先序遍历.中序遍历和后序遍历. 先序遍历为:根节点+左子树+右子树 中序遍历为:左子树+根节点+右子树 后序遍历为:左子树+右子树+根节 ...

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

    贴出学习C++数据结构线索化二叉树的过程, 方便和我一样的新手进行测试和学习 同时欢迎各位大神纠正. 不同与普通二叉树的地方会用背景色填充 //BinTreeNode_Thr.h enum Point ...

  5. 后序线索化二叉树(Java版)

    前面介绍了前序线索化二叉树.中序线索化二叉树,本文将介绍后序线索化二叉树.之所以用单独的一篇文章来分析后序线索化二叉树,是因为后序线索化二叉树比前序.中序要复杂一些:另外在复习线索化二叉树的过程中,大 ...

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

    先看一个问题 将数列 {1, 3, 6, 8, 10, 14  } 构建成一颗二叉树 问题分析: 当我们对上面的二叉树进行中序遍历时,数列为 {8, 3, 10, 1, 6, 14 } 但是 6, 8 ...

  7. C#数据结构-线索化二叉树

    为什么线索化二叉树? 对于二叉树的遍历,我们知道每个节点的前驱与后继,但是这是建立在遍历的基础上,否则我们只知道后续的左右子树.现在我们充分利用二叉树左右子树的空节点,分别指向当前节点的前驱.后继,便 ...

  8. 【算法】二叉树、N叉树先序、中序、后序、BFS、DFS遍历的递归和迭代实现记录(Java版)

    本文总结了刷LeetCode过程中,有关树的遍历的相关代码实现,包括了二叉树.N叉树先序.中序.后序.BFS.DFS遍历的递归和迭代实现.这也是解决树的遍历问题的固定套路. 一.二叉树的先序.中序.后 ...

  9. 算法进阶面试题03——构造数组的MaxTree、最大子矩阵的大小、2017京东环形烽火台问题、介绍Morris遍历并实现前序/中序/后序

    接着第二课的内容和带点第三课的内容. (回顾)准备一个栈,从大到小排列,具体参考上一课.... 构造数组的MaxTree [题目] 定义二叉树如下: public class Node{ public ...

随机推荐

  1. 监听浏览器种类,并区分safari和chrom浏览器

    //判断浏览器种类函数-处理兼容性 function myBrowser(){ var userAgent = navigator.userAgent; //取得浏览器的userAgent字符串 va ...

  2. 第 8 章 容器网络 - 055 - 创建 macvlan 网络

    1.创建 macvlan 网络 在 host1 和 host2 中创建 macvlan 网络 mac_net1: docker network create -d macvlan --subnet=1 ...

  3. 第 2 章 容器架构 - 008 - Docker 组件如何协作?

    容器启动过程如下: Docker 客户端执行 docker run 命令. Docker daemon 发现本地没有 httpd 镜像. daemon 从 Docker Hub 下载镜像. 下载完成, ...

  4. 20165303 预备作业3 Linux安装及学习

    =20165303 预备作业3 Linux安装及学习 安装虚拟机 按照http://www.cnblogs.com/rocedu/p/6012545.html步骤进行逐步操作,安装虚拟机. 遇到的问题 ...

  5. Java中的参数传递 --Java

    1.基本类型传值,对象类型传地址 按值传递:当将一个参数传递给一个方法时,方法接收的是原始值的一个副本.因此,如果方法修改了该参数,仅改变副本,而原始值保持不变. 按引用传递:当将一个参数传递给一个方 ...

  6. IntelliJ IDEA的这些配置,你值得拥有

    一.自动编译开关 二.忽略大小写开关 IDEA默认是匹配大小写,此开关如果未关.你输入字符一定要符合大小写.比如你敲string是不会出现代码提示或智能补充.但是,如果你开了这个开关,你无论输入Str ...

  7. p2693 Combination Lock

    深搜,注意模n.用set去重. #include <iostream> #include <cstdio> #include <cmath> #include &l ...

  8. Flex动画效果的用法--Resize

    Flex动画效果的用法--Resize FlexAdobeXML  <?xml version="1.0" encoding="utf-8"?> & ...

  9. PHP流程控制笔记

    一.运算符(Operator) 1.运算符 2.运算符分类   (1)按功能分   (2)按操作数个数分 3.按功能分   (1)算术运算符   (2)递增递减   (3)字符运算符   (4)赋值运 ...

  10. Linux命令详解-file

    file命令用来识别文件类型,也可用来辨别一些文件的编码格式.它是通过查看文件的头部信息来获取文件类型,而不是像Windows通过扩展名来确定文件类型的. 1.命令格式: file [ -bchikL ...