3.3.1

3.3.1 #

解答 #

结果如下:

E

AE

 |--E--|
A       S

 |--E--|
A       SY

 |--ES--|
A   Q   Y

 |--ES--|
A   Q   UY

       |--------S--------|
 |--E--|                 |--U--|
A       Q               T       Y

       |--------S--------|
 |--E--|                 |--U--|
A       IQ               T       Y

       |--------S--------|
 |--EO--|                 |--U--|
A   I   Q               T       Y

       |--------S--------|
 |--EO--|                 |--U--|
A   IN   Q               T       Y

代码 #

2-3 树的实现

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

// ReSharper disable CognitiveComplexity

namespace BalancedSearchTree
{
    /// <summary>
    ///     2-3 树。
    /// </summary>
    /// <typeparam name="TKey">键。</typeparam>
    /// <typeparam name="TValue">值。</typeparam>
    public class TwoThreeBst<TKey, TValue> : IOrderedSt<TKey, TValue>
        where TKey : IComparable<TKey>
    {
        private int _count;
        private Node _root;

        /// <inheritdoc />
        public void Put(TKey key, TValue value)
        {
            if (_root == null)
            {
                _root = new Node(null);
                _root.AddPair(key, value);
                return;
            }

            var (ends, current, _) = Get(null, _root, key);
            if (current != null)
                throw new InvalidOperationException($"The Key {key} has already been added");

            ends.AddPair(key, value);
            _root = BalanceBottomUp(ends);
            _count++;
        }

        /// <inheritdoc />
        public TValue Get(TKey key)
        {
            if (_root == null)
                return default;

            var (_, node, index) = Get(null, _root, key);
            if (node == null)
                return default;

            return node.Contents[index].Value;
        }

        /// <inheritdoc />
        public void Delete(TKey key)
        {
            var (parent, node, index) = Get(null, _root, key);
            if (node == null)
            {
                throw new InvalidOperationException("trying to delete a key not exists");
            }

            var pairToRemove = node.Contents[index];
            if (node.IsLeaf())
            {
                if (parent == null)
                {
                    // root node
                    node.Contents.Remove(pairToRemove);
                    if (node.Contents.Count == 0)
                    {
                        _root = null;
                    }

                    _count--;
                    return;
                }
                
                if (node.Degree == 2)
                {
                    // node is 2-node, make it 3 or 4
                    // find sibling
                    Node sibling;
                    if (node == parent.Left)
                    {
                        sibling = parent.Children[1];
                    }
                    else if (node == parent.Right)
                    {
                        sibling = parent.Children[^2];
                    }
                    else
                    {
                        sibling = parent.Left.Degree > parent.Right.Degree ? parent.Left : parent.Right;
                    }

                    if (sibling.Degree == 2)
                    {
                        node = Merge(node, sibling);
                    }
                    else
                    {
                        node = Redistribution(node, sibling);
                    }
                }

                // now node is not 2-node, direct remove
                node.Contents.Remove(pairToRemove);
                // all children is null, just delete last one
                node.Children.RemoveAt(node.Degree - 1);
                _count--;
                return;
            }

            var min = Min(node.Right);
            var t = node.Contents[index];
            node.Contents[index] = min.Contents[0];
            min.Contents[0] = t;
            node.Right = DeleteMin(node.Right);
            _count--;
        }

        /// <inheritdoc />
        public bool Contains(TKey key)
        {
            return Get(null, _root, key).Node != null;
        }

        /// <inheritdoc />
        public bool IsEmpty()
        {
            return _root == null;
        }

        /// <inheritdoc />
        public int Size()
        {
            return _count;
        }

        private int Size(Node x)
        {
            if (x == null)
            {
                return 0;
            }
            
            if (x.Degree == 2)
            {
                return 1 + Size(x.Left) + Size(x.Right);
            }

            return 2 + Size(x.Left) + Size(x.Middle) + Size(x.Right);
        }

        /// <inheritdoc />
        public IEnumerable<TKey> Keys()
        {
            if (IsEmpty())
            {
                return new List<TKey>();
            }

            return Keys(Min(), Max());
        }

        /// <inheritdoc />
        public int Size(TKey lo, TKey hi)
        {
            if (lo == null)
            {
                throw new ArgumentNullException(nameof(lo), "first argument to Size() is null");
            }

            if (hi == null)
            {
                throw new ArgumentNullException(nameof(hi), "second argument to Size() is null");
            }

            if (lo.CompareTo(hi) > 0)
            {
                return 0;
            }

            if (Contains(hi))
            {
                return Rank(hi) - Rank(lo) + 1;
            }

            return Rank(hi) - Rank(lo);
        }

        /// <inheritdoc />
        public IEnumerable<TKey> Keys(TKey lo, TKey hi)
        {
            if (lo == null)
            {
                throw new ArgumentNullException(nameof(lo), "first argument to Keys() is null");
            }

            if (hi == null)
            {
                throw new ArgumentNullException(nameof(hi), "second argument to Keys() is null");
            }

            var queue = new Queue<TKey>();
            Keys(_root, queue, lo, hi);
            return queue;
        }

        private void Keys(Node x, Queue<TKey> queue, TKey lo, TKey hi)
        {
            if (x == null)
            {
                return;
            }

            var cmpLo = lo.CompareTo(x.Contents[0].Key);
            var cmpHi = hi.CompareTo(x.Contents[0].Key);
            if (cmpLo < 0)
            {
                Keys(x.Left, queue, lo, hi);
            }

            if (cmpLo <= 0 && cmpHi >= 0)
            {
                queue.Enqueue(x.Contents[0].Key);
            }

            if (cmpHi > 0)
            {
                Keys(x.Children[1], queue, lo, hi);
            }

            // 3-node extra
            if (x.Children.Count == 3)
            {
                cmpLo = lo.CompareTo(x.Contents[1].Key);
                cmpHi = hi.CompareTo(x.Contents[1].Key);
                if (cmpLo < 0)
                {
                    Keys(x.Middle, queue, lo, hi);
                }

                if (cmpLo <= 0 && cmpHi >= 0)
                {
                    queue.Enqueue(x.Contents[1].Key);
                }

                if (cmpHi > 0)
                {
                    Keys(x.Right, queue, lo, hi);
                } 
            }
        }

        /// <inheritdoc />
        public TKey Min()
        {
            if (IsEmpty())
            {
                throw new InvalidOperationException("BST is empty");
            }

            return Min(_root).Contents[0].Key;
        }

        private Node Min(Node h)
        {
            if (h.Left == null)
            {
                return h;
            }

            return Min(h.Left);
        }

        /// <inheritdoc />
        public TKey Max()
        {
            if (IsEmpty())
            {
                throw new InvalidOperationException("BST is empty");
            }

            return Max(_root).Contents[^1].Key;
        }

        private Node Max(Node h)
        {
            if (h.Right == null)
            {
                return h;
            }

            return Max(h.Right);
        }

        /// <inheritdoc />
        public TKey Floor(TKey key)
        {
            if (key == null)
            {
                throw new ArgumentNullException(nameof(key), "argument to Floor() is null");
            }

            if (IsEmpty())
            {
                throw new InvalidOperationException("calls Floor() with empty symbol table");
            }

            var x = Floor(_root, key);
            if (x == null)
            {
                throw new InvalidOperationException("argument to Floor() is too small");
            }

            return x.Contents[0].Key;
        }

        private Node Floor(Node x, TKey key)
        {
            if (x == null)
            {
                return null;
            }

            var cmp = key.CompareTo(x.Contents[0].Key);
            if (cmp == 0)
            {
                return x;
            }

            if (cmp < 0)
            {
                return Floor(x.Left, key);
            }

            if (x.Degree == 3)
            {
                cmp = key.CompareTo(x.Contents[1].Key);
                if (cmp == 0)
                {
                    return x;
                }

                if (cmp < 0)
                {
                    return Floor(x.Middle, key);
                }
            }
            
            var t = Floor(x.Right, key);
            if (t != null)
            {
                return t;
            }

            return x;
        }

        /// <inheritdoc />
        public TKey Ceiling(TKey key)
        {
            if (key == null)
            {
                throw new ArgumentNullException(nameof(key), "argument to Ceiling() is null");
            }

            if (IsEmpty())
            {
                throw new InvalidOperationException("calls Ceiling with empty symbol table");
            }

            var x = Ceiling(_root, key);
            if (x == null)
            {
                throw new InvalidOperationException("argument to Ceiling is too small");
            }

            return x.Contents[^1].Key;
        }

        private Node Ceiling(Node x, TKey key)
        {
            if (x == null)
            {
                return null;
            }

            var cmp = key.CompareTo(x.Contents[0].Key);
            if (cmp == 0)
            {
                return x;
            }

            if (cmp > 0)
            {
                return Ceiling(x.Right, key);
            }

            if (x.Degree == 3)
            {
                cmp = key.CompareTo(x.Contents[1].Key);
                if (cmp == 0)
                {
                    return x;
                }

                if (cmp > 0)
                {
                    return Ceiling(x.Right, key);
                }
            }

            var t = Ceiling(x.Left, key);
            if (t != null)
            {
                return t;
            }

            return x;
        }

        /// <inheritdoc />
        public int Rank(TKey key)
        {
            if (key == null)
            {
                throw new ArgumentNullException(nameof(key), "argument to Rank() is null");
            }

            return Rank(_root, key);
        }

        private int Rank(Node x, TKey key)
        {
            if (x == null)
            {
                return 0;
            }

            var cmp = key.CompareTo(x.Contents[0].Key);
            if (cmp < 0)
            {
                return Rank(x.Left, key);
            }
            if (cmp > 0)
            {
                if (x.Degree == 2)
                {
                    return 1 + Size(x.Left) + Rank(x.Right, key);
                }

                cmp = key.CompareTo(x.Contents[1].Key);
                if (cmp < 0)
                {
                    return 1 + Size(x.Left) + Rank(x.Right, key); 
                }

                if (cmp > 0)
                {
                    return 1 + Size(x.Left) + Size(x.Middle) + Rank(x.Right, key);
                }

                return Size(x.Left) + Size(x.Middle);
            }

            return Size(x.Left);
        }

        /// <inheritdoc />
        public TKey Select(int k)
        {
            if (k < 0 || k >= Size())
            {
                throw new ArgumentOutOfRangeException(nameof(k), "argument to Select() is invalid " + k);
            }

            return Select(_root, k);
        }

        private TKey Select(Node x, int rank)
        {
            if (x == null)
            {
                return default;
            }

            var leftSize = Size(x.Left);
            if (leftSize > rank)
            {
                return Select(x.Left, rank);
            }

            if (leftSize < rank)
            {
                if (x.Degree == 3)
                {
                    leftSize += Size(x.Middle);
                    if (leftSize > rank)
                    {
                        return Select(x.Middle, rank - leftSize - 1);
                    }

                    if (leftSize == rank)
                    {
                        return x.Contents[1].Key;
                    }
                }
                
                return Select(x.Right, rank - leftSize - 1);
            }
            
            return x.Contents[0].Key;
        }

        /// <inheritdoc />
        public void DeleteMin()
        {
            if (IsEmpty())
            {
                throw new InvalidOperationException("BST underflow");
            }

            _root = DeleteMin(_root);
        }

        private Node DeleteMin(Node x)
        {
            if (x.Left == null)
            {
                // remove left key
                x.Contents.RemoveAt(0);
                x.Children.RemoveAt(0);
                return BalanceBottomUp(x);
            }

            if (x.Left.Degree != 2)
            {
                // left child is not 2-node, do nothing and turn left
                return DeleteMin(x.Left);
            }
            
            if (x.Children[1].Degree > 2)
            {
                // sibling is not 2-node, borrow one key into left
                
                // get key from parent
                x.Left.AddPair(x.Contents[0]);
                // parent get key from sibling
                x.Contents.RemoveAt(0);
                x.Contents.Insert(0, x.Children[1].Contents[0]);
                // sibling delete min key
                x.Children[1].Contents.RemoveAt(0);
                // move sibling's child to left's child
                x.Left.Children.Add(x.Children[1].Left);
                x.Children[1].Children.RemoveAt(0);

                return DeleteMin(x.Left);
            }
            
            // sibling is 2-node, merge them into 3-node
            if (x.Degree == 2)
            {
                // current node and its children are all 2-node
                x.AddPair(x.Left.Contents[0]);
                x.AddPair(x.Right.Contents[0]);
                x.Left.Children.ForEach(c => c.Parent = x);
                x.Right.Children.ForEach(c => c.Parent = x);
                var t = x.Left;
                x.Left = t.Left;
                x.MiddleLeft = t.Right;
                t = x.Right;
                x.MiddleRight = t.Left;
                x.Right = t.Right;
                return DeleteMin(x);
            }
            
            x.Left.AddPair(x.Contents[0]);
            x.Contents.RemoveAt(0);
            x.Left.AddPair(x.Children[1].Contents[0]);
            x.Left.MiddleRight = x.Children[1].Left;
            x.Left.Right = x.Children[1].Right;
            x.Children.RemoveAt(1);
            return DeleteMin(x.Left);
        }

        /// <inheritdoc />
        public void DeleteMax()
        {
            if (IsEmpty())
            {
                throw new InvalidOperationException("BST is empty");
            }

            _root = DeleteMax(_root);
        }

        private Node DeleteMax(Node x)
        {
            if (x.Right == null)
            {
                // remove right key
                x.Contents.RemoveAt(x.Contents.Count - 1);
                x.Children.RemoveAt(x.Degree - 1);
                return BalanceBottomUp(x);
            }

            if (x.Right.Degree != 2)
            {
                // left child is not 2-node, do nothing and turn left
                return DeleteMax(x.Right);
            }
            
            if (x.Children[^2].Degree > 2)
            {
                // sibling is not 2-node, borrow one key into left
                
                // get key from parent
                x.Right.AddPair(x.Contents[^1]);
                // parent get key from sibling
                x.Contents.RemoveAt(x.Contents.Count - 1);
                x.Contents.Insert(x.Contents.Count - 1, x.Children[^2].Contents[^1]);
                // sibling delete max key
                x.Children[^2].Contents.RemoveAt(x.Children[^2].Contents.Count - 1);
                // move sibling's child to right's child
                x.Right.Children.Add(x.Children[^2].Right);
                x.Children[^2].Children.RemoveAt(x.Children[^2].Degree - 1);

                return DeleteMax(x.Right);
            }
            
            // sibling is 2-node, merge them into 3-node
            if (x.Degree == 2)
            {
                // current node and its children are all 2-node
                x.AddPair(x.Left.Contents[0]);
                x.AddPair(x.Right.Contents[0]);
                x.Left.Children.ForEach(c => c.Parent = x);
                x.Right.Children.ForEach(c => c.Parent = x);
                var t = x.Left;
                x.Left = t.Left;
                x.MiddleLeft = t.Right;
                t = x.Right;
                x.MiddleRight = t.Left;
                x.Right = t.Right;
                return DeleteMax(x);
            }
            
            x.Right.AddPair(x.Contents[^1]);
            x.Contents.RemoveAt(x.Contents.Count - 1);
            x.Right.AddPair(x.Children[^2].Contents[^1]);
            x.Left.MiddleRight = x.Children[^2].Left;
            x.Left.Right = x.Children[^2].Right;
            x.Children.RemoveAt(x.Degree - 2);
            return DeleteMax(x.Right);
        }

        public override string ToString()
        {
            if (IsEmpty())
            {
                return string.Empty;
            }

            var maxDepth = Depth(_root);
            var layer = 0;
            var bottomLine = (int)Math.Pow(3, maxDepth) * 2;

            // BFS
            var lines = new List<string>();
            var nowLayer = new Queue<Node>();
            var nextLayer = new Queue<Node>();
            nextLayer.Enqueue(_root);

            while (layer != maxDepth)
            {
                var sb = new StringBuilder();
                var unitSize = bottomLine / (int)Math.Pow(3, layer);
                var temp = nowLayer;
                nowLayer = nextLayer;
                nextLayer = temp;

                while (nowLayer.Count != 0)
                {
                    var x = nowLayer.Dequeue();

                    if (x != null)
                    {
                        nextLayer.Enqueue(x.Left);
                        if (x.Degree == 3)
                            nextLayer.Enqueue(x.Middle);
                        else
                            nextLayer.Enqueue(null);
                        nextLayer.Enqueue(x.Right);
                    }
                    else
                    {
                        nextLayer.Enqueue(null);
                        nextLayer.Enqueue(null);
                        nextLayer.Enqueue(null);
                    }

                    if (x != null && x.Left != null)
                    {
                        for (var i = 0; i < unitSize / 6; i++)
                            sb.Append(" ");
                        sb.Append("|");
                        for (var i = 1; i < unitSize / 6; i++)
                            sb.Append("-");
                    }
                    else
                    {
                        for (var i = 0; i < unitSize / 3; i++)
                            sb.Append(" ");
                    }

                    if (x == null)
                        sb.Append(" ");
                    else
                        sb.Append(x.Contents.Aggregate(string.Empty, (s, n) => s + n.Key));

                    if (x != null && x.Right != null)
                    {
                        for (var i = 1; i < unitSize / 6; i++)
                            sb.Append("-");
                        sb.Append("|");
                        for (var i = 1; i < unitSize / 6; i++)
                            sb.Append(" ");
                    }
                    else
                    {
                        for (var i = 1; i < unitSize / 3; i++)
                            sb.Append(" ");
                    }
                }
                lines.Add(sb.ToString());
                layer++;
            }

            // Trim
            var margin = int.MaxValue;
            foreach (var line in lines)
            {
                var firstNonWhite = 0;
                for (var i = 0; i < line.Length; i++)
                {
                    if (line[i] == ' ') continue;
                    firstNonWhite = i;
                    break;
                }

                margin = Math.Min(margin, firstNonWhite);
            }

            for (var i = 0; i < lines.Count; i++)
            {
                lines[i] = lines[i].Substring(margin);
            }

            var result = new StringBuilder();
            foreach (var line in lines)
            {
                result.AppendLine(line);
            }

            return result.ToString();
        }

        /// <summary>
        /// 获取 2-3 树的最大深度。
        /// </summary>
        /// <param name="x">二叉树的根结点。</param>
        /// <returns>二叉树的最大深度。</returns>
        private int Depth(Node x)
        {
            if (x == null) return 0;
            if (x.Degree == 2) return 1 + Math.Max(Depth(x.Left), Depth(x.Right));
            return 1 + Math.Max(Depth(x.Left), Math.Max(Depth(x.Middle), Depth(x.Right)));
        }
        
        /// <summary>
        /// 自底向上对 2-3 树进行平衡。
        /// </summary>
        /// <param name="node"></param>
        /// <returns></returns>
        private static Node BalanceBottomUp(Node node)
        {
            if (node.Degree != 4)
            {
                // not 4-node
                return node.Parent == null ? node : BalanceBottomUp(node.Parent);
            }

            // break 4-node into 2 2-node
            var left = new Node(node.Parent);
            left.AddPair(node.Contents[0]);
            left.Left = node.Left;
            left.Right = node.MiddleLeft;

            var right = new Node(node.Parent);
            right.AddPair(node.Contents[2]);
            right.Left = node.MiddleRight;
            right.Right = node.Right;

            if (node.Parent == null)
            {
                // root is 4-node
                var root = new Node(null);
                root.AddPair(node.Contents[1]);
                root.Left = left;
                root.Right = right;
                left.Parent = root;
                right.Parent = root;
                return root;
            }

            node.Parent.AddPair(node.Contents[1]);
            if (node.Parent.Degree == 3)
            {
                // parent was 2-node
                if (node == node.Parent.Children[0])
                {
                    // 4-node is 2-node's left child
                    node.Parent.Left = left;
                    node.Parent.Middle = right;
                }
                else
                {
                    // 4-node is 2-node's right child
                    node.Parent.Middle = left;
                    node.Parent.Right = right;
                }

                return BalanceBottomUp(node.Parent);
            }

            // parent was 3-node
            if (node == node.Parent.Children[0])
            {
                // 4-node is 3-node's left child
                node.Parent.Left = left;
                node.Parent.MiddleLeft = right;
            }
            else if (node == node.Parent.Children[1])
            {
                // 4-node is 3-node's middle child
                node.Parent.MiddleLeft = left;
                node.Parent.MiddleRight = right;
            }
            else
            {
                // 4-node is 3-node's right child
                node.Parent.MiddleRight = left;
                node.Parent.Right = right;
            }

            return BalanceBottomUp(node.Parent);
        }

        /// <summary>
        /// 在 2-3 树中搜索 <paramref name="key"/>。
        /// </summary>
        /// <param name="parent">父结点。</param>
        /// <param name="current">当前结点</param>
        /// <param name="key">要搜索的键。</param>
        /// <returns>父结点、当前结点和键的下标。</returns>
        private static (Node Parent, Node Node, int Index) Get(Node parent, Node current, TKey key)
        {
            if (current == null)
                return (parent, null, -1);

            for (var i = 0; i < current.Contents.Count; i++)
            {
                var pair = current.Contents[i];
                var cmp = key.CompareTo(pair.Key);
                if (cmp == 0)
                    return (parent, current, i);

                if (cmp < 0)
                    return Get(current, current.Children[i], key);
            }

            // larger to all keys, turn right
            return Get(current, current.Children[^1], key);
        }

        /// <summary>
        /// 将两个结点合并。
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <returns></returns>
        /// <exception cref="InvalidOperationException"></exception>
        private static Node Merge(Node x, Node y)
        {
            if (x.Degree != 2 || y.Degree != 2)
            {
                throw new InvalidOperationException("x and y must all be 2-node to merge");
            }

            if (x.Parent != y.Parent)
            {
                throw new InvalidOperationException("two nodes must be siblings to perform merge");
            }

            var parent = x.Parent;
            if (parent.Degree == 2)
            {
                parent.AddPair(x.Contents[0]);
                parent.AddPair(y.Contents[0]);
                parent.Left = x.Left;
                parent.MiddleLeft = x.Right;
                parent.MiddleRight = y.Left;
                parent.Right = y.Right;
                return parent; 
            }

            if (x == parent.Left || y == parent.Left)
            {
                // merge middle to left
                x = parent.Left;
                y = parent.Middle;

                x.AddPair(parent.Contents[0]);
                x.AddPair(y.Contents[0]);
                x.MiddleRight = y.Left;
                x.Right = y.Right;
                parent.Contents.RemoveAt(0);
                parent.Children.Remove(y);
                return parent;
            }
            
            // merge middle to right
            x = parent.Middle;
            y = parent.Right;
            
            y.AddPair(parent.Contents[^1]);
            y.AddPair(x.Contents[0]);
            y.Left = x.Left;
            y.MiddleLeft = x.Right;
            parent.Contents.RemoveAt(parent.Contents.Count - 1);
            parent.Children.Remove(x);

            return parent;
        }

        /// <summary>
        /// 合并兄弟结点。
        /// </summary>
        /// <param name="node">结点。</param>
        /// <param name="sibling"><paramref name="node"/> 的兄弟结点。</param>
        /// <returns></returns>
        /// <exception cref="InvalidOperationException"></exception>
        private static Node Redistribution(Node node, Node sibling)
        {
            if (node.Degree != 2 || sibling.Degree == 2)
            {
                throw new InvalidOperationException("node must be 2-node and sibling must not be 2-node");
            }

            if (node.Parent != sibling.Parent)
            {
                throw new InvalidOperationException("two nodes must be siblings to perform merge");
            }

            var parent = node.Parent;
            if (node == parent.Left)
            {
                // flow to left
                node.AddPair(parent.Contents[0]);
                node.Right = sibling.Left;
                parent.Contents[0] = sibling.Contents[0];
                sibling.Contents.RemoveAt(0);
                sibling.Children.RemoveAt(0);
            }
            else if (node == parent.Right)
            {
                // flow to right
                node.AddPair(parent.Contents[^1]);
                node.Left = sibling.Right;
                parent.Contents[^1] = sibling.Contents[^1];
                sibling.Contents.RemoveAt(sibling.Contents.Count - 1);
                sibling.Children.RemoveAt(sibling.Children.Count - 1);
            }
            else
            {
                // flow to middle
                if (sibling == parent.Left)
                {
                    node.AddPair(parent.Contents[0]);
                    parent.Contents[0] = sibling.Contents[^1];
                    node.Children[0] = sibling.Right;
                    sibling.Contents.RemoveAt(sibling.Contents.Count - 1);
                    sibling.Children.RemoveAt(sibling.Children.Count - 1);
                }
                else
                {
                    node.AddPair(parent.Contents[^1]);
                    parent.Contents[^1] = sibling.Contents[0];
                    node.Children[^1] = sibling.Left;
                    sibling.Contents.RemoveAt(0);
                    sibling.Children.RemoveAt(0);
                }
            }

            return node;
        }

        /// <summary>
        /// 2-3 树结点。
        /// </summary>
        private class Node
        {
            /// <summary>
            /// 构造一个空的 2-3 树结点,并设置父结点。
            /// </summary>
            /// <param name="parent">父结点。</param>
            public Node(Node parent)
            {
                Parent = parent;
            }

            /// <summary>
            /// 父结点。
            /// </summary>
            public Node Parent { get; set; }
            
            /// <summary>
            /// 结点保存的键值对。
            /// </summary>
            public List<Pair> Contents { get; } = new();
            
            /// <summary>
            /// 结点的子结点。
            /// </summary>
            public List<Node> Children { get; } = new() { null };

            /// <summary>
            /// 结点的度,例如 2-结点返回的就是 2。
            /// </summary>
            public int Degree => Children.Count;

            /// <summary>
            /// 左子结点。
            /// </summary>
            public Node Left
            {
                get => Children[0];
                set
                {
                    if (value != null)
                        value.Parent = this;
                    Children[0] = value;
                }
            }

            /// <summary>
            /// 第二个子结点,只有当 <see cref="Degree"/> 是 4 时才可以访问这个属性。
            /// </summary>
            /// <exception cref="InvalidOperationException"><see cref="Degree"/> 不为 4。</exception>
            public Node MiddleLeft
            {
                get
                    => Degree == 4
                        ? Children[1]
                        : throw new InvalidOperationException("only 4-node has middle left");
                set
                {
                    if (value != null)
                        value.Parent = this;
                    Children[1] = value;
                }
            }

            /// <summary>
            /// 位于中间的子结点,只有当 <see cref="Degree"/> 为 3 时才可以访问这个属性。
            /// </summary>
            /// <exception cref="InvalidOperationException"><see cref="Degree"/> 不为 3。</exception>
            public Node Middle
            {
                get => Degree == 3
                    ? Children[1]
                    : throw new InvalidOperationException("only 3-node has middle");
                set
                {
                    if (value != null)
                        value.Parent = this;
                    Children[1] = value;
                }
            }

            /// <summary>
            /// 第三个子结点,只有当 <see cref="Degree"/> 是 4 时才可以访问这个属性。
            /// </summary>
            /// <exception cref="InvalidOperationException"><see cref="Degree"/> 不为 4。</exception>
            public Node MiddleRight
            {
                get
                    => Degree == 4
                        ? Children[2]
                        : throw new InvalidOperationException("only 4-node has middle right");
                set
                {
                    if (value != null)
                        value.Parent = this;
                    Children[2] = value;
                }
            }

            /// <summary>
            /// 右子结点。
            /// </summary>
            public Node Right
            {
                // ^1 即最后一个,与 Children.Count - 1 等同。
                get => Children[^1];
                set
                {
                    if (value != null)
                        value.Parent = this;
                    Children[^1] = value;
                }
            }

            /// <summary>
            /// 向结点添加一个键值对。
            /// </summary>
            /// <param name="key">键。</param>
            /// <param name="value">值。</param>
            public void AddPair(TKey key, TValue value)
            {
                var pair = new Pair(key, value);
                AddPair(pair);
            }

            /// <summary>
            /// 向结点添加一个键值对。
            /// </summary>
            /// <param name="pair">键值对。</param>
            public void AddPair(Pair pair)
            {
                for (var i = 0; i < Contents.Count; i++)
                {
                    var cmp = pair.Key.CompareTo(Contents[i].Key);
                    if (cmp < 0)
                    {
                        Contents.Insert(i, pair);
                        Children.Insert(i + 1, null);
                        return;
                    }
                }

                Contents.Add(pair);
                Children.Add(null);
            }

            /// <summary>
            /// 结点是否为叶结点(没有子结点)。
            /// </summary>
            /// <returns></returns>
            public bool IsLeaf()
            {
                return Children.All(c => c == null);
            }

            /// <summary>
            /// 拼接并输出结点的键。
            /// </summary>
            /// <returns></returns>
            public override string ToString()
            {
                return Contents.Aggregate(string.Empty, (s, k) => s + k.Key);
            }
        }

        /// <summary>
        /// 键值对。
        /// </summary>
        private class Pair
        {
            /// <summary>
            /// 新建一个键值对。
            /// </summary>
            /// <param name="key">键。</param>
            /// <param name="value">值。</param>
            public Pair(TKey key, TValue value)
            {
                Key = key;
                Value = value;
            }

            /// <summary>
            /// 键。
            /// </summary>
            public TKey Key { get; }
            
            /// <summary>
            /// 值。
            /// </summary>
            public TValue Value { get; }
        }
    }
}

另请参阅 #

[BalancedSearchTree ](https://github.com/ikesnowy/Algorithms-4th-Edition-in-Csharp/tree/master/3 Searching/3.3/BalancedSearchTree)