线索化二叉树

先看一个问题来理解什么是线索化二叉树

有一个数列存的是科大学生的面试成绩{1,3,6,8,10,14},将他构建成一颗二叉树

我们发现就是一个完全二叉树

问题是什么?

当我们对上面的二叉树进行中序遍历时,数列为{8,3, 10,1,14,6}但是6, 8, 10,14这几个节点的左右指针,并没有完全的利用上.如果我们希望充分的利用各个节点的左右指针,让各个节点可以指向自己的前后节点怎么办?

基本介绍

1.n个结点的二叉链表中含有n+1公式2n-(n-1)=n+1空指针域利用二叉链表中的空指针域,**存放指向该结点在某种遍历次序下的前驱和后继结点的指针(**这种附加的指针称为"线索")

这种加上了线索的二叉链表称为线索链表,相应的二叉树称为线索二叉树(Threaded BinaryTree)。根据线索性质的不同,线索二叉树可分为前序线索二叉树、中序线索二叉树和后序线索二叉树三种

怎么说呢?怎么理解进行中序遍历时,数列为{8,3, 10,1,14,6}

8这个节点没利用完全,在中序遍历时,他就可以在增加一个后继节点指向3

6这个节点有一个没利用,在中序遍历时,他就可以增加一个前驱节点指向1

请完成中序遍历线索化二叉树的代码

有一些容易误导的地方,比如说1,他如果按照中序遍历线索化二叉树,它的前驱节点不应该指向10么?但是他的左指针明明指向的是3呀

说明:

当线索化二叉树后, Node节点的属性left和right,有如下情况:

1.left指向的是左子树也可能是指向的前驱节点.比如①节点left指向的左子树,而10节点的left指向 的就是前驱结点

2.right指向的是右子树,也可能是指向后继节点,比如①节点right指向的是右子树,而10节点的right指向的是后继节点.

代码实现

package 树;
//线索化二叉树
//2021年1月27日15:48:29
//作者  王
public class ThreadedBinnaryTreeDemo {public static void main(String[] args) {//测试中序先锁二叉树HeroNode1 root = new HeroNode1(1, "tom");HeroNode1 node2 = new HeroNode1(3, "jack");HeroNode1 node3 = new HeroNode1(6, "smith");HeroNode1 node4 = new HeroNode1(8, "wang");HeroNode1 node5 = new HeroNode1(10, "huang");HeroNode1 node6 = new HeroNode1(14, "taotao");root.setLeft(node2);root.setRight(node3);node2.setLeft(node4);node2.setRight(node5);node3.setLeft(node6);ThreadedBinnaryTree binnaryTree = new ThreadedBinnaryTree();binnaryTree.setRoot(root);binnaryTree.threadedNodes();//测试10号节点HeroNode1 leftNode = node5.getLeft();System.out.println("10号节点的前驱节点是"+leftNode);System.out.println("10号节点的后继节点是"+node5.getRight());}
}//先创建HeroNode节点
class HeroNode1{private int no;private String name;private HeroNode1 left;private HeroNode1 right;//用来标识这个指针指向的是子树还是前驱结点//如果leftType == 0 表示指向的是左子树,如果等于1则表示指向前驱结点//如果rightType == 0 表示指向的是右子树,如果等于1则表示指向后继结点private int leftType;private int rightType;public HeroNode1(int no, String name) {super();this.no = no;this.name = name;}public int getNo() {return no;}public void setNo(int no) {this.no = no;}public String getName() {return name;}public void setName(String name) {this.name = name;}public HeroNode1 getLeft() {return left;}public void setLeft(HeroNode1 left) {this.left = left;}public HeroNode1 getRight() {return right;}public void setRight(HeroNode1 right) {this.right = right;}public int getLeftType() {return leftType;}public void setLeftType(int leftType) {this.leftType = leftType;}public int getRightType() {return rightType;}public void setRightType(int rightType) {this.rightType = rightType;}@Overridepublic String toString() {return "HeroNode1 [no=" + no + ", name=" + name + "]";}//编写前序遍历方法public void preOrder(){System.out.println(this);//先输出父节点//递归向左子树前序遍历if(this.left != null){this.left.preOrder();}//递归向右子树前序遍历if(this.right != null){this.right.preOrder();}}//中序遍历public void infixOrder(){//递归向左子树中序遍历if(this.left != null){this.left.infixOrder();}System.out.println(this);//输出父节点//向右递归中序遍历子树if(this.right != null){this.right.infixOrder();}}//后序遍历public void postOrder(){if(this.left != null){this.left.postOrder();}if(this.right != null){this.right.postOrder();}System.out.println(this);//输出父节点}/*** 前序遍历查找* @param no  待查找编号* @return*/public HeroNode1 preOrderSearch(int no){//判断当前节点是不是if(this.no == no){return this;}//判断左子节点是否为空HeroNode1 resNode = null;if(this.left != null){resNode = this.left.preOrderSearch(no);}if(resNode != null){//说明左子树上找到了return resNode;}if(this.right != null){resNode = this.right.preOrderSearch(no);}return resNode;}//中序遍历查找public HeroNode1 infixOrderSearch(int no){HeroNode1 resNode = null;//先判断当前节点的左子节点if(this.left != null){resNode = this.left.infixOrderSearch(no);}if(resNode != null){return resNode;}if(this.no == no){return this;}if(this.right != null){resNode = this.right.infixOrderSearch(no);}return resNode;}//后序遍历查找public HeroNode1 postOrderSearch(int no){HeroNode1 resNode = null;if(this.left != null){resNode = this.left.postOrderSearch(no);}if(resNode != null){return resNode;}if(this.right != null){resNode = this.right.postOrderSearch(no);}if(resNode != null){return resNode;}if(this.no == no)return this;return resNode;}//递归删除节点//1.删除叶子结点的话,我们直接删除就OK了//2.如果删除的节点是非叶子节点,则要删除这个子树public void deleteHeroNode(int no){//思路如果当前节点的左子节点不为空,//并且左子节点就是需要删除的节点,也就是说要删除油哥,//我们要找到小黄,判断小黄的左子节点是不是需要删除的节点,//是的话就将this.left = null;没有找到就去当前节点的右子节点继续判断//都不是就递归左子树,再去递归右子树if(this.left != null && this.left.no == no){this.left = null;return;}if(this.right != null && this.right.no == no){this.right = null;return;}//向左子树递归if(this.left != null){this.left.deleteHeroNode(no);}//向右子树递归删除if(this.right != null){this.right.deleteHeroNode(no);}}
}
//定义一个ThreadedBinnaryTree二叉树
class ThreadedBinnaryTree{private HeroNode1 root;//为了实现线索化,需要增加指向当前节点的前驱结点的指针遍历//pre   递归线索化时  一直保留前一个节点private HeroNode1 pre = null;public void setRoot(HeroNode1 root) {this.root = root;}public void threadedNodes(){this.threadedNodes(root);}/*** /编写对二叉树进行中序线索化的方法* @param node    node就是需要线索化的节点*/public void threadedNodes(HeroNode1 node){//如果当前节点为空,则不能线索化if(node == null){return;}else{//先线索化左子树(因为是中序遍历嘛)threadedNodes(node.getLeft());//线索化当前节点{难度}//先处理当前节点的前驱节点if(node.getLeft() == null){//让当前节点的左指针指向前驱节点node.setLeft(pre);//修改当前节点的左指针的类型node.setLeftType(1);//当前节点的左指针指向的是前驱结点}//处理后继节点if(pre != null && pre.getRight() == null){//让前驱节点的右指针指向当前节点pre.setRight(node);pre.setRightType(1);//修改类型}//每处理一个节点后,让当前的节点是下一个节点的前驱节点pre = node; //线索化右子树threadedNodes(node.getRight());}}//前序遍历public void preOrder(){if(this.root != null){this.root.preOrder();}else{System.out.println("二叉树为空,无法遍历");}}//中序遍历public void infixOrder(){if(this.root != null){this.root.infixOrder();}else{System.out.println("二叉树为空,无法遍历");}}//后序遍历public void postOrder(){if(this.root != null){this.root.postOrder();}else{System.out.println("二叉树为空,无法遍历");}}//前序遍历查找 public HeroNode1 preOrderSearch(int no){if(root != null){return root.preOrderSearch(no);}else{return null;}}//中序遍历查找 public HeroNode1 infixOrderSearch(int no){if(root != null){return root.infixOrderSearch(no);}else{return null;}}//后续序遍历查找 public HeroNode1 postOrderSearch(int no){if(root != null){return root.postOrderSearch(no);}else{return null;}}/*** 删除节点操作* @param no   需要删除的节点*/public void deleteHeroNode(int no){if(root == null){System.out.println("该二叉树为空树,不能删除");}else{//如果root只有一个节点,这里立即判断root是不是要删除的节点//否则往下一找就没机会回来了if(root.getNo() == no){root = null;}else{//递归删除root.deleteHeroNode(no);}}}
}

线索化二叉树的遍历

当线索化二叉树后就不能在使用原来的遍历方式了,因为线索化二叉树后,我们把节点都利用起来,连接起来了,导致我们在遍历这种方式,他一直有左右节点,一直会调用自己,导致我们的栈溢出,这时需要新的方式遍历线索化二叉树,各个节点可以通过线型方式遍历,无需再用递归的方式,遍历的次序应当和中序遍历保持一致

代码

//遍历线索化二叉树的方法public void threadedList(){//定义一个变量,临时存储当前遍历的节点,从root开始HeroNode1 node = root;while(node != null){//循环的找到leftType = 1的节点,就是找到我们的端点//第一个找到的就是8节点,后面随着遍历而变化//因为当leftType = 1时,说明该节点是按照线索化处理后的有效节点while(node.getLeftType() == 0){node = node.getLeft();}//打印当前节点System.out.println(node);//如果当前节点的右指针,指向的是后继节点,就一直输出while(node.getRightType() == 1){//获取当前节点的后继节点node = node.getRight();System.out.println(node);}//替换这个遍历的节点node = node.getRight();}}

所有的线索化二叉树代码

package 树;import org.w3c.dom.html.HTMLHRElement;//线索化二叉树
//2021年1月27日15:48:29
//作者  王
public class ThreadedBinnaryTreeDemo {public static void main(String[] args) {//测试中序先锁二叉树HeroNode1 root = new HeroNode1(1, "tom");HeroNode1 node2 = new HeroNode1(3, "jack");HeroNode1 node3 = new HeroNode1(6, "smith");HeroNode1 node4 = new HeroNode1(8, "wang");HeroNode1 node5 = new HeroNode1(10, "huang");HeroNode1 node6 = new HeroNode1(14, "taotao");root.setLeft(node2);root.setRight(node3);node2.setLeft(node4);node2.setRight(node5);node3.setLeft(node6);ThreadedBinnaryTree binnaryTree = new ThreadedBinnaryTree();binnaryTree.setRoot(root);binnaryTree.threadedNodes();//测试10号节点HeroNode1 leftNode = node5.getLeft();System.out.println("10号节点的前驱节点是"+leftNode);System.out.println("10号节点的后继节点是"+node5.getRight());System.out.println("测试我们线性遍历线索化二叉树");binnaryTree.threadedList();}
}//先创建HeroNode节点
class HeroNode1{private int no;private String name;private HeroNode1 left;private HeroNode1 right;//用来标识这个指针指向的是子树还是前驱结点//如果leftType == 0 表示指向的是左子树,如果等于1则表示指向前驱结点//如果rightType == 0 表示指向的是右子树,如果等于1则表示指向后继结点private int leftType;private int rightType;public HeroNode1(int no, String name) {super();this.no = no;this.name = name;}public int getNo() {return no;}public void setNo(int no) {this.no = no;}public String getName() {return name;}public void setName(String name) {this.name = name;}public HeroNode1 getLeft() {return left;}public void setLeft(HeroNode1 left) {this.left = left;}public HeroNode1 getRight() {return right;}public void setRight(HeroNode1 right) {this.right = right;}public int getLeftType() {return leftType;}public void setLeftType(int leftType) {this.leftType = leftType;}public int getRightType() {return rightType;}public void setRightType(int rightType) {this.rightType = rightType;}@Overridepublic String toString() {return "HeroNode1 [no=" + no + ", name=" + name + "]";}//编写前序遍历方法public void preOrder(){System.out.println(this);//先输出父节点//递归向左子树前序遍历if(this.left != null){this.left.preOrder();}//递归向右子树前序遍历if(this.right != null){this.right.preOrder();}}//中序遍历public void infixOrder(){//递归向左子树中序遍历if(this.left != null){this.left.infixOrder();}System.out.println(this);//输出父节点//向右递归中序遍历子树if(this.right != null){this.right.infixOrder();}}//后序遍历public void postOrder(){if(this.left != null){this.left.postOrder();}if(this.right != null){this.right.postOrder();}System.out.println(this);//输出父节点}/*** 前序遍历查找* @param no  待查找编号* @return*/public HeroNode1 preOrderSearch(int no){//判断当前节点是不是if(this.no == no){return this;}//判断左子节点是否为空HeroNode1 resNode = null;if(this.left != null){resNode = this.left.preOrderSearch(no);}if(resNode != null){//说明左子树上找到了return resNode;}if(this.right != null){resNode = this.right.preOrderSearch(no);}return resNode;}//中序遍历查找public HeroNode1 infixOrderSearch(int no){HeroNode1 resNode = null;//先判断当前节点的左子节点if(this.left != null){resNode = this.left.infixOrderSearch(no);}if(resNode != null){return resNode;}if(this.no == no){return this;}if(this.right != null){resNode = this.right.infixOrderSearch(no);}return resNode;}//后序遍历查找public HeroNode1 postOrderSearch(int no){HeroNode1 resNode = null;if(this.left != null){resNode = this.left.postOrderSearch(no);}if(resNode != null){return resNode;}if(this.right != null){resNode = this.right.postOrderSearch(no);}if(resNode != null){return resNode;}if(this.no == no)return this;return resNode;}//递归删除节点//1.删除叶子结点的话,我们直接删除就OK了//2.如果删除的节点是非叶子节点,则要删除这个子树public void deleteHeroNode(int no){//思路如果当前节点的左子节点不为空,//并且左子节点就是需要删除的节点,也就是说要删除油哥,//我们要找到小黄,判断小黄的左子节点是不是需要删除的节点,//是的话就将this.left = null;没有找到就去当前节点的右子节点继续判断//都不是就递归左子树,再去递归右子树if(this.left != null && this.left.no == no){this.left = null;return;}if(this.right != null && this.right.no == no){this.right = null;return;}//向左子树递归if(this.left != null){this.left.deleteHeroNode(no);}//向右子树递归删除if(this.right != null){this.right.deleteHeroNode(no);}}
}
//定义一个ThreadedBinnaryTree二叉树
class ThreadedBinnaryTree{private HeroNode1 root;//为了实现线索化,需要增加指向当前节点的前驱结点的指针遍历//pre   递归线索化时  一直保留前一个节点private HeroNode1 pre = null;public void setRoot(HeroNode1 root) {this.root = root;}public void threadedNodes(){this.threadedNodes(root);}//遍历线索化二叉树的方法public void threadedList(){//定义一个变量,临时存储当前遍历的节点,从root开始HeroNode1 node = root;while(node != null){//循环的找到leftType = 1的节点,就是找到我们的端点//第一个找到的就是8节点,后面随着遍历而变化//因为当leftType = 1时,说明该节点是按照线索化处理后的有效节点while(node.getLeftType() == 0){node = node.getLeft();}//打印当前节点System.out.println(node);//如果当前节点的右指针,指向的是后继节点,就一直输出while(node.getRightType() == 1){//获取当前节点的后继节点node = node.getRight();System.out.println(node);}//替换这个遍历的节点node = node.getRight();//如果我们不替换的话,就会一值输出,卡死}}/*** /编写对二叉树进行中序线索化的方法* @param node    node就是需要线索化的节点*/public void threadedNodes(HeroNode1 node){//如果当前节点为空,则不能线索化if(node == null){return;}else{//先线索化左子树(因为是中序遍历嘛)threadedNodes(node.getLeft());//线索化当前节点{难度}//先处理当前节点的前驱节点if(node.getLeft() == null){//让当前节点的左指针指向前驱节点node.setLeft(pre);//修改当前节点的左指针的类型node.setLeftType(1);//当前节点的左指针指向的是前驱结点}//处理后继节点if(pre != null && pre.getRight() == null){//让前驱节点的右指针指向当前节点pre.setRight(node);pre.setRightType(1);//修改类型}//每处理一个节点后,让当前的节点是下一个节点的前驱节点pre = node; //线索化右子树threadedNodes(node.getRight());}}//前序遍历public void preOrder(){if(this.root != null){this.root.preOrder();}else{System.out.println("二叉树为空,无法遍历");}}//中序遍历public void infixOrder(){if(this.root != null){this.root.infixOrder();}else{System.out.println("二叉树为空,无法遍历");}}//后序遍历public void postOrder(){if(this.root != null){this.root.postOrder();}else{System.out.println("二叉树为空,无法遍历");}}//前序遍历查找 public HeroNode1 preOrderSearch(int no){if(root != null){return root.preOrderSearch(no);}else{return null;}}//中序遍历查找 public HeroNode1 infixOrderSearch(int no){if(root != null){return root.infixOrderSearch(no);}else{return null;}}//后续序遍历查找 public HeroNode1 postOrderSearch(int no){if(root != null){return root.postOrderSearch(no);}else{return null;}}/*** 删除节点操作* @param no   需要删除的节点*/public void deleteHeroNode(int no){if(root == null){System.out.println("该二叉树为空树,不能删除");}else{//如果root只有一个节点,这里立即判断root是不是要删除的节点//否则往下一找就没机会回来了if(root.getNo() == no){root = null;}else{//递归删除root.deleteHeroNode(no);}}}}

线索化二叉树的创建与遍历相关推荐

  1. 线索化二叉树的建立与遍历

    线索化二叉树 基本介绍 1)具有n个节点的二叉链表中有(2n - (n - 1))= n + 1 个空指针域,利用空指针域存放指向该节点某种遍历次序下的前驱结点和后继节点的指针,这种附加的指针称为线索 ...

  2. 数据结构 - 线索化二叉树(线索化与遍历)

    !!(这里我debug很久才理解过来)** 这里8的前驱为null,所以8的leftType=1,但是6是没有后继的或者说后继为null但是rightType为0(因为后继是在下一个节点来进行连接的, ...

  3. 线索化二叉树,中序建立线索,带线索中序遍历,删除,c/c++描述

      二叉树的叶节点的两个指针都没有利用,一些分支节点也可能有一个指针指向NULL,这些指针造成了程序在内存空间上的浪费,但这些叶节点也必不可省.所以我们可以把这些指向NULL的指针,重新赋值,左指针则 ...

  4. 线索化二叉树的学习记录

    文章目录 前言 一.线索化二叉树是什么? 二.应用案例 前言 先看一个问题: 将数列 {1, 3, 6, 8, 10, 14 } 构建成一颗二叉树. n+1=7 问题分析: 当我们对上面的二叉树进行中 ...

  5. C语言实现线索化二叉树(先序、中序、后序)

    >>如何用C语言构建一颗二叉树? 第一种方法: ThreadTree A = (ThreadTree)malloc(sizeof(ThreadNode));A->data = { ' ...

  6. Java实现前中后序线索化二叉树以及遍历

    文章目录 一.线索化二叉树的原理 二.构建线索化二叉树 三.代码实现线索二叉树 一.线索化二叉树的原理 在前面介绍二叉树的文章中提到,二叉树可以使用两种存储结构:顺序存储和链式存储,在使用链式存储时, ...

  7. 遍历线索化二叉树+图解

    图解 代码实现 package com.atguigu.tree.threadedbinarytree;/*** @创建人 wdl* @创建时间 2021/3/25* @描述*/ public cla ...

  8. 线索化二叉树及其遍历

    线索化二叉树及其遍历 线索二叉树基本介绍 1.利用二叉表中空指针域,存放指向该结点在某种遍历次序下的前驱与后续节点的指针称为线索 2.这种加上了线索的二叉链表称为线索链表,相应的二叉树也称为线索二叉树 ...

  9. 数据结构与算法(java):树-二叉树(二叉查找树(BST)、线索化二叉树、哈夫曼树、平衡二叉树【AVL】、二叉树的前中后序遍历)

    二叉树 1.定义 二叉树 就是度不超过2的树(每个结点最多只有两个子结点).如图 2.特殊二叉树 满二叉树 当二叉树的每一个层的结点树都达到最大值,则这个二叉树就是满二叉树. 完全二叉树 叶结点只能出 ...

  10. 【算法系列之线索化二叉树,前序线索化、中序线索化、后序线索化以及遍历~】

    1.何谓线索化二叉树 2.线索化二叉树的本质 3.线索化二叉树的存储结构 4.构建线索化二叉树 4.1.先序线索化 4.2.中序线索化 4.3.后序线索化 5.遍历线索化二叉树 5.1.先序遍历 先序 ...

最新文章

  1. Java实现的文件Copy例子
  2. 深入理解JS中this关键字
  3. [转] GDBT详解
  4. python format格式化函数用法
  5. 启明云端分享|SSW101B WIFI调试会遇到的问题答疑
  6. mysql查询一个数据库所有表的记录数,mysql 查看数据库中所有表的记录数
  7. python分享文件_使用简单的python http服务共享文件
  8. 解决离线安装依赖包的方法
  9. 国内git clone报错问题解决办法
  10. 人脸方向学习(二十):Face Landmark Detection-TS3-解读
  11. 哈工大2020计算机组织与体系结构期末
  12. 低通滤波器的设计与DSP实现
  13. 重症监护室(ICU)100260
  14. uniapp开发App调用微信授权登陆
  15. 硬盘格式化工具 标记坏扇区_硬盘格式化后是否还记得坏扇区?
  16. 计算机大神专业小说,5本高人气系统流小说,无敌满足你,一路爽到底,全部是大神出品...
  17. 常州工学院Java作业03
  18. .htaccess rewrite 规则详细说明
  19. IDEA报错:Failed to obtain JDBC ConnectionCannot create PoolableConnectionFactory
  20. 燕文物流完成上市辅导:董事长周文兴持股30%,曾因丢失邮件被批

热门文章

  1. 后端的日期类型赋值前端表单_后端获取前端数据的四种方式
  2. unityar自动识别人脸_AR开发实战项目之人脸识别(实现换脸、人脸涂妆、动作特效)...
  3. 80字符带空格 段错误_简单错误记录
  4. 怎么做301永久重定向
  5. 斯坦福大学深度学习公开课cs231n学习笔记(6)神经网路输入数据预处理(归一化,PCA等)及参数初始化
  6. LeetCode之寻找峰值
  7. 深度学习中的数据增强
  8. SpringBoot学习(第一天)
  9. KendoUI 基础:Grid 绑定template展示
  10. 【英语天天读】Albert Einstein’s Advices