为什么线索化二叉树?

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

不多说,直接上代码:

/// <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. 【硬件】HDMI接口HPD原理

    目录 一.什么是HPD? 二.HDMI的HPD(热插拔)原理 三.HDMI源端对HPD信号有什么要求? 由于项目需要通过HDMI获取EDID的数据,需要学习一下其获取的工作原理,所以在这里记录下. 一 ...

  2. zabbix的搭建及操作(2)监控windows10及网络设备

    实验环境 配置完成的Server端 Web --详情点击 windows10 192.168.10.1 (与虚拟机同一Vmnet的IP地址)必须关闭防火墙 网络设备GNS 192.168.10.50 ...

  3. 文档丢失不用怕,EasyRecovery帮你一键恢复

    我们在使用电脑的过程中,有时会因为各种原因,导致我们所写的文档丢失了.遇到这种情况,该怎么办呢? 下面,就给大家分享一下用EasyRecovery如何恢复被丢失的文档. 1.双击进入EasyRecov ...

  4. python基础之条件语句

    检查相等和不等 多个检查条件 age1 = 22 age2 = 19 s1 = age1 > 21 and age2 > 19 print(s1) s2 = age1 > 21 or ...

  5. 洛谷 P1284 三角形牧场 题解(背包+海伦公式)

    题目链接 题目大意 给你 n块木板(n<=40),每块木板长度为\(l[i]<=40\) 每块木板都要用,求最大的三角形面积×100,答案直接舍去小数 题目思路 首先如果已知三条边的长度可 ...

  6. C语言讲义——错误处理

    errno C语言不提供对错误处理的直接支持. 以返回值的形式表示是否出错. 在发生错误时,大多数的C函数调用返回1或NULL. 同时设置一个错误代码errno(全局变量),表示在函数调用期间发生了错 ...

  7. kafka入门之broker-集群管理

    依赖于zookeeper,broker向zk中注册的信息以json格式保存,其中包括: 1.listener_security_protocol_map:此值指定了该broker与外界通信所用的安全协 ...

  8. redis面试问题(一)

    五大常用数据类型 redis与其他缓存的比较 rdb和aof 主从复制,读写分离,哨兵机制 -------------------------------- 1.为什么使用redis (一)性能 我们 ...

  9. 华为交换机eNSP删除Vlan的详细步骤

    设备支持批量删除VLAN和单个删除VLAN两种方式: 单个删除VLAN10 <HUAWEI> system-view [HUAWEI] undo vlan 10 批量删除VLAN10到VL ...

  10. linux系统下oracle表空间占用情况

    1.我们先查询表空间的占用情况,使用sql如下: select upper(f.tablespace_name) "表空间名", d.tot_grootte_mb "表空 ...