C#数据结构-红黑树实现

在前辈大神的代码基础,完善了删除节点的逻辑,红黑树删除节点时,增加删除节点没有子结点和只有一个子节点的情况处理。

同时,对很多实现步骤增加了注释,让代码更加容易理解一些。

更详细的介绍,请看 C#数据结构-七大查找算法中关于红黑树的介绍

倪升武的博客 对红黑树有很深的理解,本文的红黑树实现就是在此基础上实现的。

简述一下,红黑树的查找和删除的实现原理:

红黑树的查找:

将要查找的value和节点的value比较,如果小于,那么就在Left Node节点查找;如果大于,则在Right Node节点查找,如果相等,更新Value。

红黑树的删除:

1.如果删除节点没有子节点,直接返回null

2.如果只有一个子节点,返回其子节点代替删除节点即可

3.当左右子节点都不为空时,找到其右子树中的最小节点,替换删除节点的位置

在实现原理上,红黑树的查找和删除,跟二叉查找树是一致的。不过,为了保证树在插入、删除完成之后,保持红黑树的平衡状态,需要实现更多更复杂的逻辑。

using System;
using System.Collections.Generic;

namespace StructScript
{
    /// 红黑树定义:
    /// 性质1.节点是红色或黑色
    /// 性质2.根是黑色
    /// 性质3.所有叶子都是黑色(叶子是NIL节点)
    /// 性质4.如果一个节点是红的,则它的两个子节点都是黑的(从每个叶子到根的所有路径上不能有两个连续的红色节点)
    /// 性质5.从任一节点到其叶子的所有路径都包含相同数目的黑色节点。
    public class RedBlackTree<T>
    {
        //根节点
        private RedBlackTreeNode<T> mRoot;
        //比较器
        private Comparer<T> mComparer;
        private const bool RED = true;
        private const bool BLACK = false;

        public RedBlackTree()
        {
            mRoot = null;
            mComparer = Comparer<T>.Default;
        }

        public bool Contains(T value)
        {
            RedBlackTreeNode<T> node;
            return Contain(value, out node);
        }

        public bool Contain(T value, out RedBlackTreeNode<T> newNode)
        {
            if (value == null)
            {
                throw new ArgumentNullException();
            }
            newNode = null;
            RedBlackTreeNode<T> node = mRoot;
            while (node != null)
            {
                int comparer = mComparer.Compare(value, node.Data);
                if (comparer > 0)
                {
                    node = node.RightChild;
                }
                else if (comparer < 0)
                {
                    node = node.LeftChild;
                }
                else
                {
                    newNode = node;
                    return true;
                }
            }
            return false;
        }

        public void Add(T value)
        {
            if (mRoot == null)
            {
                // 根节点是黑色的
                mRoot = new RedBlackTreeNode<T>(value, BLACK);
            }
            else
            {
                // 新插入节点是红色的
                Insert1(new RedBlackTreeNode<T>(value, RED), value);
            }
        }

        private void Insert1(RedBlackTreeNode<T> newNode, T value)
        {
            //遍历找到插入位置
            RedBlackTreeNode<T> node = mRoot;
            //插入节点的父节点
            RedBlackTreeNode<T> parent = null;
            while (node != null)
            {
                parent = node;
                int comparer = mComparer.Compare(value, node.Data);
                if (comparer > 0)
                {
                    node = node.RightChild;
                }
                else if (comparer < 0)
                {
                    node = node.LeftChild;
                }
                else
                {
                    node.Data = value;
                    return;
                }
            }
            //找到插入位置,设置新插入节点的父节点为current
            newNode.Parent = parent;
            //比较插入节点的值跟插入位置的值的大小, 插入新节点
            int comparer1 = mComparer.Compare(value, parent.Data);
            if (comparer1 > 0)
            {
                parent.RightChild = newNode;
            }
            else if (comparer1 < 0)
            {
                parent.LeftChild = newNode;
            }
            //将它重新修整为一颗红黑树
            InsertFixUp(newNode);
        }

        private void InsertFixUp(RedBlackTreeNode<T> newNode)
        {
            RedBlackTreeNode<T> parent = newNode.Parent; //插入节点的父节点
            RedBlackTreeNode<T> gParent = null; //插入节点的祖父节点
            //父节点的颜色是红色,并且不为空
            while (IsRed(parent) && parent != null)
            {
                //获取祖父节点,这里不用判空,
                //因为如果祖父节点为空,parent就是根节点,根节点是黑色,不会再次进入循环
                gParent = parent.Parent;
                //若父节点是祖父节点的左子节点 
                if (parent == gParent.LeftChild)
                {
                    RedBlackTreeNode<T> uncle = gParent.RightChild; //获得叔叔节点  

                    //case1: 叔叔节点也是红色  
                    if (uncle != null && IsRed(uncle))
                    {
                        //把父节点和叔叔节点涂黑,祖父节点涂红
                        parent.Color = BLACK;
                        uncle.Color = BLACK;
                        gParent.Color = RED;
                        //把祖父节点作为插入节点,向上继续遍历
                        newNode = gParent;
                        parent = newNode.Parent;
                        continue; //继续while,重新判断  
                    }

                    //case2: 叔叔节点是黑色,且当前节点是右子节点  
                    if (newNode == parent.RightChild)
                    {
                        //从父节点处左旋
                        //当这种情况时,只能左旋,因为父亲节点和祖父节点变色,无论左旋还是右旋,都会违背红黑树的基本性质
                        RotateLeft(parent);
                        //当左旋后,红黑树变成case3的情况,区别就是插入节点是父节点
                        //所以,将父节点和插入节点调换一下,为下面右旋做准备
                        RedBlackTreeNode<T> tmp = parent;
                        parent = newNode;
                        newNode = tmp;
                    }

                    //case3: 叔叔节点是黑色,且当前节点是左子节点
                    // 父亲和祖父节点变色,从祖父节点处右旋
                    parent.Color = BLACK;
                    gParent.Color = RED;
                    RotateRight(gParent);
                }
                else
                {
                    //若父节点是祖父节点的右子节点,与上面的完全相反
                    RedBlackTreeNode<T> uncle = gParent.LeftChild;

                    //case1: 叔叔节点也是红色  
                    if (uncle != null & IsRed(uncle))
                    {
                        //把父节点和叔叔节点涂黑,祖父节点涂红
                        parent.Color = BLACK;
                        uncle.Color = BLACK;
                        gParent.Color = RED;
                        //把祖父节点作为插入节点,向上继续遍历
                        newNode = gParent;
                        parent = newNode.Parent;
                        continue;//继续while,重新判断
                    }

                    //case2: 叔叔节点是黑色的,且当前节点是左子节点  
                    if (newNode == parent.LeftChild)
                    {
                        //从父节点处右旋
                        //当这种情况时,只能右旋,因为父亲节点和祖父节点变色,无论左旋还是右旋,都会违背红黑树的基本性质
                        RotateRight(parent);
                        RedBlackTreeNode<T> tmp = parent;
                        parent = newNode;
                        newNode = tmp;
                    }

                    //case3: 叔叔节点是黑色的,且当前节点是右子节点  
                    // 父亲和祖父节点变色,从祖父节点处右旋
                    parent.Color = BLACK;
                    gParent.Color = RED;
                    RotateLeft(gParent);
                }
            }
            //将根节点设置为黑色
            mRoot.Color = BLACK;
        }

        public bool IsRed(RedBlackTreeNode<T> node)
        {
            if (node == null)
            {
                return false;
            }
            if (node.Color == RED)
            {
                return true;
            }
            return false;
        }

        public bool IsBlack(RedBlackTreeNode<T> node)
        {
            if (node == null)
            {
                return false;
            }
            if (node.Color == BLACK)
            {
                return true;
            }
            return false;
        }

        // 左旋转,逆时针旋转
        /*************对红黑树节点x进行左旋操作 ******************/
        /* 
         * 左旋示意图:对节点x进行左旋 
         *     p                       p 
         *    /                       / 
         *   x                       y 
         *  / \                     / \ 
         * lx  y                   x  ry 
         *    / \                 / \ 
         *   ly ry               lx ly 
         * 左旋做了三件事: 
         * 1. 将y的左子节点赋给x的右子节点,并将x赋给y左子节点的父节点(y左子节点非空时) 
         * 2. 将x的父节点p(非空时)赋给y的父节点,同时更新p的子节点为y(左或右) 
         * 3. 将y的左子节点设为x,将x的父节点设为y 
         */
        private void RotateLeft(RedBlackTreeNode<T> x)
        {
            //1. 将y的左子节点赋给x的右子节点,并将x赋给y左子节点的父节点(y左子节点非空时)  
            RedBlackTreeNode<T> y = x.RightChild;
            x.RightChild = y.LeftChild;

            if (y.LeftChild != null)
            {
                y.LeftChild.Parent = x;
            }

            //2. 将x的父节点p(非空时)赋给y的父节点,同时更新p的子节点为y(左或右) 
            if (x.Parent != null)
            {
                y.Parent = x.Parent;
            }

            if (x.Parent == null)
            {
                mRoot = y; //如果x的父节点为空,则将y设为父节点
                y.Parent = null;//设置y的父节点为空  
            }
            else
            {
                //如果x是左子节点  
                if (x == x.Parent.LeftChild)
                {
                    //则也将y设为左子节点  
                    x.Parent.LeftChild = y;
                }
                else
                {
                    //否则将y设为右子节点 
                    x.Parent.RightChild = y;
                }
            }

            //3. 将y的左子节点设为x,将x的父节点设为y  
            y.LeftChild = x;
            x.Parent = y;
        }

        // 右旋转,顺时针旋转
        /*************对红黑树节点y进行右旋操作 ******************/
        /* 
         * 左旋示意图:对节点y进行右旋 
         *        p                   p 
         *       /                   / 
         *      y                   x 
         *     / \                 / \ 
         *    x  ry               lx  y 
         *   / \                     / \ 
         * lx  rx                   rx ry 
         * 右旋做了三件事: 
         * 1. 将x的右子节点赋给y的左子节点,并将y赋给x右子节点的父节点(x右子节点非空时) 
         * 2. 将y的父节点p(非空时)赋给x的父节点,同时更新p的子节点为x(左或右) 
         * 3. 将x的右子节点设为y,将y的父节点设为x 
         */
        private void RotateRight(RedBlackTreeNode<T> y)
        {
            //1.将x的右子节点赋值给y的左子节点,同时将y赋值给x的右子节点的父节点(如果x的右子节点非空)
            RedBlackTreeNode<T> x = y.LeftChild;
            y.LeftChild = x.RightChild;

            if (x.RightChild != null)
            {
                x.RightChild.Parent = y;
            }

            //2.如果y的父节点非空时,将y的父节点赋值给x的父节点,同时更新p的子节点为x
            if (y.Parent != null)
            {
                x.Parent = y.Parent;
            }
            //如果y的父节点为空,则将x设为父节点
            if (y.Parent == null)
            {
                mRoot = x;
                x.Parent = null;//设置x的父节点为空
            }
            else
            {
                //如果y是右子节点
                if (y == y.Parent.RightChild)
                {
                    //则也将y设为右子节点  
                    y.Parent.RightChild = x;
                }
                else
                {
                    //否则将x设为左子节点
                    y.Parent.LeftChild = x;
                }
            }

            //3.将x的右子节点设为y,y的父节点设置为x
            x.RightChild = y;
            y.Parent = x;
        }

        public int Count
        {
            get
            {
                return CountLeafNode(mRoot);
            }
        }

        private int CountLeafNode(RedBlackTreeNode<T> root)
        {
            if (root == null)
            {
                return 0;
            }
            else
            {
                return CountLeafNode(root.LeftChild) + CountLeafNode(root.RightChild) + 1;
            }
        }

        public int Depth
        {
            get
            {
                return GetHeight(mRoot);
            }
        }

        private int GetHeight(RedBlackTreeNode<T> root)
        {
            if (root == null)
            {
                return 0;
            }
            int leftHight = GetHeight(root.LeftChild);
            int rightHight = GetHeight(root.RightChild);
            return leftHight > rightHight ? leftHight + 1 : rightHight + 1;
        }

        public T Max
        {
            get
            {
                RedBlackTreeNode<T> node = mRoot;
                while (node.RightChild != null)
                {
                    node = node.RightChild;
                }
                return node.Data;
            }
        }

        public T Min
        {
            get
            {
                if (mRoot != null)
                {
                    RedBlackTreeNode<T> node = GetMinNode(mRoot);
                    return node.Data;
                }
                else
                {
                    return default(T);
                }
            }
        }

        public void DelMin()
        {
            mRoot = DelMin(mRoot);
        }

        private RedBlackTreeNode<T> DelMin(RedBlackTreeNode<T> node)
        {
            if (node.LeftChild == null)
            {
                return node.RightChild;
            }
            node.LeftChild = DelMin(node.LeftChild);
            return node;
        }

        public void Remove(T value)
        {
            mRoot = Delete(mRoot, value);
        }

        private RedBlackTreeNode<T> Delete(RedBlackTreeNode<T> node, T value)
        {
            if (node == null)
            {
                Console.WriteLine("没有找到要删除的节点: " + value);
                return null;
            }
            int comparer = mComparer.Compare(value, node.Data);
            if (comparer > 0)
            {
                node.RightChild = Delete(node.RightChild, value);
            }
            else if (comparer < 0)
            {
                node.LeftChild = Delete(node.LeftChild, value);
            }
            else
            {
                // a.如果删除节点没有子节点,直接返回null
                // b.如果只有一个子节点,返回其子节点代替删除节点即可
                if (node.LeftChild == null)
                {
                    if (node.RightChild != null)
                    {
                        node.RightChild.Parent = node.Parent;
                    }
                    return node.RightChild;
                }
                else if (node.RightChild == null)
                {
                    if (node.LeftChild != null)
                    {
                        node.LeftChild.Parent = node.Parent;
                    }
                    return node.LeftChild;
                }
                else
                {
                    // c.被删除的节点“左右子节点都不为空”的情况  
                    RedBlackTreeNode<T> child;
                    RedBlackTreeNode<T> parent;
                    bool color;
                    // 1. 先找到“删除节点的右子树中的最小节点”,用它来取代被删除节点的位置
                    // 注意:这里也可以选择“删除节点的左子树中的最大节点”作为被删除节点的替换节点
                    RedBlackTreeNode<T> replace = node;
                    replace = GetMinNode(replace.RightChild);

                    // 2. 更新删除父节点及其子节点
                    // 要删除的节点不是根节点  
                    if (node.Parent != null)
                    {
                        // 要删除的节点是:删除节点的父节点的左子节点 
                        if (node == node.Parent.LeftChild)
                        {
                            // 把“删除节点的右子树中的最小节点”赋值给“删除节点的父节点的左子节点” 
                            node.Parent.LeftChild = replace;
                        }
                        else
                        {
                            // 把“删除节点的右子树中的最小节点”赋值给“删除节点的父节点的右子节点”
                            node.Parent.RightChild = replace;
                        }
                    }
                    else
                    {
                        // 要删除的节点是根节点
                        // 如果只有一个根节点,把mRoot赋值为null,这时replace为null
                        // 如果不止一个节点,返回根节点的右子树中的最小节点
                        mRoot = replace;
                    }

                    // 记录被删除节点的右子树中的最小节点的右子节点,父亲节点及颜色,没有左子节点
                    child = replace.RightChild;
                    parent = replace.Parent;
                    color = replace.Color;

                    // 3. 删除“被删除节点的右子树中的最小节点”,同时更新替换节点的左右子节点,父亲节点及颜色
                    // 替换节点 也就是 最小节点
                    if (parent == node)
                    {
                        // 被删除节点的右子树中的最小节点是被删除节点的子节点
                        parent = replace;
                    }
                    else
                    {
                        //如果最小节点的右子节点不为空,更新其父节点
                        if (child != null)
                        {
                            child.Parent = parent;
                        }
                        //更新最小节点的父节点的左子节点,指向最小节点的右子节点
                        parent.LeftChild = child;
                        //更新替换节点的右子节点
                        replace.RightChild = node.RightChild;
                        //更新删除节点的右子节点的父节点
                        node.RightChild.Parent = replace;
                    }
                    //更新替换节点的左右子节点,父亲节点及颜色
                    replace.Parent = node.Parent;
                    //保持原来位置的颜色
                    replace.Color = node.Color;
                    replace.LeftChild = node.LeftChild;
                    //更新删除节点的左子节点的父节点
                    node.LeftChild.Parent = replace;

                    //红黑树平衡修复
                    //如果删除的最小节点颜色是黑色,需要重新平衡红黑树
                    //如果删除的最小节点颜色是红色,只需要替换删除节点后,涂黑即可
                    //上面的保持原来位置的颜色已经处理了这种情况,这里只需要判断最小节点是黑色的情况
                    if (color == BLACK)
                    {
                        //将最小节点的child和parent传进去
                        RemoveFixUp(child, parent);
                    }
                    return replace;
                }
            }
            return node;
        }

        private void RemoveFixUp(RedBlackTreeNode<T> node, RedBlackTreeNode<T> parent)
        {
            RedBlackTreeNode<T> brother;
            // 被删除节点的右子树中的最小节点 不是 被删除节点的子节点的情况
            while ((node == null || IsBlack(node)) && (node != mRoot))
            {
                if (parent.LeftChild == node)
                {
                    //node是左子节点,下面else与这里的刚好相反  
                    brother = parent.RightChild; //node的兄弟节点  
                    if (IsRed(brother))
                    {
                        //case1: node的兄弟节点brother是红色的 
                        brother.Color = BLACK;
                        parent.Color = RED;
                        RotateLeft(parent);
                        brother = parent.RightChild;
                    }

                    //case2: node的兄弟节点brother是黑色的,且brother的两个子节点也都是黑色的
                    //继续向上遍历  
                    if ((brother.LeftChild == null || IsBlack(brother.LeftChild)) &&
                        (brother.RightChild == null || IsBlack(brother.RightChild)))
                    {
                        //把兄弟节点设置为黑色,平衡红黑树
                        brother.Color = RED;
                        node = parent;
                        parent = node.Parent;
                    }
                    else
                    {
                        //case3: node的兄弟节点brother是黑色的,且brother的左子节点是红色,右子节点是黑色  
                        if (brother.RightChild == null || IsBlack(brother.RightChild))
                        {
                            brother.LeftChild.Color = BLACK;
                            brother.Color = RED;
                            RotateRight(brother);
                            brother = parent.RightChild;
                        }

                        //case4: node的兄弟节点brother是黑色的,且brother的右子节点是红色,左子节点任意颜色  
                        brother.Color = parent.Color;
                        parent.Color = BLACK;
                        brother.RightChild.Color = BLACK;
                        RotateLeft(parent);
                        node = mRoot;
                        break;
                    }
                }
                else
                {
                    //与上面的对称  
                    brother = parent.LeftChild;
                    if (IsRed(brother))
                    {
                        // Case 1: node的兄弟brother是红色的   
                        brother.Color = BLACK;
                        parent.Color = RED;
                        RotateRight(parent);
                        brother = parent.LeftChild;
                    }
                    // Case 2: node的兄弟brother是黑色,且brother的俩个子节点都是黑色的 
                    if ((brother.LeftChild == null || IsBlack(brother.LeftChild)) &&
                        (brother.RightChild == null || IsBlack(brother.RightChild)))
                    {
                        //把兄弟节点设置为黑色,平衡红黑树
                        brother.Color = RED;
                        node = parent;
                        parent = node.Parent;
                    }
                    else
                    {
                        // Case 3: node的兄弟brother是黑色的,并且brother的左子节点是红色,右子节点为黑色。
                        if (brother.LeftChild == null || IsBlack(brother.LeftChild))
                        {
                            brother.RightChild.Color = BLACK;
                            brother.Color = RED;
                            RotateLeft(brother);
                            brother = parent.LeftChild;
                        }

                        // Case 4: node的兄弟brother是黑色的;并且brother的左子节点是红色的,右子节点任意颜色  
                        brother.Color = parent.Color;
                        parent.Color = BLACK;
                        brother.LeftChild.Color = BLACK;
                        RotateRight(parent);
                        node = mRoot;
                        break;
                    }
                }
            }
            //如果删除的最小节点的右子节点是红色,只需要替换最小节点后,涂黑即可
            if (node != null)
            {
                node.Color = BLACK;
            }
        }

        private RedBlackTreeNode<T> GetMinNode(RedBlackTreeNode<T> node)
        {
            while (node.LeftChild != null)
            {
                node = node.LeftChild;
            }
            return node;
        }

        // 中序遍历:首先遍历其左子树,然后访问根结点,最后遍历其右子树。
        // 递归方法实现体内再次调用方法本身的本质是多个方法的简写,递归一定要有出口
        public void ShowTree()
        {
            ShowTree(mRoot);
        }

        private void ShowTree(RedBlackTreeNode<T> node)
        {
            if (node == null)
            {
                return;
            }
            ShowTree(node.LeftChild);
            string nodeColor = node.Color == RED ? "red" : "black";
            string log;
            if (node.Parent != null)
            {
                log = node.Data + " " + nodeColor + " parent= " + node.Parent.Data;
            }
            else
            {
                log = node.Data + " " + nodeColor + " parent= null";
            }
            //打印节点数据
            Console.WriteLine(log);
            ShowTree(node.RightChild);
        }
    }

    public class RedBlackTreeNode<T>
    {
        //数据
        public T Data { get; set; }

        //左子节点
        public RedBlackTreeNode<T> LeftChild { get; set; }

        //右子节点
        public RedBlackTreeNode<T> RightChild { get; set; }

        //父节点
        public RedBlackTreeNode<T> Parent { get; set; }

        //该节点颜色
        public bool Color { get; set; }

        public RedBlackTreeNode(T value, bool color)
        {
            Data = value;
            LeftChild = null;
            RightChild = null;
            Color = color;
        }
    }
}

一个旧金山大学计算机系的一个网站,可以根据你输入的插入、删除和查找的的值,提供非常直观的插入、删除和查找红黑树节点的动画展示。超链接地址

《C#数据结构-红黑树实现》

    原文作者:算法小白
    原文地址: https://blog.csdn.net/qq826364410/article/details/79730911
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞