C# 二叉树 | 二叉顺序树 | 二叉链式树 的原理及代码实现

题外话: 诶,就是说家人们,我!真!是!个!天!才!!!
咳咳,怎样,自我催眠一下嘛。。
大学的时候,二叉树学的一脸懵逼,给孩子学自闭了,以为自己是不是智商不行,现在认真学一遍,不过如此嘛。
现在不仅明白了二叉树的结构、各种概念、各种遍历方式,甚至还能全靠自己写出来代码!!!啊哈哈哈哈!

PS: 本文的重点代码代码代码!而不是数据结构的一些基础概念和分类;
完全没接触过的同学,可以去看B站王卓教授的视频。


1.什么是二叉树?

  • 定义概念就别在我这看了,网上一大堆,套话一套一套的,我只说自己的理解。
  • 首先,它是一个树的结构(一对多,且不相交);
  • 其次,每个结点,最多有两个孩子(0个,1个,2个都可以);
  • 最后,孩子分老大老二(区分左孩子和右孩子)

逐条说明:

  1. 一对多? 如图,1结点对两个结点2和3,二叉树嘛,一对二也就是一对多。
  2. 不相交? 如果,你把2和3连一起了,这就不是二叉树了,连树都不是。也就是说: 一旦你是老大,你和老二,还有老二的子孙们,就不能有直系的血缘关系了(连线),你有关系,老二和你拼命!(笑)
  3. 最多俩孩子:1有俩孩子,3只有一个孩子,4没有孩子,这都是可以的。相当于应付二胎政策的年轻人,你不生可以、生一个好,生俩更好,但是生三个是要罚款的
  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;
        }
    }
}


结束语: 二叉树到此为止就告一段落了,关于二叉树的概念:结点、孩子、双亲、度、兄弟、堂兄弟这些随便找个其他博客就能看到了。
其实能静下心来看代码,基本都能明白什么意思。

猜你喜欢

转载自blog.csdn.net/Liyager/article/details/129066001