先前说了树的基本操作,我们采用的是二叉链表来保存树形结构,当然二叉有二叉的困扰之处,比如我想找到当前结点的“前驱”和“后继”,那么我们就必须要遍历一下树,然后才能定位到该“节点”的“前驱”和“后继”,每次定位都是O(n),这不是我们想看到的,那么有什么办法来解决呢?
从“结构图”中可以看到,现在结点的指针域中要么是”子节点(SubTree)“或者是”线索(Thread)“,此时就要设立标志位来表示指针域存放的是哪一种。
#region 中序遍历构建线索二叉树
/// summary>
/// 中序遍历构建线索二叉树
/// /summary>
/// typeparam name="T">/typeparam>
/// param name="tree">/param>
public void BinTreeThreadingCreate_LDRT>(ref ThreadTreeT> tree, ref ThreadTreeT> prevNode)
{
if (tree == null)
return;
//先左子树遍历,寻找起始点
BinTreeThreadingCreate_LDR(ref tree.left, ref prevNode);
//如果left为空,则说明该节点可以放“线索”
tree.leftFlag = (tree.left == null) ? NodeFlag.Thread : NodeFlag.SubTree;
//如果right为空,则说明该节点可以放“线索”
tree.rightFlag = (tree.right == null) ? NodeFlag.Thread : NodeFlag.SubTree;
if (prevNode != null)
{
if (tree.leftFlag == NodeFlag.Thread)
tree.left = prevNode;
if (prevNode.rightFlag == NodeFlag.Thread)
prevNode.right = tree;
}
//保存前驱节点
prevNode = tree;
BinTreeThreadingCreate_LDR(ref tree.right, ref prevNode);
}
#endregion
#region 查找指定节点的后继
/// summary>
/// 查找指定节点的后继
/// /summary>
/// typeparam name="T">/typeparam>
/// param name="tree">/param>
public ThreadTreeT> BinTreeThreadNext_LDRT>(ThreadTreeT> tree)
{
if (tree == null)
return null;
//如果查找节点的标志域中是Thread,则直接获取
if (tree.rightFlag == NodeFlag.Thread)
return tree.right;
else
{
//根据中序遍历的规则是寻找右子树中中序遍历的第一个节点
var rightNode = tree.right;
//如果该节点是subTree就需要循环遍历
while (rightNode.leftFlag == NodeFlag.SubTree)
{
rightNode = rightNode.left;
}
return rightNode;
}
}
#endregion
#region 查找指定节点的前驱
/// summary>
/// 查找指定节点的前驱
/// /summary>
/// typeparam name="T">/typeparam>
/// param name="tree">/param>
/// returns>/returns>
public ThreadTreeT> BinTreeThreadPrev_LDRT>(ThreadTreeT> tree)
{
if (tree == null)
return null;
//如果标志域中存放的是线索,那么可以直接找出来
if (tree.leftFlag == NodeFlag.Thread)
return tree.left;
else
{
//根据”中序“的规则可知,如果不为Thread,则要找出左子树的最后节点
//也就是左子树中最后输出的元素
var leftNode = tree.left;
while (leftNode.rightFlag == NodeFlag.SubTree)
leftNode = leftNode.right;
return leftNode;
}
}
#endregion
#region 遍历线索二叉树
/// summary>
/// 遍历线索二叉树
/// /summary>
/// typeparam name="T">/typeparam>
/// param name="tree">/param>
public void BinTreeThread_LDRT>(ThreadTreeT> tree)
{
if (tree == null)
return;
while (tree.leftFlag == NodeFlag.SubTree)
tree = tree.left;
do
{
Console.Write(tree.data + "\t");
tree = BinTreeThreadNext_LDR(tree);
} while (tree != null);
}
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ThreadChainTree
{
class Program
{
static void Main(string[] args)
{
ThreadTreeManager manager = new ThreadTreeManager();
//生成根节点
ThreadTreestring> tree = CreateRoot();
//生成节点
AddNode(tree);
ThreadTreestring> prevNode = null;
//构建线索二叉树
manager.BinTreeThreadingCreate_LDR(ref tree, ref prevNode);
Console.WriteLine("\n线索二叉树的遍历结果为:\n");
//中序遍历线索二叉树
manager.BinTreeThread_LDR(tree);
}
#region 生成根节点
/// summary>
/// 生成根节点
/// /summary>
/// returns>/returns>
static ThreadTreestring> CreateRoot()
{
ThreadTreestring> tree = new ThreadTreestring>();
Console.WriteLine("请输入根节点,方便我们生成树\n");
tree.data = Console.ReadLine();
Console.WriteLine("根节点生成已经生成\n");
return tree;
}
#endregion
#region 插入节点操作
/// summary>
/// 插入节点操作
/// /summary>
/// param name="tree">/param>
static ThreadTreestring> AddNode(ThreadTreestring> tree)
{
ThreadTreeManager mananger = new ThreadTreeManager();
while (true)
{
ThreadTreestring> node = new ThreadTreestring>();
Console.WriteLine("请输入要插入节点的数据:\n");
node.data = Console.ReadLine();
Console.WriteLine("请输入要查找的父节点数据:\n");
var parentData = Console.ReadLine();
if (tree == null)
{
Console.WriteLine("未找到您输入的父节点,请重新输入。");
continue;
}
Console.WriteLine("请确定要插入到父节点的:1 左侧,2 右侧");
Direction direction = (Direction)Enum.Parse(typeof(Direction), Console.ReadLine());
tree = mananger.BinTreeThreadAddNode(tree, node, parentData, direction);
Console.WriteLine("插入成功,是否继续? 1 继续, 2 退出");
if (int.Parse(Console.ReadLine()) == 1)
continue;
else
break;
}
return tree;
}
#endregion
}
#region 节点标识(用于判断孩子是节点还是线索)
/// summary>
/// 节点标识(用于判断孩子是节点还是线索)
/// /summary>
public enum NodeFlag
{
SubTree = 1,
Thread = 2
}
#endregion
#region 线索二叉树的结构
/// summary>
/// 线索二叉树的结构
/// /summary>
/// typeparam name="T">/typeparam>
public class ThreadTreeT>
{
public T data;
public ThreadTreeT> left;
public ThreadTreeT> right;
public NodeFlag leftFlag;
public NodeFlag rightFlag;
}
#endregion
#region 插入左节点或者右节点
/// summary>
/// 插入左节点或者右节点
/// /summary>
public enum Direction { Left = 1, Right = 2 }
#endregion
#region 线索二叉树的基本操作
/// summary>
/// 线索二叉树的基本操作
/// /summary>
public class ThreadTreeManager
{
#region 将指定节点插入到二叉树中
/// summary>
/// 将指定节点插入到二叉树中
/// /summary>
/// typeparam name="T">/typeparam>
/// param name="tree">/param>
/// param name="node">/param>
/// param name="direction">插入做左是右/param>
/// returns>/returns>
public ThreadTreeT> BinTreeThreadAddNodeT>(ThreadTreeT> tree, ThreadTreeT> node, T data, Direction direction)
{
if (tree == null)
return null;
if (tree.data.Equals(data))
{
switch (direction)
{
case Direction.Left:
if (tree.left != null)
throw new Exception("树的左节点不为空,不能插入");
else
tree.left = node;
break;
case Direction.Right:
if (tree.right != null)
throw new Exception("树的右节点不为空,不能插入");
else
tree.right = node;
break;
}
}
BinTreeThreadAddNode(tree.left, node, data, direction);
BinTreeThreadAddNode(tree.right, node, data, direction);
return tree;
}
#endregion
#region 中序遍历构建线索二叉树
/// summary>
/// 中序遍历构建线索二叉树
/// /summary>
/// typeparam name="T">/typeparam>
/// param name="tree">/param>
public void BinTreeThreadingCreate_LDRT>(ref ThreadTreeT> tree, ref ThreadTreeT> prevNode)
{
if (tree == null)
return;
//先左子树遍历,寻找起始点
BinTreeThreadingCreate_LDR(ref tree.left, ref prevNode);
//如果left为空,则说明该节点可以放“线索”
tree.leftFlag = (tree.left == null) ? NodeFlag.Thread : NodeFlag.SubTree;
//如果right为空,则说明该节点可以放“线索”
tree.rightFlag = (tree.right == null) ? NodeFlag.Thread : NodeFlag.SubTree;
if (prevNode != null)
{
if (tree.leftFlag == NodeFlag.Thread)
tree.left = prevNode;
if (prevNode.rightFlag == NodeFlag.Thread)
prevNode.right = tree;
}
//保存前驱节点
prevNode = tree;
BinTreeThreadingCreate_LDR(ref tree.right, ref prevNode);
}
#endregion
#region 查找指定节点的后继
/// summary>
/// 查找指定节点的后继
/// /summary>
/// typeparam name="T">/typeparam>
/// param name="tree">/param>
public ThreadTreeT> BinTreeThreadNext_LDRT>(ThreadTreeT> tree)
{
if (tree == null)
return null;
//如果查找节点的标志域中是Thread,则直接获取
if (tree.rightFlag == NodeFlag.Thread)
return tree.right;
else
{
//根据中序遍历的规则是寻找右子树中中序遍历的第一个节点
var rightNode = tree.right;
//如果该节点是subTree就需要循环遍历
while (rightNode.leftFlag == NodeFlag.SubTree)
{
rightNode = rightNode.left;
}
return rightNode;
}
}
#endregion
#region 查找指定节点的前驱
/// summary>
/// 查找指定节点的前驱
/// /summary>
/// typeparam name="T">/typeparam>
/// param name="tree">/param>
/// returns>/returns>
public ThreadTreeT> BinTreeThreadPrev_LDRT>(ThreadTreeT> tree)
{
if (tree == null)
return null;
//如果标志域中存放的是线索,那么可以直接找出来
if (tree.leftFlag == NodeFlag.Thread)
return tree.left;
else
{
//根据”中序“的规则可知,如果不为Thread,则要找出左子树的最后节点
//也就是左子树中最后输出的元素
var leftNode = tree.left;
while (leftNode.rightFlag == NodeFlag.SubTree)
leftNode = leftNode.right;
return leftNode;
}
}
#endregion
#region 遍历线索二叉树
/// summary>
/// 遍历线索二叉树
/// /summary>
/// typeparam name="T">/typeparam>
/// param name="tree">/param>
public void BinTreeThread_LDRT>(ThreadTreeT> tree)
{
if (tree == null)
return;
while (tree.leftFlag == NodeFlag.SubTree)
tree = tree.left;
do
{
Console.Write(tree.data + "\t");
tree = BinTreeThreadNext_LDR(tree);
} while (tree != null);
}
#endregion
}
#endregion
}