二叉树以及其节点的实现
1. 二叉树节点的定义及实现
根据二叉树的特点可知,二叉树的子节点最多有两个子树,分别是左子树和右子树。所以,一个二叉树的节点应有三个部分组成:
² 当前节点数据<T>;
² 左子节点的引用;
² 右子节点的引用。
如下图左部分的Node类定义。

Node类的实现:

Code
/// <summary>
/// 二叉树节点代码
/// </summary>
/// <typeparam name="T"></typeparam>
public class Node<T>
{
private T _data;
private Node<T> _lChild;
private Node<T> _rChild;
/// <summary>
/// 二叉树当前节点的数据
/// </summary>
public T Data
{
get { return _data; }
set { _data = value; }
}
/// <summary>
/// 二叉树左子节点的引用
/// </summary>
public Node<T> LChild
{
get { return _lChild; }
set { _lChild = value; }
}
/// <summary>
/// 二叉树右子节点的引用
/// </summary>
public Node<T> RChild
{
get { return _rChild; }
set { _rChild = value; }
}
public Node()
{
_data = default(T);
_lChild = null;
_rChild = null;
}
public Node(T _data)
{
this._data = _data;
_lChild = null;
_rChild = null;
}
public Node(T _data, Node<T> _lChild, Node<T> _rChild)
{
this._data = _data;
this._lChild = _lChild;
this._rChild = _rChild;
}
public Node(Node<T> _lChild, Node<T> _rChild)
{
this._lChild = _lChild;
this._rChild = _rChild;
}
}
2 二叉树的实现
二叉树的实现主要有能下几个功能:
Ø 构造一个新的二叉树
Ø 插入指定节点的左子节点;
Ø 插入指定节点的右子节点;
Ø 删除指定节点的左子节点;
Ø 删除指定节点的右子节点;
Ø 判断二叉树是否为空;
Ø 判断节点是否为叶子节点。
代码实现如下:

Code
1
/**//// <summary>
2
/// 二叉树的操作实现
3
/// </summary>
4
/// <typeparam name="T"></typeparam>
5
public class BinaryTree<T>
6
{
7
private Node<T> _header;
8
9
10
..构造函数..#region ..构造函数..
11
12
public BinaryTree()
13
{
14
_header = null;
15
}
16
17
public BinaryTree(T _data)
18
{
19
Node<T> _headerNode = new Node<T>(_data);
20
this._header = _headerNode;
21
}
22
23
public BinaryTree(T _data, Node<T> _lChild, Node<T> _rChild)
24
{
25
Node<T> _headerNode = new Node<T>(_data, _lChild, _rChild);
26
this._header = _headerNode;
27
}
28
29
#endregion
30
31
/**//// <summary>
32
/// 二叉树根节点(头节点)
33
/// </summary>
34
public Node<T> Header
35
{
36
get
{ return _header; }
37
set
{ _header = value; }
38
}
39
40
/**//// <summary>
41
/// 插入左子节点
42
/// </summary>
43
/// <param name="_fNode"></param>
44
/// <param name="_data"></param>
45
public void InsertL(Node<T> _fNode, T _data)
46
{
47
/**//*
48
* 步骤:
49
* 1 New新的节点
50
* 2 新节点引用要原父节点的左子节点
51
* 3 父节点的左子节点引用新节点
52
*/
53
Node<T> _node = new Node<T>(_data);
54
_node.LChild = _fNode.LChild;
55
_fNode.LChild = _node;
56
}
57
58
/**//// <summary>
59
/// 插入右子节点
60
/// </summary>
61
/// <param name="_fNode"></param>
62
/// <param name="_data"></param>
63
public void InsertR(Node<T> _fNode, T _data)
64
{
65
//思路同插入左子节点
66
Node<T> _node = new Node<T>(_data);
67
_node.LChild = _fNode.RChild;
68
_fNode.RChild = _node;
69
}
70
71
72
/**//// <summary>
73
/// 删除指定节点的左子节点
74
/// </summary>
75
/// <param name="node"></param>
76
public Node<T> DeleteL(Node<T> node)
77
{
78
if (node == null || node.LChild == null)
79
{
80
return null;
81
}
82
/**//*
83
* 步骤:
84
* 1 获取左子节点的引用
85
* 2 把指定节点的左子节点引用设为NULL
86
* 3 返回左子节点的引用
87
*/
88
Node<T> lChild = node.LChild;
89
node.LChild = null;
90
return lChild;
91
}
92
93
/**//// <summary>
94
/// 删除指定节点的右子节点
95
/// </summary>
96
/// <param name="node"></param>
97
/// <returns></returns>
98
public Node<T> DeleteR(Node<T> node)
99
{
100
if (node == null || node.RChild == null)
101
{
102
return null;
103
}
104
//步骤同删除指定节点的左子节点相同,只不过是首先获取的是右子节点的引用
105
Node<T> rChild = node.RChild;
106
node.RChild = null;
107
return rChild;
108
}
109
110
/**//// <summary>
111
/// 判断是否为空
112
/// </summary>
113
/// <returns></returns>
114
public bool IsEmpty()
115
{
116
return _header == null;
117
}
118
119
/**//// <summary>
120
/// 判断指定的节点是否为叶子节点
121
/// </summary>
122
/// <param name="_node"></param>
123
/// <returns></returns>
124
public bool IsLeaf(Node<T> _node)
125
{
126
//叶子结点(Leaf Node):度为0的结点,也叫终端结点
127
return (_node != null && _node.LChild == null && _node.RChild == null);
128
}
129
}
3 二叉树的遍历
二叉树的遍历是按照某种顺序对树中的每个节点访问且只能访问一次的过程,包括查询、修改、计算等。
二叉树的遍历实际是将非线性结构线性化,它是二叉树的各种计算和操作的基础。
由二叉树的定义可知,一棵二叉树由根结点、左子树和右子树三部分组成,若规定D、L、R分别代表遍历根结点、遍历左子树、遍历右子树,则二叉树的遍历方式有6种:DLR、DRL、LDR、LRD、RDL、RLD。由于先遍历左子树和先遍历右子树在算法设计上没有本质区别,所以,只讨论三种方式:DLR(先序遍历)、LDR(中序遍历)和LRD(后序遍历)。
先序遍历:先访问根节点,再访问左子树,再访问右子树。
中序遍历:先访问左子树,再访问根节点,最后访问右子树。
后序遍历:先访问左子以,再访问右子树,最后访问根节点。
3.1 先序遍历(DLR)
先序遍历的基本思想是:首先访问根结点,然后先序遍历其左子树,最后先序遍历其右子树。先序遍历的递归算法实现如下。(这里只是获取节点的值)

Code
/// <summary>
/// 先序遍历之递归
/// </summary>
/// <param name="node"></param>
/// <returns></returns>
public string perOrderNode(Node<char> node)
{
if (node == null)
return null;
StringBuilder nodeValues = new StringBuilder();
nodeValues.Append(node.Data);
//递归调用
nodeValues.Append(perOrderNode(node.LChild));
nodeValues.Append(perOrderNode(node.RChild));
return nodeValues.ToString();
}
public string perOrderRecursion(BinaryTree.BinaryTree<char> tree)
{
return perOrderNode(tree.Header);
}
非递归算法实现:

Code
/// <summary>
/// 二叉树先序遍历之非递归算法
/// 利用栈实现二叉树的遍历.
/// </summary>
/// <param name="tree"></param>
/// <returns></returns>
public string perOrderNoRecursion(BinaryTree.BinaryTree<char> tree)
{
StringBuilder result = new StringBuilder();
Stack<Node<char>> rightStack = new Stack<Node<char>>();
Node<char> curNode = tree.Header;
while (curNode != null || rightStack.Count != 0)
{
if (curNode != null)
{
result.Append(curNode.Data);
if (curNode.RChild != null)
{
//如果右子节点不为NULL,则先放入Stack中.
rightStack.Push(curNode.RChild);
}
curNode = curNode.LChild;
}
else
{
curNode = rightStack.Pop();
}
}
return result.ToString();
}
3.2 中序遍历(LDR)
中序遍历的基本思想是:首先中序遍历根结点的左子树,然后访问根结点,最后中序遍历其右子树。中序遍历的递归算法实现如下: (附件)
3.3 后序遍历(LRD)
后序遍历的基本思想是:首先后序遍历根结点的左子树,然后后序遍历根结点的右子树,最后访问根结点。后序遍历的递归算法实现如下: (附件)
代码下载
【推荐】还在用 ECharts 开发大屏?试试这款永久免费的开源 BI 工具!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET制作智能桌面机器人:结合BotSharp智能体框架开发语音交互
· 软件产品开发中常见的10个问题及处理方法
· .NET 原生驾驭 AI 新基建实战系列:向量数据库的应用与畅想
· 从问题排查到源码分析:ActiveMQ消费端频繁日志刷屏的秘密
· 一次Java后端服务间歇性响应慢的问题排查记录
· 互联网不景气了那就玩玩嵌入式吧,用纯.NET开发并制作一个智能桌面机器人(四):结合BotSharp
· Vite CVE-2025-30208 安全漏洞
· 《HelloGitHub》第 108 期
· MQ 如何保证数据一致性?
· 一个基于 .NET 开源免费的异地组网和内网穿透工具