C#数据结构-线索化二叉树

线索化二叉树是一种优化二叉树遍历的方法,通过利用空指针存储前驱和后继节点信息,将二叉树转化为双向链表,从而在不增加额外空间复杂度的情况下,实现快速查找节点的前驱和后继。本文介绍了线索化二叉树的概念、目的以及其实现代码,包括插入、删除节点和中序遍历等功能。在测试案例中,展示了如何创建并线索化一个二叉树,以及中序遍历的结果。
摘要由CSDN通过智能技术生成

为什么线索化二叉树?

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

不多说,直接上代码:


/// <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

在这里插入图片描述

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值