資料結構和演算法學習筆記十六:紅黑樹

movin2333發表於2021-08-03

一.簡介:

  紅黑樹是2-3-4樹(一種B樹)的實現,所以如果想要理解紅黑樹的增刪操作的原理,必須先了解2-3-4樹的增刪操作步驟.將紅黑樹轉化為對應的2-3-4樹,只需要理解黑色節點才是真正的節點,紅色節點是從屬於黑色節點的,如下圖的紅黑樹和對應的2-3-4樹:

  參考資料:一般的做法是將參考資料放在最後的,但是我習慣於將參考資料放在簡介中.

    1)挑戰全B站的紅黑樹視訊,不服來戰!終結B站沒人能講清楚紅黑樹的歷史!_嗶哩嗶哩_bilibili.講解java中treemap資料結構(底層是紅黑樹)的實現原理,在講解的過程中對照2-3-4樹詳細梳理了紅黑樹的規則的由來和增刪實現,講解的很細緻但是有一定基礎的同志會覺得他很囉嗦,適合完全沒有基礎的人觀看.個人覺得後半部分紅黑樹的刪除講得有些亂.

    2)紅黑樹第一講_嗶哩嗶哩_bilibili;  紅黑樹第二講_嗶哩嗶哩_bilibili.  使用C++手撕紅黑樹,在評論區作者將視訊和其中用到的文件課件\原始碼等放到了度盤中並提供了下載地址,這裡也一併貼上:

      地址:https://pan.baidu.com/s/1dCUtkYHRjxzdGGogKv8vaQ
      密碼:pg9c

    這個作者將紅黑樹的增刪過程中遇到的情況分類得很詳細,並且將增加節點時雙紅的解決方案和刪除節點時失黑的解決方案梳理得很具體,強烈推薦.本文中部分圖片(如上面的2-3-4樹和紅黑樹對應圖)也來自於這個作者的文件課件.

二.紅黑樹的性質

  1.紅黑樹的性質說明:紅黑樹既然是2-3-4樹的實現,那麼紅黑樹的性質就一定是為了使這棵樹對應的2-3-4樹合乎2-3-4樹的規則.因此,要理解紅黑樹的性質,有必要先理解2-3-4樹.2-3-4樹是B樹的一種,關於B樹需要滿足的條件及其增刪操作的步驟可以參考:資料結構和演算法學習筆記十五:多路查詢樹(B樹) - movin2333 - 部落格園 (cnblogs.com).在這篇部落格中以最簡單的2-3樹為例講解了B樹的性質和增刪操作,2-3-4樹的相應增刪操作類推即可,如有必要讀者也可自行查詢資料瞭解2-3-4樹.

  2.紅黑樹的性質:

    1)紅黑樹是一顆平衡二叉樹,其中序遍歷結果單調不減.

    2)節點是紅色或者黑色.

    3)根節點是黑色.

    4)每個葉節點是黑色的.這裡的葉節點是指如果某個節點的左右子節點指標中有空值時用來替換這個空值的Null節點.

    5)每個紅色節點的兩個子節點都是黑色節點.換言之,不存在父子節點顏色都是紅色的情況.

    6)從根節點到每個葉子節點的每一條路徑中黑色節點的數量都是相同的.黑色節點的數量叫黑高度.

  3.紅黑樹的性質和2-3-4樹的一些關聯:

    如上圖所示,首先2-3-4樹本身就是一顆平衡的二叉樹,其中序遍歷的結果單調不減,因此對應的紅黑樹也具有相同的性質.其次,在紅黑樹對應的2-3-4樹中,我們發現2-3-4樹中的每一種節點都和紅黑樹中的節點之間有對應關係,這裡不詳細列舉.性質5說不存在兩個連續紅色節點,也是因為紅黑樹中兩個連續的紅色節點的情況在2-3-4樹中找不到節點或節點之間的關聯情況與之對映.性質6中的黑高度應該很好理解,在2-3-4樹中所有葉子節點都在同一層,2-3-4樹中每個節點對應到紅黑樹中都是1個黑色節點和0-2個紅色節點.

三.向紅黑樹中增加元素:

  如果向紅黑樹中增加元素,首先將紅黑樹當做一顆普通二叉樹,使用迴圈遍歷的方法找到要增加節點的位置,然後新增節點.新增的節點預設都是紅色的,在新增完成節點後,需要注意有可能遇到連續紅色節點的情況,需要進行雙紅修正.對應到2-3-4樹中,新增節點時如果新增到2節點可以直接新增,2節點變為3節點;新增到3節點上,也是直接新增,3節點變為4節點,但是4節點的黑色元素一定是3個元素中中間的那個,所以可能需要修正節點顏色;新增到4節點上時無法直接新增,需要先將原4節點的3個元素中中間的那個擠到父節點中再將剩下兩個元素拆成兩個2節點,之後再新增節點,而一個元素被擠到父節點中時父節點也可能需要修正.不論2-3-4樹中的情況如何,在紅黑樹中,新增節點時只要遇到了雙紅現象就需要修正.雙紅現象又分為以下幾種:

  1.新增節點時父節點為黑色,沒有雙紅現象發生;

  2.叔叔節點是黑色節點或Null節點,這種情況對應到2-3-4樹中就是插入到3節點中,只需要進行1-2次旋轉和相應的變色即可;

  3.叔叔節點是紅色,這種情況對應到2-3-4樹中就是插入到4節點中,原來的4節點會分裂為兩個2節點再新增元素,這時需要遞迴.

四.移除紅黑樹中的元素:

  紅黑樹的刪除操作相對是比增加操作麻煩的,我們同樣先簡單梳理2-3-4樹中的元素刪除:

  在2-3-4樹中,如果被移除的元素是葉子節點上的元素,且這個葉子節點是3節點或4節點,那麼直接移除就好,但是如果這個葉子節點是2節點,則需要找這個節點的後繼節點或前驅節點代替這個節點,然後刪除前驅節點或後繼節點.如果被移除的元素不在葉子節點中,那麼同樣找到它的前驅節點或後繼節點替換它,移除前驅或後繼,移除時可以繼續找其前驅或後繼替換,知道被移除的節點在葉子節點中.

  在紅黑樹中,同樣首先找到要移除元素的位置,找到後如果這個節點是一個葉子節點(這裡葉子節點不是指Null節點,而是指當前節點的左右子節點指標都為空),直接刪除,如果是黑色的節點或不是葉子節點,則找這個節點的前驅節點或後繼節點進行替換,替換後繼續判斷這個節點是否是葉子節點,是就可以刪除了.如果葉子節點是紅色,直接刪除(對應移除2-3-4樹中的3節點或4節點),但是葉子節點是黑色的話,就不能直接刪除(對應移除2-3-4樹中的2節點),刪除後會破壞整棵樹的平衡,這種情況下需要對移除節點後的紅黑樹進行重新平衡,這個平衡過程就叫失黑修正.失黑修正的步驟可以分以下幾種情況:

  1.沒有紅色侄子,父節點為紅色.這種情況的失黑修正只需將父節點變為黑色,兄弟節點變為紅色即可.對應2-3-4樹中的情況就是在移除2節點時父節點為3節點或4節點,兄弟節點也是2節點,將父節點變為2節點或3節點,多出來的元素移動到兄弟節點中去,這樣這個2節點就可以直接移除.

  2.有紅色侄子,父節點為紅色.這種情況的失黑修正只需要在移除後進行一次或兩次旋轉操作,然後進行染色,染色的結果是父節點位置的新節點為紅色,其子節點都為黑色.對應到2-3-4樹中,這個操作就是移除2節點時,兄弟節點是3節點或4節點,可以借用父節點中的元素來填補2節點移除後的空缺位置,再從兄弟節點中借用1-2個元素填補父節點被移除後的空缺位置.

  3.沒有紅色侄子,父節點為黑色,兄弟節點為黑色.這種情況的失黑修正無法從兄弟節點找元素補齊,因此會降低紅黑樹的層數.具體做法是將兄弟節點變為紅色,然後向上遞迴進行失黑修正.

  4.父節點為黑色,兄弟節點為紅色.這種情況下因為紅黑樹的不同分支黑高度相同,因此紅色的兄弟節點一定有兩個黑色的子節點,這時進行一次旋轉,就可以將這種情況轉化為其他情況.

五.紅黑樹的增刪實現程式碼(C#):

/************************************
* 建立人:movin
* 建立時間:2021/7/28 21:09:48
* 版權所有:個人
***********************************/
using System;
using System.Collections.Generic;
using System.Text;

namespace SearchCore
{
    /// <summary>
    /// 紅黑樹節點類
    /// </summary>
    public class RedBlackNode
    {
        /// <summary>
        /// 儲存的資料
        /// </summary>
        public int Content { get; set; }
        /// <summary>
        /// 左子節點
        /// </summary>
        public RedBlackNode LeftChild { get; private set; }
        /// <summary>
        /// 右子節點
        /// </summary>
        public RedBlackNode RightChild { get; private set; }
        /// <summary>
        /// 顏色
        /// </summary>
        public Color Color { get; private set; }
        /// <summary>
        /// 父節點
        /// </summary>
        public RedBlackNode Parent { get; private set; }
        public RedBlackNode(int content,RedBlackNode parent)
        {
            Content = content;
            Parent = parent;
            //初始節點都是紅色
            Color = Color.RED;
        }
        /// <summary>
        /// 新增左子節點
        /// </summary>
        /// <param name="leftChild"></param>
        public void AddLeftChild(RedBlackNode leftChild)
        {
            LeftChild = leftChild;
            if(leftChild != null)
            {
                leftChild.Parent = this;
            }
        }
        /// <summary>
        /// 新增右子節點
        /// </summary>
        /// <param name="rightChild"></param>
        public void AddRightChild(RedBlackNode rightChild)
        {
            RightChild = rightChild;
            if(rightChild != null)
            {
                rightChild.Parent = this;
            }
        }
        /// <summary>
        /// 設定顏色
        /// </summary>
        /// <param name="color"></param>
        public void SetColor(Color color)
        {
            Color = color;
        }
    }
    /// <summary>
    /// 紅黑樹的節點顏色
    /// </summary>
    public enum Color
    {
        RED,
        BLACK,
    }
}
/************************************
* 建立人:movin
* 建立時間:2021/7/28 21:10:19
* 版權所有:個人
***********************************/
using System;
using System.Collections.Generic;
using System.Text;

namespace SearchCore
{
    public class RedBlackTree
    {
        /// <summary>
        /// 根節點
        /// </summary>
        public RedBlackNode Root { get; private set; }
        /// <summary>
        /// 節點數量
        /// </summary>
        public int Count { get; private set; }

        public RedBlackTree()
        {
            Count = 0;
        }
        #region add and remove node
        public void Remove(int content)
        {
            if(Root == null)
            {
                return;
            }
            RedBlackNode removeNode = RealRemove(content, Root);
            if(removeNode != null)
            {
                //實際的節點移除操作
                if (removeNode.Parent == null)
                {
                    Root = null;
                }
                else
                {
                    if (removeNode.Parent.LeftChild == removeNode)
                    {
                        removeNode.Parent.AddLeftChild(null);
                    }
                    else
                    {
                        removeNode.Parent.AddRightChild(null);
                    }
                }
                //節點數量減少
                Count--;
            }
        }
        /// <summary>
        /// 真正移除節點的方法,遞迴
        /// </summary>
        /// <param name="content"></param>
        /// <param name="node"></param>
        private RedBlackNode RealRemove(int content,RedBlackNode node)
        {
            if(content < node.Content)
            {
                if(node.LeftChild != null)
                {
                    return RealRemove(content, node.LeftChild);
                }
            }
            else if(content > node.Content)
            {
                if(node.RightChild != null)
                {
                    return RealRemove(content, node.RightChild);
                }
            }
            //要刪除的值和當前節點的值相等時,當前節點就是要刪除的節點
            else
            {
                //找到真正要刪除的節點(找當前節點的前驅節點或後繼節點,並在找到後就交換資料,並遞迴或迴圈繼續找,直到找到某個葉子節點身上)
                var realDeleteNode = GetRealDeleteNode(node);
                //找到的葉子節點是黑色節點,需要進行失黑修正,再移除;是紅色節點,不用失黑修正,直接移除
                if(realDeleteNode.Color == Color.BLACK)
                {
                    LoseBlackAdjust(realDeleteNode);
                }
                //返回被移除的節點
                return realDeleteNode;
            }
            return null;
        }
        /// <summary>
        /// 新增節點
        /// </summary>
        /// <param name="content"></param>
        public void Add(int content)
        {
            RedBlackNode newNode = null;
            if (Root == null)
            {
                newNode = new RedBlackNode(content, null);
                Root = newNode;
            }
            else
            {
                newNode = RealAddNode(content, Root);
            }
            //如果成功插入了節點,需要作雙紅修正
            if(newNode != null)
            {
                RedRedAdjust(newNode);
                Count++;
            }
            
        }
        /// <summary>
        /// 真正新增節點的方法,會遞迴
        /// </summary>
        /// <param name="content"></param>
        /// <param name="node"></param>
        private RedBlackNode RealAddNode(int content, RedBlackNode node)
        {
            if (content < node.Content)
            {
                if (node.LeftChild == null)
                {
                    node.AddLeftChild(new RedBlackNode(content, node));
                    return node.LeftChild;
                }
                return RealAddNode(content, node.LeftChild);
            }
            else if (content > node.Content)
            {
                if (node.RightChild == null)
                {
                    node.AddRightChild(new RedBlackNode(content, node));
                    return node.RightChild;
                }
                return RealAddNode(content, node.RightChild);
            }
            else
            {
                return null;
            }
        }
        #endregion
        #region lose black adjust
        #region lose black adjust
        /// <summary>
        /// 失黑修正
        /// </summary>
        /// <param name="deleteNode"></param>
        private void LoseBlackAdjust(RedBlackNode deleteNode)
        {
            if(deleteNode.Parent != null)
            {
                if(deleteNode.Parent.RightChild == deleteNode)
                {
                    LoseBlackAdjustRight(deleteNode.Parent);
                }
                else
                {
                    LoseBlackAdjustLeft(deleteNode.Parent);
                }
            }
            else
            {
                //沒有父節點的節點是根節點,根節點必須是黑色節點
                deleteNode.SetColor(Color.BLACK);
            }
        }
        /// <summary>
        /// 失黑修正-移除左子黑色節點
        /// </summary>
        /// <param name="root"></param>
        private void LoseBlackAdjustLeft(RedBlackNode root)
        {
            if(root.Color == Color.BLACK)
            {
                if(root.RightChild.Color == Color.RED)
                {
                    LoseBlackAdjust_BlackParent_RedBrother_Left(root);
                }
                else
                {
                    //兄弟節點為黑色,如果有侄子,侄子節點一定是紅色
                    if(root.RightChild.LeftChild != null || root.RightChild.RightChild != null)
                    {
                        LoseBlackAdjust_WithRedNephew_Left(root);
                    }
                    else
                    {
                        LoseBlackAdjust_BlackParent_BlackBrother_WithoutRedNephew_Left(root);
                    }
                }
            }
            else
            {
                if(root.RightChild.LeftChild != null || root.RightChild.RightChild != null)
                {
                    LoseBlackAdjust_WithRedNephew_Left(root);
                }
                else
                {
                    LoseBlackAdjust_RedParent_BlackBrother_WithoutRedNephew_Left(root);
                }
            }
        }
        /// <summary>
        /// 失黑修正-移除右子黑色節點
        /// </summary>
        /// <param name="root"></param>
        private void LoseBlackAdjustRight(RedBlackNode root)
        {
            if (root.Color == Color.BLACK)
            {
                if (root.LeftChild.Color == Color.RED)
                {
                    LoseBlackAdjust_BlackParent_RedBrother_Right(root);
                }
                else
                {
                    //兄弟節點為黑色,如果有侄子,侄子節點一定是紅色
                    if (root.LeftChild.LeftChild != null || root.LeftChild.RightChild != null)
                    {
                        LoseBlackAdjust_WithRedNephew_Right(root);
                    }
                    else
                    {
                        LoseBlackAdjust_BlackParent_BlackBrother_WithoutRedNephew_Right(root);
                    }
                }
            }
            else
            {
                if (root.LeftChild.LeftChild != null || root.LeftChild.RightChild != null)
                {
                    LoseBlackAdjust_WithRedNephew_Right(root);
                }
                else
                {
                    LoseBlackAdjust_RedParent_BlackBrother_WithoutRedNephew_Right(root);
                }
            }
        }
        #endregion
        #region with red nephew
        /// <summary>
        /// 失黑修正-有紅色侄子-移除的黑色節點在根節點左邊
        /// </summary>
        /// <param name="root"></param>
        private void LoseBlackAdjust_WithRedNephew_Left(RedBlackNode root)
        {
            //如果沒有右子節點的右子節點,要先觸發一次右旋
            if(root.RightChild.RightChild == null)
            {
                RightRotate(root.RightChild);
                root.RightChild.SetColor(Color.BLACK);
                root.RightChild.RightChild.SetColor(Color.RED);
            }
            //觸發一次左旋
            LeftRotate(root);
            //旋轉後變色(注意:旋轉後root已經不在根節點的位置上了)
            Color parentColor = root.Color;//儲存原來的根節點顏色
            root.SetColor(Color.BLACK);
            root.Parent.SetColor(parentColor);//根節點顏色不變
            root.Parent.RightChild.SetColor(Color.BLACK);
        }
        /// <summary>
        /// 失黑修正-有紅色侄子-移除的黑色節點在根節點右邊
        /// </summary>
        /// <param name="root"></param>
        private void LoseBlackAdjust_WithRedNephew_Right(RedBlackNode root)
        {
            //如果沒有左子節點的左子節點,要先觸發一次右旋
            if (root.LeftChild.LeftChild == null)
            {
                LeftRotate(root.LeftChild);
                root.LeftChild.SetColor(Color.BLACK);
                root.LeftChild.LeftChild.SetColor(Color.RED);
            }
            //觸發一次右旋
            RightRotate(root);
            //旋轉後變色(注意:旋轉後root已經不在根節點的位置上了)
            Color parentColor = root.Color;//儲存原來的根節點顏色
            root.SetColor(Color.BLACK);
            root.Parent.SetColor(parentColor);//根節點顏色不變
            root.Parent.LeftChild.SetColor(Color.BLACK);
        }
        #endregion
        #region without red nephew
        /// <summary>
        /// 失黑修正-黑色父節點-黑色兄弟節點-沒有紅色侄子-移除的黑色節點在根節點左邊
        /// </summary>
        /// <param name="root"></param>
        private void LoseBlackAdjust_BlackParent_BlackBrother_WithoutRedNephew_Left(RedBlackNode root)
        {
            root.RightChild.SetColor(Color.RED);
            LoseBlackAdjust(root);
        }
        /// <summary>
        /// 失黑修正-黑色父節點-黑色兄弟節點-沒有紅色侄子-移除的黑色節點在根節點右邊
        /// </summary>
        /// <param name="root"></param>
        private void LoseBlackAdjust_BlackParent_BlackBrother_WithoutRedNephew_Right(RedBlackNode root)
        {
            root.LeftChild.SetColor(Color.RED);
            LoseBlackAdjust(root);
        }
        /// <summary>
        /// 失黑修正-紅色父節點-黑色兄弟節點-沒有紅色侄子-移除的黑色節點在根節點左邊
        /// </summary>
        /// <param name="root"></param>
        private void LoseBlackAdjust_RedParent_BlackBrother_WithoutRedNephew_Left(RedBlackNode root)
        {
            root.SetColor(Color.BLACK);
            root.RightChild.SetColor(Color.RED);
        }
        /// <summary>
        /// 失黑修正-紅色父節點-黑色兄弟節點-沒有紅色侄子-移除的黑色節點在根節點右邊
        /// </summary>
        /// <param name="root"></param>
        private void LoseBlackAdjust_RedParent_BlackBrother_WithoutRedNephew_Right(RedBlackNode root)
        {
            root.SetColor(Color.BLACK);
            root.LeftChild.SetColor(Color.RED);
        }
        /// <summary>
        /// 失黑修正-黑色父節點-紅色兄弟節點-移除的黑色節點在根節點左邊
        /// </summary>
        /// <param name="root"></param>
        private void LoseBlackAdjust_BlackParent_RedBrother_Left(RedBlackNode root)
        {
            if(root.RightChild.RightChild == null)
            {
                RightRotate(root.RightChild);
                root.RightChild.RightChild.SetColor(Color.BLACK);
            }
            else
            {
                root.RightChild.SetColor(Color.BLACK);
            }
            LeftRotate(root);
            root.SetColor(Color.RED);
            //旋轉後還要繼續修正
            LoseBlackAdjustLeft(root);
        }
        /// <summary>
        /// 失黑修正-黑色父節點-紅色兄弟節點-移除的黑色節點在根節點右邊
        /// </summary>
        /// <param name="root"></param>
        private void LoseBlackAdjust_BlackParent_RedBrother_Right(RedBlackNode root)
        {
            if (root.LeftChild.LeftChild == null)
            {
                LeftRotate(root.LeftChild);
                root.LeftChild.LeftChild.SetColor(Color.BLACK);
            }
            else
            {
                root.LeftChild.SetColor(Color.BLACK);
            }
            RightRotate(root);
            root.SetColor(Color.RED);
            //旋轉後還要繼續修正
            LoseBlackAdjustLeft(root);
        }
        #endregion
        #endregion
        #region red red adjust
        #region red red adjust
        /// <summary>
        /// 雙紅在根節點左邊時判斷進行哪種修正,並進行雙紅修正
        /// </summary>
        /// <param name="root"></param>
        private void RedRedAdjust_Left(RedBlackNode root)
        {
            //校驗不是雙紅直接返回
            if (root.LeftChild.Color == Color.BLACK)
            {
                return;
            }
            //右節點為空或者右子節點顏色為黑,對應2-3-4樹中的3節點
            if (root.RightChild == null || root.RightChild.Color == Color.BLACK)
            {
                if (root.LeftChild.LeftChild != null && root.LeftChild.LeftChild.Color == Color.RED)
                {
                    RedRedAdjust_UncleBlack_LeftLeft(root);
                }
                else
                {
                    RedRedAdjust_UncleBlack_LeftRight(root);
                }
            }
            //右子節點顏色為紅色,對應2-3-4樹中的4節點
            else
            {
                RedRedAdjust_UncleRed_Left(root);
            }
        }
        /// <summary>
        /// 雙紅在根節點右邊時判斷進行哪種修正,並進行雙紅修正
        /// </summary>
        /// <param name="root"></param>
        private void RedRedAdjust_Right(RedBlackNode root)
        {
            //校驗不是雙紅直接返回
            if (root.RightChild.Color == Color.BLACK)
            {
                return;
            }
            //右節點為空或者右子節點顏色為黑,對應2-3-4樹中的3節點
            if (root.LeftChild == null || root.LeftChild.Color == Color.BLACK)
            {
                if (root.RightChild.RightChild != null && root.RightChild.RightChild.Color == Color.RED)
                {
                    RedRedAdjust_UncleBlack_RightRight(root);
                }
                else
                {
                    RedRedAdjust_UncleBlack_RightLeft(root);
                }
            }
            //右子節點顏色為紅色,對應2-3-4樹中的4節點
            else
            {
                RedRedAdjust_UncleRed_Right(root);
            }
        }
        /// <summary>
        /// 新插入節點時進行雙紅修正
        /// </summary>
        /// <param name="newNode"></param>
        private void RedRedAdjust(RedBlackNode newNode)
        {
            //校驗父節點和爺爺節點是否存在,父節點不存在,則這是第一個根節點,爺爺節點不存在則這是第2個或可能是第3個節點
            if (newNode.Parent == null)
            {
                //根節點必須為黑色
                newNode.SetColor(Color.BLACK);
                return;
            }
            if (newNode.Parent.Parent == null)
            {
                return;
            }
            if (newNode.Parent == newNode.Parent.Parent.LeftChild)
            {
                RedRedAdjust_Left(newNode.Parent.Parent);
            }
            else
            {
                RedRedAdjust_Right(newNode.Parent.Parent);
            }
        }
        #endregion
        #region black uncle
        /// <summary>
        /// 叔叔節點為黑色時的雙紅修正(新插入節點在父節點左側,在根節點左側)
        /// </summary>
        /// <param name="grandpa">雙紅調整前的根節點</param>
        private void RedRedAdjust_UncleBlack_LeftLeft(RedBlackNode grandpa)
        {
            RightRotate(grandpa);
            //調整顏色(注意:右旋後grandpa已經不是根節點了)
            grandpa.SetColor(Color.RED);
            grandpa.Parent.SetColor(Color.BLACK);
        }
        /// <summary>
        /// 叔叔節點為黑色時的雙紅修正(新插入節點在父節點右側,在根節點左側)
        /// </summary>
        /// <param name="grandpa">雙紅調整前的根節點</param>
        private void RedRedAdjust_UncleBlack_LeftRight(RedBlackNode grandpa)
        {
            //先左旋轉化為左左的情況
            LeftRotate(grandpa.LeftChild);
            //呼叫左左情況的修正函式
            RedRedAdjust_UncleBlack_LeftLeft(grandpa);
        }
        /// <summary>
        /// 叔叔節點為黑色時的雙紅修正(新插入節點在父節點右側,在根節點右側)
        /// </summary>
        /// <param name="grandpa">雙紅調整前的根節點</param>
        private void RedRedAdjust_UncleBlack_RightRight(RedBlackNode grandpa)
        {
            LeftRotate(grandpa);
            //調整顏色
            grandpa.SetColor(Color.RED);
            grandpa.Parent.SetColor(Color.BLACK);
        }
        /// <summary>
        /// 叔叔節點為黑色時的雙紅修正(新插入節點在父節點左側,在根節點右側)
        /// </summary>
        /// <param name="grandpa">雙紅調整前的根節點</param>
        private void RedRedAdjust_UncleBlack_RightLeft(RedBlackNode grandpa)
        {
            //先右旋轉化為右右的情況
            RightRotate(grandpa.RightChild);
            //呼叫右右的修正函式
            RedRedAdjust_UncleBlack_RightRight(grandpa);

        }
        #endregion
        #region red uncle
        /// <summary>
        /// 叔叔節點為黑色時的雙紅修正(新插入節點在根節點的左子樹)
        /// </summary>
        /// <param name="grandpa">雙紅調整前的根節點</param>
        private void RedRedAdjust_UncleRed_Left(RedBlackNode grandpa)
        {
            //直接變色(實際上對應到2-3-4樹中是元素的上移和節點的分裂)
            grandpa.SetColor(Color.RED);
            grandpa.LeftChild.SetColor(Color.BLACK);
            //檢驗是否是根節點,如果是根節點需要增加樹的深度,並終止遞迴
            if (grandpa.Parent == null)
            {
                //將根節點的顏色改為黑色,紅色的叔叔節點改為黑色,樹就增長了
                grandpa.SetColor(Color.BLACK);
                grandpa.RightChild.SetColor(Color.BLACK);
                //無論如何,遞迴到根節點後都不需要再遞迴
                return;
            }
            //向上兩層節點不存在也就不用遞迴了(說明父節點是根節點,一定是黑色)
            if (grandpa.Parent.Parent == null)
            {
                grandpa.RightChild.SetColor(Color.BLACK);
                return;
            }
            //繼續向上遞迴(向上兩層)
            if (grandpa.Parent.Parent.RightChild == grandpa.Parent)
            {
                RedRedAdjust_Right(grandpa.Parent.Parent);
            }
            else
            {
                RedRedAdjust_Left(grandpa.Parent.Parent);
            }
            grandpa.RightChild.SetColor(Color.BLACK);
        }
        /// <summary>
        /// 叔叔節點為黑色時的雙紅修正(新插入節點在根節點的右子樹)
        /// </summary>
        /// <param name="grandpa">雙紅調整前的根節點</param>
        private void RedRedAdjust_UncleRed_Right(RedBlackNode grandpa)
        {
            //直接變色(實際上對應到2-3-4樹中是元素的上移和節點的分裂)
            grandpa.SetColor(Color.RED);
            grandpa.RightChild.SetColor(Color.BLACK);
            //檢驗是否是根節點,如果是根節點需要增加樹的深度,並終止遞迴
            if (grandpa.Parent == null)
            {
                //將根節點的顏色改為黑色,紅色的叔叔節點改為黑色,樹就增長了
                grandpa.SetColor(Color.BLACK);
                grandpa.LeftChild.SetColor(Color.BLACK);
                //無論如何,遞迴到根節點後都不需要再遞迴
                return;
            }
            //向上兩層節點不存在也就不用遞迴了(說明父節點是根節點,一定是黑色)
            if (grandpa.Parent.Parent == null)
            {
                grandpa.LeftChild.SetColor(Color.BLACK);
                return;
            }
            //繼續向上遞迴(向上兩層)
            if (grandpa.Parent.Parent.RightChild == grandpa.Parent)
            {
                RedRedAdjust_Right(grandpa.Parent.Parent);
            }
            else
            {
                RedRedAdjust_Left(grandpa.Parent.Parent);
            }
            grandpa.LeftChild.SetColor(Color.BLACK);
        }
        #endregion
        #endregion
        #region leftRotate and rightRotate
        /// <summary>
        /// 左旋操作,只做節點的位置變化,不作顏色變化
        /// </summary>
        /// <param name="root">左旋前的根節點</param>
        private void LeftRotate(RedBlackNode root)
        {
            if (root.RightChild == null)
            {
                return;
            }
            var temp = root.RightChild;
            root.AddRightChild(temp.LeftChild);
            if (root.Parent != null)
            {
                if (root.Parent.RightChild == root)
                {
                    root.Parent.AddRightChild(temp);
                }
                else
                {
                    root.Parent.AddLeftChild(temp);
                }
            }
            else
            {
                Root = temp;
                temp.SetParent(null);
            }
            temp.AddLeftChild(root);
        }
        /// <summary>
        /// 右旋操作,只做節點的位置變化,不作顏色變化
        /// </summary>
        /// <param name="root">右旋前的根節點</param>
        private void RightRotate(RedBlackNode root)
        {
            if (root.LeftChild == null)
            {
                return;
            }
            var temp = root.LeftChild;
            root.AddLeftChild(temp.RightChild);
            if (root.Parent != null)
            {
                if (root.Parent.RightChild == root)
                {
                    root.Parent.AddRightChild(temp);
                }
                else
                {
                    root.Parent.AddLeftChild(temp);
                }
            }
            else
            {
                Root = temp;
                temp.SetParent(null);
            }
            temp.AddRightChild(root);
        }
        #endregion
        #region find real delete node
        /// <summary>
        /// 找到真正該刪除的節點
        /// </summary>
        /// <param name="currentNode"></param>
        /// <returns></returns>
        private RedBlackNode GetRealDeleteNode(RedBlackNode currentNode)
        {
            RedBlackNode result = currentNode;
            while (result.LeftChild != null || result.RightChild != null)
            {
                RedBlackNode temp;
                if(result.LeftChild != null)
                {
                    //找到左鄰居
                    temp = result.RightChild;
                    while (temp.LeftChild != null)
                    {
                        temp = temp.LeftChild;
                    }
                }
                else
                {
                    //找到右鄰居
                    temp = result.LeftChild;
                    while (temp.RightChild != null)
                    {
                        temp = temp.RightChild;
                    }
                }
                //交換節點
                result.Content = result.Content ^ temp.Content;
                temp.Content = result.Content ^ temp.Content;
                result.Content = result.Content ^ temp.Content;
                //賦值
                result = temp;
            }
            //result沒有子節點時(是葉子節點)返回
            return result;
        }
        #endregion
    }
}

 

相關文章