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

先看一个问题

将数列 {1, 3, 6, 8, 10, 14  } 构建成一颗二叉树

问题分析:

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

线索二叉树基本介绍

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

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

3、一个结点的前一个结点,称为前驱结点

4、一个结点的后一个结点,称为后继结点

线索二叉树应用案例

应用案例说明:将下面的二叉树,进行中序线索二叉树。中序遍历的数列为 {8, 3, 10, 1, 14, 6}

思路分析: 中序遍历的结果:{8, 3, 10, 1, 14, 6}

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

  1. left 指向的是左子树,也可能是指向的前驱节点. 比如 ① 节点 left 指向的左子树, 而 ⑩ 节点的 left 指向的就是前驱节点.
  2. right指向的是右子树,也可能是指向后继节点,比如 ① 节点right 指向的是右子树,而⑩ 节点的right 指向的是后继节点.

代码实现:

 1 class BinaryTree {
 2 private HeroNode root;
 3 privateHeroNode pre=null;
 4 public void threadNodes() {
 5 threadNodes(root);
 6 }
 7 public void threadNodes(HeroNode node) {
 8 if(node==null) {
 9 return;
10 }
11 threadNodes(node.getLeft());
12 if(node.getLeft()==null){
13 node.setLeft(pre);
14 node.setLeftType(1);
15 }
16 if(pre!=null&&pre.getRight()==null) {
17 pre.setRight(node);
18 pre.setRightType(1); }
19 pre=node;
20 threadNodes(node.getRight());}}

代码

 1 public class BinaryTreeDemo {
 2 public static void main(String[] args) {
 3 BinaryTree binaryTree = new BinaryTree();
 4 HeroNode root = new HeroNode(1, "jack");
 5 HeroNode node1 = new HeroNode(3, "tom");
 6 HeroNode node2 = new HeroNode(6, "mike");
 7
 8 root.setLeftNode(node1);
 9 root.setRightNode(node2);
10 binaryTree.setRoot(root);
11
12 HeroNode node3 = new HeroNode(8, "林冲");
13 HeroNode node4 = new HeroNode(10, "关胜");
14 node1.setLeftNode(node3);
15 node1.setRightNode(node4);
16 HeroNode node5 = new HeroNode(14, "jerry");
17 node2.setLeftNode(node5);
18
19 System.out.println("---中序---");
20 binaryTree.infixOrder();
21 //中序线索化二叉树
22 binaryTree.threadNodes();
23
24 HeroNode afterHeroNode10 = node4.getRight();
25 System.out.println(afterHeroNode10);// no=1 name=jack ok了
26 }}

测试

遍历线索化二叉树

说明:对前面的中序线索化的二叉树, 进行遍历

分析:因为线索化后,各个结点指向有变化,因此原来的遍历方式不能使用,这时需要使用新的方式遍历线索化二叉树,各个节点可以通过线型方式遍历,因此无需使用递归方式,这样也提高了遍历的效率。遍历的次序应当和中序遍历保持一致

 1 //遍历线索化二叉树的方法
 2     public void threadedList() {
 3         //定义一个变量,存储当前遍历的结点,从root开始
 4         HeroNode node = root;
 5         while(node != null) {
 6             //循环的找到leftType == 1的结点,第一个找到就是8结点
 7             //后面随着遍历而变化,因为当leftType==1时,说明该结点是按照线索化
 8             //处理后的有效结点
 9             while(node.getLeftType() == 0) {
10                 node = node.getLeft();
11             }
12
13             //打印当前这个结点
14             System.out.println(node);
15             //如果当前结点的右指针指向的是后继结点,就一直输出
16             while(node.getRightType() == 1) {
17                 //获取到当前结点的后继结点
18                 node = node.getRight();
19                 System.out.println(node);
20             }
21             //替换这个遍历的结点
22             node = node.getRight();
23
24         }
25     }

代码

 1 public static void main(String[] args) {
 2         //测试一把中序线索二叉树的功能
 3         HeroNode root = new HeroNode(1, "tom");
 4         HeroNode node2 = new HeroNode(3, "jack");
 5         HeroNode node3 = new HeroNode(6, "smith");
 6         HeroNode node4 = new HeroNode(8, "mary");
 7         HeroNode node5 = new HeroNode(10, "king");
 8         HeroNode node6 = new HeroNode(14, "dim");
 9
10         //二叉树,后面我们要递归创建, 现在简单处理使用手动创建
11         root.setLeft(node2);
12         root.setRight(node3);
13         node2.setLeft(node4);
14         node2.setRight(node5);
15         node3.setLeft(node6);
16
17         //测试中序线索化
18         ThreadedBinaryTree threadedBinaryTree = new ThreadedBinaryTree();
19         threadedBinaryTree.setRoot(root);
20         threadedBinaryTree.threadedNodes();
21
22         //测试: 以10号节点测试
23         HeroNode leftNode = node5.getLeft();
24         HeroNode rightNode = node5.getRight();
25         System.out.println("10号结点的前驱结点是 ="  + leftNode); //3
26         System.out.println("10号结点的后继结点是="  + rightNode); //1
27
28         //当线索化二叉树后,能在使用原来的遍历方法
29         //threadedBinaryTree.infixOrder();
30         System.out.println("使用线索化的方式遍历 线索化二叉树");
31         threadedBinaryTree.threadedList(); // 8, 3, 10, 1, 14, 6
32
33     }

测试

原文地址:https://www.cnblogs.com/justBobo/p/11192302.html

时间: 2024-08-07 00:17:29

数据结构与算法---线索化二叉树(Threaded BinaryTree)的相关文章

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

本文是数据结构基础系列(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

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

//二叉树的线索化 #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 *

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

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

线索化二叉树的构建与先序,中序遍历(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

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

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

JAVA递归实现线索化二叉树

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

线索化二叉树

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

树五:线索化二叉树

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