为什么线索化二叉树?

对于二叉树的遍历,我们知道每个节点的前驱与后继,但是这是建立在遍历的基础上,否则我们只知道后续的左右子树。现在我们充分利用二叉树左右子树的空节点,分别指向当前节点的前驱、后继,便于快速查找树的前驱后继。

不多说,直接上代码:

/// <summary>
/// 线索二叉树 节点
/// </summary>
/// <typeparam name="T"></typeparam>
public class ClueTreeNode<T>
{
/// <summary>
/// 内容
/// </summary>
public T data { get; set; }
/// <summary>
/// 左树
/// </summary>
public ClueTreeNode<T> leftNode { get; set; }
/// <summary>
/// 右树
/// </summary>
public ClueTreeNode<T> rightNode { get; set; }
/// <summary>
/// 0 标识左树 1 标识 当前节点的前驱
/// </summary>
public int leftTag { get; set; }
/// <summary>
/// 0标识右树 1 标识 当前节点的后继
/// </summary>
public int rightTag { get; set; } public ClueTreeNode()
{
data = default(T);
leftNode = null;
rightNode = null;
} public ClueTreeNode(T item)
{
data = item;
leftNode = null;
rightNode = null;
}
}
/// <summary>
/// 线索化 二叉树
///
/// 为什么线索化二叉树?
/// 第一:对于二叉树,如果有n个节点,每个节点有指向左右孩子的两个指针域,所以一共有2n个指针域。
/// 而n个节点的二叉树一共有n-1条分支线数,也就是说,其实是有 2n-(n-1) = n+1个空指针。
/// 这些空间不存储任何事物,白白浪费内存的资源。
/// 第二:对于二叉树的遍历,我们知道每个节点的前驱与后继,但是这是建立在遍历的基础上。
/// 否则我们只知道后续的左右子树。
/// 第三:对于二叉树来说,从结构上来说是单向链表,引入前驱后继后,线索化二叉树可以认为是双向链表。
/// </summary>
/// <typeparam name="T"></typeparam>
public class ClueBinaryTree<T>
{
/// <summary>
/// 树根节
/// </summary>
private ClueTreeNode<T> head { get; set; }
/// <summary>
/// 线索化时作为前驱转存
/// </summary>
private ClueTreeNode<T> preNode { get; set; } public ClueBinaryTree(){
head = new ClueTreeNode<T>();
}
public ClueBinaryTree(T val){
head = new ClueTreeNode<T>(val);
} public ClueTreeNode<T> GetRoot(){
return head;
} /// <summary>
/// 插入左节点
/// </summary>
/// <param name="val"></param>
/// <param name="node"></param>
/// <returns></returns>
public ClueTreeNode<T> AddLeftNode(T val, ClueTreeNode<T> node){
if (node == null)
throw new ArgumentNullException("参数错误");
ClueTreeNode<T> treeNode = new ClueTreeNode<T>(val);
ClueTreeNode<T> childNode = node.leftNode;
treeNode.leftNode = childNode;
node.leftNode = treeNode;
return treeNode;
} /// <summary>
/// 插入右节点
/// </summary>
/// <param name="val"></param>
/// <param name="node"></param>
/// <returns></returns>
public ClueTreeNode<T> AddRightNode(T val, ClueTreeNode<T> node){
if (node == null)
throw new ArgumentNullException("参数错误");
ClueTreeNode<T> treeNode = new ClueTreeNode<T>(val);
ClueTreeNode<T> childNode = node.rightNode;
treeNode.rightNode = childNode;
node.rightNode = treeNode;
return treeNode;
}
/// <summary>
/// 删除当前节点的 左节点
/// </summary>
/// <param name="node"></param>
/// <returns></returns>
public ClueTreeNode<T> DeleteLeftNode(ClueTreeNode<T> node){
if (node == null || node.leftNode == null)
throw new ArgumentNullException("参数错误");
ClueTreeNode<T> leftChild = node.leftNode;
node.leftNode = null;
return leftChild;
} /// <summary>
/// 删除当前节点的 右节点
/// </summary>
/// <param name="node"></param>
/// <returns></returns>
public ClueTreeNode<T> DeleteRightNode(ClueTreeNode<T> node){
if (node == null || node.rightNode == null)
throw new ArgumentNullException("参数错误");
ClueTreeNode<T> rightChild = node.rightNode;
node.rightNode = null;
return rightChild;
} /// <summary>
/// 中序遍历线索化二叉树
/// </summary>
public void MiddlePrefaceTraversal(){
ClueTreeNode<T> node = head;
while (node != null)
{
//判断是否是
while (node.leftTag == 0)
{
node = node.leftNode;
}
Console.Write($" {node.data}");
while (node.rightTag == 1)
{
node = node.rightNode;
Console.Write($" {node.data}");
}
node = node.rightNode;
}
}
/// <summary>
/// 线索化二叉树
/// </summary>
/// <param name="node"></param>
public void MiddleClueNodes(ClueTreeNode<T> node){
if (node == null)
{
return;
}
//线索化左子树
MiddleClueNodes(node.leftNode);
//当左树为空时,指向前驱,标识为 1
if (node.leftNode == null)
{
node.leftNode = preNode;
node.leftTag = 1;
}
//如果 前驱的右树不为空
if (preNode != null && preNode.rightNode == null)
{
preNode.rightNode = node;
preNode.rightTag = 1;
}
preNode = node;
//线索化右子树
MiddleClueNodes(node.rightNode);
}
}

现在我们测试:

//创建树
ClueBinaryTree<string> clueBinaryTree = new ClueBinaryTree<string>("A");
ClueTreeNode<string> tree1 = clueBinaryTree.AddLeftNode("B", clueBinaryTree.GetRoot());
ClueTreeNode<string> tree2 = clueBinaryTree.AddRightNode("C", clueBinaryTree.GetRoot());
ClueTreeNode<string> tree3 = clueBinaryTree.AddLeftNode("D", tree1);
clueBinaryTree.AddRightNode("E", tree1);
clueBinaryTree.AddLeftNode("F", tree2);
clueBinaryTree.AddRightNode("G", tree2); clueBinaryTree.MiddleClueNodes(clueBinaryTree.GetRoot()); Console.Write("中序遍历");
clueBinaryTree.MiddlePrefaceTraversal();

打印结果:

中序遍历 D B E A F C G

C#数据结构-线索化二叉树的更多相关文章

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  8. 数据结构丨N叉树

    遍历 N叉树的遍历 树的遍历 一棵二叉树可以按照前序.中序.后序或者层序来进行遍历.在这些遍历方法中,前序遍历.后序遍历和层序遍历同样可以运用到N叉树中. 回顾 - 二叉树的遍历 前序遍历 - 首先访 ...

  9. js:数据结构笔记9--二叉树

    树:以分层的方式存储数据:节点:根节点,子节点,父节点,叶子节点(没有任何子节点的节点):层:根节点开始0层: 二叉树:每个节点子节点不超过两个:查找快(比链表),添加,删除快(比数组): BST:二 ...

随机推荐

  1. 精益求精!Spring Boot 知识点全面回顾,带你重新细读源码!

    约定优于配置 Build Anything with Spring Boot:Spring Boot is the starting point for building all Spring-bas ...

  2. 面试官:小伙子,你给我说一下Java Exception 和 Error 的区别吧?

    前言 昨天在整理粉丝给我私信的时候,发现了一个挺有意思的事情.是这样的,有一个粉丝朋友私信问我Java 的 Exception 和 Error 有什么区别呢?说他在面试的时候被问到这个问题卡壳了,最后 ...

  3. 【MathType教学】如何让括号内的内容居中

    作为一款非常好用的公式编辑器,MathType它的功能十分强大,不仅包含了大量的数学符号,并且能和Office软件很好地兼容.但是有的时候打出来的公式可能不是自己想要的效果,这时我们就要用一些特别的办 ...

  4. 追星女孩必备!使用Camtasia制作爱豆视频

    制作爱豆视频,我用得比较多的是Camtasia(Windows)教程录制.因为这款软件操作简单,功能强大,用起来相当顺手呢.而且更重要的是,Camtasia有录屏功能,电脑存量不足的情况下,真的很好用 ...

  5. AWS CodePipeline部署Maven项目至EC2

    背景 AWS CodePipeline 是一种持续性的集成与交付服务,可以实现快速而可靠的应用程序和基础设施更新.根据您定义的发布流程模型,只要代码发生变更,CodePipeline 便会生成.测试和 ...

  6. DC靶机1-9合集

    DC1 文章前提概述 本文介绍DC-1靶机的渗透测试流程 涉及知识点(比较基础): nmap扫描网段端口服务 msf的漏洞搜索 drupal7的命令执行利用 netcat反向shell mysql的基 ...

  7. zk特性

    看了又忘系列: 1.zk会将全量的数据存储在内存中,以此来实现提高服务器吞吐,减少延迟的目的. 2.集群中每台机器都会在内存中维护当前的服务器状态,并且每台机器之间都相互保持着通信.只要集群中存在超过 ...

  8. H3CNE(教程)

    培训机构提供的ppt,可能也是来自于官方提供,涉及到H3CNE认证考试中的全部知识点,学真技术还得看这个.包括帧中继,哪怕是淘汰了十多年了. https://huxiaoyao.lanzous.com ...

  9. JZOJ2020年8月11日提高组T1 密码

    JZOJ2020年8月11日提高组T1 密码 题目 Description 在浩浩茫茫的苍穹深处,住着上帝和他的神仆们,他们闲谈着下界的凡人俗事,对人世间表现的聪明智慧,大加赞赏.今天他们正在观赏大地 ...

  10. 推荐系统实践 0x0b 矩阵分解

    前言 推荐系统实践那本书基本上就更新到上一篇了,之后的内容会把各个算法拿来当专题进行讲解.在这一篇,我们将会介绍矩阵分解这一方法.一般来说,协同过滤算法(基于用户.基于物品)会有一个比较严重的问题,那 ...