题外话: 诶,就是说家人们,我!真!是!个!天!才!!!
咳咳,怎样,自我催眠一下嘛。。
大学的时候,二叉树学的一脸懵逼,给孩子学自闭了,以为自己是不是智商不行,现在认真学一遍,不过如此嘛。
现在不仅明白了二叉树的结构、各种概念、各种遍历方式,甚至还能全靠自己写出来代码!!!啊哈哈哈哈!
PS: 本文的重点是代码代码代码!而不是数据结构的一些基础概念和分类;
完全没接触过的同学,可以去看B站王卓教授的视频。
1.什么是二叉树?
- 定义概念就别在我这看了,网上一大堆,套话一套一套的,我只说自己的理解。
- 首先,它是一个树的结构(一对多,且不相交);
- 其次,每个结点,最多有两个孩子(0个,1个,2个都可以);
- 最后,孩子分老大老二(区分左孩子和右孩子)
逐条说明:
- 一对多? 如图,1结点对两个结点2和3,二叉树嘛,一对二也就是一对多。
- 不相交? 如果,你把2和3连一起了,这就不是二叉树了,连树都不是。也就是说: 一旦你是老大,你和老二,还有老二的子孙们,就不能有直系的血缘关系了(连线),你有关系,老二和你拼命!(笑)
- 最多俩孩子:1有俩孩子,3只有一个孩子,4没有孩子,这都是可以的。相当于应付二胎政策的年轻人,你不生可以、生一个好,生俩更好,但是生三个是要罚款的。
- 孩子分老大老二:左右是不能互换的,你能把老二叫成老大嘛?显然不行。如图,6是3的右孩子,你不能说它是3的左孩子。(杠精来袭:啊,你没有老大哪来的老二?老大叫Null,开心不,意外不?)
2.两种特殊的二叉树
- 满二叉树:
- 每一层都满了的二叉树;
- 俗话: 除了最新一代还没生娃的年轻人,上面每一代人,都生了二胎。
- 完全二叉树:
- 在满二叉树中,从最后一个结点开始,连续去掉任意个数的结点,即是完全二叉树。
- 俗话: 额,这就不说俗话了,俗话很难形容这种按照顺序的不生娃,哈哈哈。
3.为什么要讲上面这两种特殊二叉树?
- 为了引入二叉树的“顺序结构”,即数组实现二叉树。
4.二叉树的两种存储结构
- 二叉树的顺序结构:
说明: 这里懒了,直接把笔记截图了,不要介意哈~
- 二叉树的链式结构
说明: 这里所谓的二叉链表和三叉链表的区别,也就是指针的数量嘛。
二叉链表带俩指针,分别指向左右孩子。
三叉链表是为了能让孩子能找到父母,所以多加了一个指针指向父节点而已,原理上是一致的。
5.二叉树的顺序结构代码实现
- 代码实现
namespace YoyoCode
{
/// <summary>
/// 顺序结构的二叉树,默认是完全二叉树的结构。
/// </summary>
internal class SqBTree<T>
{
public T[] _array;
public int MaxSize;
/// <summary>
/// 自带一个数组,构造一个二叉树,其中空结点用null表示。
/// 注意:如果T是值类型,请用?修饰,使其可以成为可空类型。
/// </summary>
/// <param name="array"></param>
public SqBTree(T[] array) {
_array = new T[array.Length];
Array.Copy(array, _array, array.Length);
}
/// <summary>
/// 创建树时,自定义顺序树的大小
/// </summary>
/// <param name="maxSize"></param>
public SqBTree(int maxSize = 4)
{
MaxSize = maxSize;
_array= new T[MaxSize];
}
/// <summary>
/// 根据索引值,设定树中某个结点的值
/// </summary>
public void SetTreeNode(int idx, T value)
{
if (idx < 0 || idx >= _array.Length)
{
throw (new OverflowException());
}
_array[idx] = value;
}
/// <summary>
/// 先序遍历
/// </summary>
/// <param name="idx">此id是数组的索引,一般传入0即可</param>
/// <param name="uFunc"></param>
public void PreOrderIteration(int idx ,Action<T> uFunc)
{
if (idx < 0 || idx >= _array.Length || _array[idx] == null)
{
return;
}
uFunc(_array[idx]);
PreOrderIteration( idx * 2 + 1 , uFunc);
PreOrderIteration( idx * 2 + 2 , uFunc);
}
/// <summary>
/// 中序遍历
/// </summary>
/// <param name="idx">此id是数组的索引,一般传入0即可</param>
/// <param name="uFunc"></param>
public void InOrderIteration(int idx, Action<T> uFunc)
{
if (idx < 0 || idx >= _array.Length || _array[idx] == null)
{
return;
}
InOrderIteration(idx * 2 + 1, uFunc);
uFunc(_array[idx]);
InOrderIteration(idx * 2 + 2, uFunc);
}
/// <summary>
/// 后序遍历
/// </summary>
/// <param name="idx">此id是数组的索引,一般传入0即可</param>
/// <param name="uFunc"></param>
public void PostOrderIteration(int idx, Action<T> uFunc)
{
if (idx < 0 || idx >= _array.Length || _array[idx] == null)
{
return;
}
PostOrderIteration(idx * 2 + 1, uFunc);
PostOrderIteration(idx * 2 + 2, uFunc);
uFunc(_array[idx]);
}
/// <summary>
/// 层次遍历
/// </summary>
/// <param name="uFunc"></param>
public void LevelIteration(Action<T> uFunc)
{
foreach (T t in _array)
{
if (t != null)
uFunc(t);
}
}
}
}
说明: 不知道先、中、后序、层次遍历的,可以去查查,是二叉树最重要的遍历方式。
- 测试代码及结果
6.二叉树的链式结构代码实现
namespace YoyoCode
{
internal class LinkedBTree<T>
{
public LinkedBTreeNode<T> Root;
/// <summary>
/// 通过一个数组,创建链式二叉树,该数组是完全二叉树表示法。
/// </summary>
/// <param name="array"></param>
/// <exception cref="ArgumentNullException"></exception>
public LinkedBTree(T[] array)
{
if (array == null) throw new ArgumentNullException("array cannot be null");
if (array[0] == null) throw new ArgumentNullException("二叉树的根节点不能为空!");
//创建根节点
Root = new LinkedBTreeNode<T>(array[0]);
CreateBTree(0, array, Root);
}
/// <summary>
/// 创建二叉树
/// </summary>
public void CreateBTree(int idx, T[] array, LinkedBTreeNode<T> curNode)
{
int lIdx = idx * 2 + 1;
int rIdx = idx * 2 + 2;
if (lIdx < array.Length && array[lIdx] != null)
{
var node = new LinkedBTreeNode<T>(array[lIdx]);
curNode.LChild = node;
CreateBTree(lIdx, array, node);
}
if (rIdx < array.Length && array[rIdx] != null)
{
var node = new LinkedBTreeNode<T>(array[rIdx]);
curNode.RChild = node;
CreateBTree(rIdx, array, node);
}
}
/// <summary>
/// 先序遍历
/// </summary>
public void PreOrderIteration(LinkedBTreeNode<T> root, Action<T> myFunc)
{
if (root == null)
{
return;
}
myFunc(root.Value);
PreOrderIteration(root.LChild, myFunc);
PreOrderIteration(root.RChild, myFunc);
}
/// <summary>
/// 中序遍历
/// </summary>
public void InOrderIteration(LinkedBTreeNode<T> root, Action<T> myFunc)
{
if (root == null)
{
return;
}
InOrderIteration(root.LChild, myFunc);
myFunc(root.Value);
InOrderIteration(root.RChild, myFunc);
}
/// <summary>
/// 后序遍历
/// </summary>
public void PostOrderIteration(LinkedBTreeNode<T> root, Action<T> myFunc)
{
if (root == null)
{
return;
}
PostOrderIteration(root.LChild, myFunc);
PostOrderIteration(root.RChild, myFunc);
myFunc(root.Value);
}
/// <summary>
/// 层次遍历,用队列实现
/// </summary>
public void LevelIteration(Action<T> myFunc)
{
Queue<LinkedBTreeNode<T>> myQueue = new Queue<LinkedBTreeNode<T>>();
LinkedBTreeNode<T> tempNode = null;
myQueue.Enqueue(Root);
while (myQueue.Count!= 0)
{
tempNode = myQueue.Dequeue();
myFunc(tempNode.Value);
if (tempNode.LChild != null)
{
myQueue.Enqueue(tempNode.LChild);
}
if (tempNode.RChild != null)
{
myQueue.Enqueue(tempNode.RChild);
}
}
}
}
/// <summary>
/// 树的结点
/// </summary>
/// <typeparam name="T"></typeparam>
internal class LinkedBTreeNode<T>
{
public LinkedBTreeNode<T> LChild;
public LinkedBTreeNode<T> RChild;
public T Value {
get; set; }
public LinkedBTreeNode(T value)
{
Value = value;
}
}
}
结束语: 二叉树到此为止就告一段落了,关于二叉树的概念:结点、孩子、双亲、度、兄弟、堂兄弟这些随便找个其他博客就能看到了。
其实能静下心来看代码,基本都能明白什么意思。