C#数据结构-线索化二叉树
为什么线索化二叉树?
对于二叉树的遍历,我们知道每个节点的前驱与后继,但是这是建立在遍历的基础上,否则我们只知道后续的左右子树。现在我们充分利用二叉树左右子树的空节点,分别指向当前节点的前驱、后继,便于快速查找树的前驱后继。
不多说,直接上代码:
/// <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#数据结构-线索化二叉树的更多相关文章
- 线索化二叉树的构建与先序,中序遍历(C++版)
贴出学习C++数据结构线索化二叉树的过程, 方便和我一样的新手进行测试和学习 同时欢迎各位大神纠正. 不同与普通二叉树的地方会用背景色填充 //BinTreeNode_Thr.h enum Point ...
- 数据结构与算法---线索化二叉树(Threaded BinaryTree)
先看一个问题 将数列 {1, 3, 6, 8, 10, 14 } 构建成一颗二叉树 问题分析: 当我们对上面的二叉树进行中序遍历时,数列为 {8, 3, 10, 1, 6, 14 } 但是 6, 8 ...
- 后序线索化二叉树(Java版)
前面介绍了前序线索化二叉树.中序线索化二叉树,本文将介绍后序线索化二叉树.之所以用单独的一篇文章来分析后序线索化二叉树,是因为后序线索化二叉树比前序.中序要复杂一些:另外在复习线索化二叉树的过程中,大 ...
- JAVA递归实现线索化二叉树
JAVA递归实现线索化二叉树 基础理论 首先,二叉树递归遍历分为先序遍历.中序遍历和后序遍历. 先序遍历为:根节点+左子树+右子树 中序遍历为:左子树+根节点+右子树 后序遍历为:左子树+右子树+根节 ...
- YTU 3026: 中序线索化二叉树
原文链接:https://www.dreamwings.cn/ytu3026/2896.html 3026: 中序线索化二叉树 时间限制: 1 Sec 内存限制: 128 MB 提交: 9 解决: ...
- 图解前序遍历线索化二叉树,前序线索二叉树遍历,C\C++描述
body, table{font-family: 微软雅黑; font-size: 13.5pt} table{border-collapse: collapse; border: solid gra ...
- 图解中序遍历线索化二叉树,中序线索二叉树遍历,C\C++描述
body, table{font-family: 微软雅黑; font-size: 13.5pt} table{border-collapse: collapse; border: solid gra ...
- 数据结构丨N叉树
遍历 N叉树的遍历 树的遍历 一棵二叉树可以按照前序.中序.后序或者层序来进行遍历.在这些遍历方法中,前序遍历.后序遍历和层序遍历同样可以运用到N叉树中. 回顾 - 二叉树的遍历 前序遍历 - 首先访 ...
- js:数据结构笔记9--二叉树
树:以分层的方式存储数据:节点:根节点,子节点,父节点,叶子节点(没有任何子节点的节点):层:根节点开始0层: 二叉树:每个节点子节点不超过两个:查找快(比链表),添加,删除快(比数组): BST:二 ...
随机推荐
- 网页中审查元素(按F12)与查看网页源代码的区别
问题 在验证目标系统是含有XSS漏洞,查看源代码,看不到插入的跨站脚本代码. 原理 所谓查看源代码,就是别人服务器发送到浏览器的原封不动的代码. 审查元素时,你看到那些,在源代码中找不到的代码,是在浏 ...
- 会声会影使用教程:剪辑Vlogo短视频
随着抖音.快手等视频分享软件的兴起,很多人已经开始尝试制作短视频分享.那么,对于视频制作新手来说,短视频的制作难度大吗?其实,只要选对了视频制作软件,视频制作将会变得相当简单. 在众多视频制作软件中, ...
- 让mac电脑更简单运行Windows软件的CrossOver,优势知多少?
如今,一些iPhone和iPad机型拥有Face ID功能,此功能作用允许用户通过面部识别来解锁设备.该功能还不能在Mac上使用,但是国外媒体于7月27日报道称,在公测第三版的macOS Big Su ...
- guitar pro系列教程(十五):Guitar Pro播放没有声音怎么办?
相信很多朋友在安装了guitar pro打谱后都会发现没有声音,关于这个问题相信大家都很苦恼到底是什么问题呢,今天小编就这个问题与大家好好讨论讨论,希望对大家有帮助,感兴趣的朋友可以进来一起学习哦. ...
- robot 如何定义用户关键字、变量
1.用户关键字,使用robot语法定义的关键字 2.系统关键字 自带的 3.资源文件,自己定义的关键字 4.变量 自己定义的关键字,需单独建一个资源文件 自己写的关键字,需写在***Keywords ...
- Hadoop优化之数据压缩
bBHadoop数据压缩 概述 运行hadoop程序时,I/O操作.网络数据传输.shuffle和merge要花大量的时间,尤其是数据规模很大和工作负载密集的情况下,这个时候,使用数据压缩可以提高效率 ...
- Java集合【1】--俯瞰java集合分类
目录 (一) java集合分类 (1) Iterable接口 1. 内部定义的方法 1.1 iterator方法 1.2 forEach方法 1.3 spliterator方法 2. Collecti ...
- MySQL replace into那些隐藏的风险
目录 replace into时存在主键冲突 replace into时存在唯一索引冲突 replace into时存在主键冲突&唯一索引冲突 存在问题 结论 MySQL中 replace i ...
- CentOS下构建Shell简易分发系统
bash经典收集 经典收集1 for f in `(cd .; find suite -type f | grep -v SCCS)`; \ do \ d=/usr/local/mysql/mysql ...
- MIT-6.004计算结构(2019年春)
L01: Introduction 略 L02: RISC-V Assembly 1.计算机处理器主要有三部分组成:内存.寄存器.算数逻辑单元 算数逻辑单元与寄存器通信,寄存器与内存通信,而算术逻辑单 ...