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:二 ...
随机推荐
- BurpSuite 抓安卓app包
认准夜神安卓模拟器,下了几个其他的模拟器都没有代理的功能 一.夜神安卓模拟器设置代理 1.点击右下角的设置 2.点击设置,进入到WIFI连接选项 3.进入列表后,鼠标点击wifi位置,长按左键,会出现 ...
- java开发两年!这些异常处理的方式你得知道,不然你凭什么涨薪!
前言 异常是在程序中导致程序中断运行的一种指令流,当异常发生时,程序将直接中断,不再执行后续的任何操作! 示例:两数相除,若不处理任何异常,则只有在正确输入两个数字时,才能显示出运算结果. publi ...
- 用思维导图软件MindManager整理假期
今天带大家使用MindManager2020软件构建出2020年的节假日思维导图. 既然是做2020年的节假日思维导图,那么有个MindManager技巧就是,关于这一类思维导图我们都可以选择时间线导 ...
- 带你入门Camtasia Studio录像机软件
Camtasia软件和其他录制软件不同,不论是编辑功能还是制作功能还是其他功能方面都远远高于其他录制软件.那这边我们可以一起了解一下基础软件功能. 首先,我们在电脑端安装了软件以后,进行实际操作.在操 ...
- 2018年第九届蓝桥杯【C++省赛B组】B、C、D、F、G 题解
B. 明码 #STL 题意 把每个字节转为2进制表示,1表示墨迹,0表示底色.每行2个字节,一共16行,布局是: 第1字节,第2字节 第3字节,第4字节 .... 第31字节, 第32字节 给定一段由 ...
- C语言讲义——结构体struct
结构体是一种变量类型,可以包含多个变量(变量类型不必相同). 结构体的关键字是struct也是一种值类型. 例:设计一个表示"书本"的结构体: structBook { chari ...
- leetcode_3FizzBuzz的一些思考
题目很简单,给定一个正整数n,如果n能整除3的话往list里加入Fizz,如果n能整除5的话往list里面加入Buzz,如果即能整除3又能整除5的话,加入FizzBuzz,代码也很简单 public ...
- Steps 组件的设计与实现
NutUI 组件源码揭秘 前言 本文的主题是 Steps 组件的设计与实现.Steps 组件是 Steps 步骤和 Timeline 组件结合的组件,在此之前他们是两个不同的组件,在 NutUI 最近 ...
- HTML-webstorm添加快捷键
快速输入标签: 先输入标签p,按Tab键变成<p></p>,光标会在标签中间 输入内容后按end键 快速复制粘贴光标所在的一整行内容Ctrl+D 快速删除光标所在的行 Ctrl ...
- linux查看内存及磁盘使用情况
1.查看当前目录 命令: df -h (统一每个目录下磁盘的整体情况) 2.查看指定目录 在命令后直接放目录名,比如查看"usr"目录使用情况: 命令: df ...