利用栈实现线索化二叉树_C#数据结构线索化二叉树

3a726253918ba24553691b635ad7fcc7.png

为什么线索化二叉树?

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

不多说,直接上代码:

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

线索化二叉树的过程(中序遍历):

6bea08f7c752da96e3230d1228bbced3.png

539f756211c3b1d769c220b36b15f405.png

现在我们测试:

//创建树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
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值