红黑二叉查找树

    xiaoxiao2025-06-16  5

    //红黑二叉查找树 //红黑二叉查找树是一种平衡二叉树,是基于2-3查找树的基础上演变的 //这里不对2-3查找树的算法进行描述,感兴趣的朋友可以自行了解一下 //2-3查找树的实现原理. // //在2-3查找树算法中,难点就是3-Node类型(含有2个key,3个子节点的节点类型) //的节点数据结构处理,那么在红黑二叉查找树中这种3-Node类型节点演变为如 //下表示方式.非常巧妙的在原普通二叉数结点数据结构中增加了一个颜色的成 //员变量,就使得红黑二叉查找树可以替换为2-3查找树.当一个节点的左子节点 //的颜色为红色时,该节点即为2-3查找树中的3-Node类型节点,此时可以将红 //色的子节点理解为和父节点是平行的,一体的,比如当前示例中将Node1与 //Node2合并成一个节点,该节点相当于2-3查找树中的3-Node类型节点,即含有 //2个key,3个子节点。 // // ** 相当于2-3查找树中的3-Node类型节点: // Node1(Black) // -------------- // / \ // Node2(Red) NULL(Black) // ----------- // / \ // NULL(Black) NULL(Black) // // ** 相当于2-3查找树中的2-Node类型节点: // Node1(Black) // -------------- // / \ // Node2(Black) NULL(Black) // ----------- // / \ // NULL(Black) NULL(Black) // //红黑二叉查找树的定义如下: //1 根节点必须为黑色 //2 红链接均为左链接 //3 没有任何一个结点同时和两条红链接相连 //4 该树是完美黑色平衡的,即任意空链接到根结点的路径上的黑链接数量 // 相同。 //备注: 空节点的颜色为黑色。 private const bool RED = true; private const bool BLACK = false; private Node root; class Node { public Node Left { get; set; } public Node Right { get; set; } public TKey Key { get; set; } public TValue Value { get; set; } public int Number { get; set; } public bool Color { get; set; } public Node(TKey key, TValue value,int number, bool color) { this.Key = key; this.Value = value; this.Number = number; this.Color = color; } } private bool IsRed(Node node) { if (node == null) return false; return node.Color == RED; } //树的左旋: //1 首先把该节点的右子节点进行提升 //2 之后把该节点进行下降(由于右子节点提升,该节点下降,所以此刻该节点的等级 // 仅仅和新位置的右子点的子节点平行,即当前右子节点已经可以做为被旋转节点 // 的父节点了) //3 将该节点的右链接指向原右子节点的左树 //4 将原右子节点的左链接指向该节点 // (3、4步之所以这样操作,主要是为了满足二叉树的特性,每个节点最多有两个子 // 节点,同时左子节点一定小于该节点,右子节点一定大于该节点。) //5 可以看到当前仅被旋转节点和该节点的右子节点做了变化,所以仅对这两个节点 // 的相关信息进行更新。 // //示例:对5节点进行左旋 // // 旋转前 // 5 // / \ // 3 7 // / \ / \ // 2 4 6 8 // // 旋转后 // 7 // / \ // 5 8 // / \ // 3 6 // / \ // 2 4 // //左旋转 private Node RotateLeft(Node h) { Node x = h.Right; //将x的左节点复制给h右节点 h.Right = x.Left; //将h复制给x右节点 x.Left = h; x.Color = h.Color; h.Color = RED; return x; } //树的右旋: //同左旋原理相同,这里仅给出示例 // //示例:对5节点进行右旋 // // 旋转前 // 5 // / \ // 3 7 // / \ / \ // 2 4 6 8 // // 旋转后 // 3 // / \ // 2 5 // / \ // 4 7 // / \ // 6 8 // //右旋转 private Node RotateRight(Node h) { Node x = h.Left; h.Left = x.Right; x.Right = h; x.Color = h.Color; h.Color = RED; return x; } void flipColors(Node h){ h.color = RED; h.left.color = BLACK; h.right.color = BLACK; } //红黑二叉查找树的插入 //这里同2-3查找树算法类似,主要考虑5种情况: //1 插入到2-Node类型节点的左侧 //2 插入到2-Node类型节点的右侧 //3 插入到3-Node类型节点的左侧 //4 插入到3-Node类型节点的中侧 //5 插入到3-Node类型节点的右侧 // // * 插入到2-Node类型节点的左侧 // 如下示例所示,将2插入到4节点的左侧,新建立的节点都是红色,此时 // 满足红黑树的特性,所以后继不需要在做其它处理。 // // 插入前: // 4(B) // / \ // NULL 6(B) // // 插入后: // 4(B) // / \ // 2(R) 6(B) // // * 插入到2-Node类型节点的右侧 // 如下示例所示,将6插入到4节点的右侧,新建立的节点是红色,此时 // 违背了红黑树的特性,不能出现红色的右链接,为了恢复红黑树的特性, // 这里需要再将4节点进行左旋转,可以参见树左旋的介绍,这样红黑 // 树的特性又满足了。 // // 插入前: // 4(B) // / \ // 5(B) NULL // // 插入后: // 4(B) // / \ // 5(B) 6(R) // // * 插入到3-Node类型节点的右侧 // 先介绍这种情况,因为这种情况在3-Node类型节点的几种插入算法中最简单。 // 如下示例所示,将8插入到7节点的右侧,新建立的节点为红色,由于这里是 // 将新节点插入到3-Node类型节点中,所以不能依照2-Node类型算法中的插入 // 右侧进行分析,3-Node类型节点的插入有自己单独的算法,在插入3-Node类 // 型节点右侧的情况下,将7的左、右两个子节点5和8的颜色全部变成黑色,之 // 后7节点自身的颜色变成红色,这时候关键就看7这个节点,如果7节点也是一棵 // 子树,则由于7节点颜色发生了变化,所以需要检测7节点上面的父节点情况, // 一般情况下父节点会有3种情况 // a 如果父节点不存在,则7就是最根节点,则根节点7为了满足红黑树的特性, // 必须变成黑色 // b 如果父节点为2-Node类型节点,则按照上面2-Node的两种方法进行调用父 // 节点也当前子树根节点的关系即可 // c 如果父节点为3-Node类型节点,则同当前3-Node类型节点的方法相同,调 // 整父节点与当前子树根节点的关系,采用这种循环处理父节点的方式, // 直到上升调整到父节点为空或为2-Node类型则停止。 // (此时的算法已经十分类似向2-3查找树算法中,向3-Node类型节点插入相似) // // 插入前: // 7(B) // / \ // 5(R) NULL // / \ // 3(B) 6(B) // // 插入后: // 7(B) // / \ // 5(R) 8(R) // / \ // 3(B) 6(B) // // * 插入到3-Node类型节点的左侧 // 如示例所示,将节点3插入到3-Node类型节点左侧,新建的节点是红色, // 插入后,出现7的左节点为红色,同时左节点的左节点也是红色,此时不 // 能满足红黑树的特性,之后将节点7进行右旋,仔细查看右旋后的情况, // 和之前"插入到3-Node类型节点的右侧"后的情况竟然相同了,此时参数 // 上述方法,将两个子节点变成黑色,当前子树的根节点变成红色即可。 // // 插入前: // 7(B) // / \ // 5(R) 8(B) // / \ // 6(B) // // 插入后: // 7(B) // / \ // 5(R) 8(B) // / \ // 3(R) 6(B) // // 右旋后: // 5(B) // / \ // 3(R) 7(R) // / \ // 6(B) 8(B) // // * 插入到3-Node类型节点的中侧 // 最后一种插入情况了,放最后是因为这种情况最麻烦 // 如示例所示,将6插入到3-Node类型节点的中侧,插入后,新节点为红色, // 不满足红黑树中右链接不能出现红色的特性,此时将5节点进行左旋转,旋转 // 后仔细观察,其中这时候出现左节点、以及左节点的左节点为红色,这个现象 // 同之前的"插入到3-Node类型节点的左侧"的相同了,所以采用那种情况的解决 // 方法,将当前节点7再进行一次右旋转,这时候又出现了左、右子节点都为红 // 色的现象,采用之前的方法,将两个子节点变成黑色,子树的根节点变成红 // 色。 // // 插入前: // 7(B) // / \ // 5(R) 8(B) // / \ // 3(B) // // 插入后: // 7(B) // / \ // 5(R) 8(B) // / \ // 3(B) 6(R) // // 对5节点进行左旋后: // 7(B) // / \ // 6(R) 8(B) // / // 5(R) // / // 3(B) // // 对7节点进行右旋后: // 6(B) // / \ // 5(R) 7(R) // / \ // 3(B) 8(B) // public override void Put(TKey key, TValue value) { root = Put(root, key, value); root.Color = BLACK; } private Node Put(Node h, TKey key, TValue value) { if (h == null) return new Node(key, value, 1, RED); int cmp = key.CompareTo(h.Key); if (cmp < 0) h.Left = Put(h.Left, key, value); else if (cmp > 0) h.Right = Put(h.Right, key, value); else h.Value = value; //平衡化操作 if (IsRed(h.Right) && !IsRed(h.Left)) h = RotateLeft(h); if (IsRed(h.Right) && IsRed(h.Left.Left)) h = RotateRight(h); if (IsRed(h.Left) && IsRed(h.Right)) h = FlipColor(h); h.Number = Size(h.Left) + Size(h.Right) + 1; return h; } private int Size(Node node) { if (node == null) return 0; return node.Number; } //查找 //这里同普通二叉树查找算法相同,不再进行描述 //查找获取指定的值 public override TValue Get(TKey key) { return GetValue(root, key); } private TValue GetValue(Node node, TKey key) { if (node == null) return default(TValue); int cmp = key.CompareTo(node.Key); if (cmp == 0) return node.Value; else if (cmp > 0) return GetValue(node.Right, key); else return GetValue(node.Left, key); }

    如果上面概念不是很清楚,再上几张图片:

    左旋:

    右旋:

    flipColors:

    参考:

    http://www.cnblogs.com/yangecnu/p/Introduce-Red-Black-Tree.html

    http://blog.csdn.net/liujianfeng1984/article/details/47378715

    转载请注明原文地址: https://ju.6miu.com/read-1300014.html
    最新回复(0)