何时开始改变?就是现在了

红黑树是一个二叉搜索树,具有如下规则:

  1. 每个节点不是红色就是黑色。
  2. 根节点必须为黑色。
  3. 如果节点为红,其子节点必须为黑,父子节点不得同时为红。
  4. 任一节点至NULL(NULL为黑色)的任何路径,所含黑节点数必须相同。

根据规则4,新增节点必须为红。

根据规则3,新增节点的父节点必须为黑。

因为新增节点必须是红,那么只有在父节点不为黑的时候才需要调整,父节点为黑则无需调整。

着色法则的一个推论(证明过程未了解)是,红黑树的高度最多是2log(N+1)。因此,查找保证是一种对数的操作。

红黑树的平衡性比AVL树要弱,但红黑树通常能够导致良好的平衡状态。经验告诉我们,红黑树的搜索平均效率和AVL树几乎相等(STL源码剖析P210)。

红黑树的调整大体分三种情况,单旋转和双旋转非常类似于AVL树的旋转方法:

1、叔父节点为黑色,新节点在外侧插入。调整方法是:父节点和祖父节点单旋转并改变颜色,如下图。

2、叔父节点为黑色,新节点在内侧插入。调整方法是:先用新节点和父节点执行一次单旋转,如下图。

右图和第一种情况一样了,执行一次单旋转再变色即可。

3、叔父节点为红色,即祖父节点为黑,父节点和叔父节点为红。调整方法是:改变祖父、父、叔父颜色。

如果节点G的父节点为红,那么使用前两种方法进行调整即可。

从上面的插入操作可以看出,不管是内侧插入还是外侧插入,只要叔父节点颜色为黑,那么执行相应的单旋转或双旋转即可。但如果叔父节点为红,则需要改变三个节点的颜色。上图中,G的父节点又有可能为红,那么就要根据G的叔父节点的颜色持续向上改变,这是自底向上的插入方法,STL中的红黑树就用了这种做法(源码剖析P225)。另外一种做法是自顶向下,在沿着路径搜寻插入点的同时,只要发现某个黑节点的两个儿子全为红,则改变颜色,从而避免了持续向上的改变,《数据结构与算法分析》中的例程就用了这种方法(P356)。

下面是代码:

#include <stdio.h>

typedef int ElementType;
typedef struct RedBlackNode *Position;
typedef Position RedBlackTree;
typedef enum ColorType {Red, Black} ColorType;

struct RedBlackNode {
    ElementType Element;
    RedBlackTree Left;
    RedBlackTree Right;
    ColorType Color;
};

Position NullNode = NULL;

// 初始化两个特殊节点:根标记和NULL节点
RedBlackTree Initialize(void)
{
    RedBlackTree T;

    if (NullNode == NULL)
    {
        NullNode = malloc(sizeof (struct RedBlackNode));
        if (NullNode == NULL)
            return -1;
        NullNode->Left = NullNode->Right = NullNode;
        NullNode->Element = 65535;
        NullNode->Color = Black;    // NULL节点为黑色
    }

    T = malloc(sizeof (struct RedBlackNode));
    if (T == NULL)
        return -1;
    T->Left = T->Right = NullNode;
    T->Element = -65535;
    T->Color = Black;

    return T;
}

Position SingleRotate_Left(Position T)
{
    Position NewRoot;

    NewRoot = T->Left;
    T->Left = NewRoot->Right;
    NewRoot->Right = T;

    return NewRoot;
}

Position SingleRotate_Right(Position T)
{
    Position NewRoot;

    NewRoot = T->Right;
    T->Right = NewRoot->Left;
    NewRoot->Left = T;

    return NewRoot;
}

static Position Rotate(Position Parent, ElementType Item)
{
    if (Item < Parent->Element)
    {
        if (Item < Parent->Left->Element)
            return Parent->Left = SingleRotate_Left(Parent->Left);     // 左-左
        else
            return Parent->Left = SingleRotate_Right(Parent->Left);    // 左-右
    }
    else
    {
        if (Item > Parent->Right->Element)
            return Parent->Right = SingleRotate_Right(Parent->Right);   // 右-右
        else
            return Parent->Right = SingleRotate_Left(Parent->Right);    // 右-左
    }
}

static Position x, p, gp, ggp;

static void HandleReorient(RedBlackTree T, ElementType Item)
{
    x->Color = Red;
    x->Left->Color = Black;
    x->Right->Color = Black;

    if (p->Color == Red)
    {
        gp->Color = Red;
        if ((Item < gp->Element) != (Item < p->Element))
            p = Rotate(gp, Item);   // 之字形,双旋转
        x = Rotate(ggp, Item);
        x->Color = Black;
    }

    T->Right->Color = Black;
}

// 每进行一次插入都要从上至下的遍历
RedBlackTree Insert(RedBlackTree T, ElementType Item)
{
    gp = p = x = T;
    NullNode->Element = Item;

    while (x->Element != Item)
    {
        ggp = gp;
        gp = p;
        p = x;
        if (Item < x->Element)
            x = x->Left;
        else
            x = x->Right;
        if (x->Left->Color == Red && x->Right->Color == Red)
            HandleReorient(T, Item);
    }
    if (x != NullNode)
        return T;

    x = malloc(sizeof (struct RedBlackNode));
    if (x == NULL)
        return -1;
    x->Element = Item;
    x->Left = x->Right = NullNode;
    if (Item < p->Element)
        p->Left = x;
    else
        p->Right = x;

    HandleReorient(T, Item);
    return T;
}

static void MidPrint(RedBlackTree T)
{
    if (T != NullNode)
    {
        MidPrint(T->Left);
        printf("%d ", T->Right->Element);
        MidPrint(T->Right);
    }
}

int main()
{
    RedBlackTree tree;

    tree = Initialize();
    tree = Insert(tree, 10);
    tree = Insert(tree, 85);
    tree = Insert(tree, 15);
    tree = Insert(tree, 70);
    tree = Insert(tree, 20);
    tree = Insert(tree, 60);
    tree = Insert(tree, 30);
    tree = Insert(tree, 50);
    tree = Insert(tree, 65);
    tree = Insert(tree, 80);
    tree = Insert(tree, 90);
    tree = Insert(tree, 40);
    tree = Insert(tree, 5);
    tree = Insert(tree, 55);
    MidPrint(tree);
    return 0;
}

参考:

《STL源码剖析》 P208.

《数据结构与算法分析》 P351.

何时开始改变?就是现在了

时间: 2024-10-17 05:10:08

何时开始改变?就是现在了的相关文章

给软工大二学生:用行动開始改变

[来信] 贺老师: 您好! 我学的是软件project专业,如今已经大二下学期了.再过一两个月就升大三了.可是回忆这两年来走过的路,迷迷茫茫.浑浑噩噩,整天玩游戏,差点儿没有怎么认真学习过,各种编程语言都是一知半解,数据结构.操作系统什么的也是接近于零的认识.唯一让我认为不难看的是英语.转眼就大三了,我不想再浑浑噩噩下去了.我想从这个暑假開始做一个彻底的蜕变,打算考研.贺老师能给什么建议吗 [回复] "我想从这个暑假開始做一个彻底的蜕变",这就是一个改变的起点,这个"開始&q

Windows Phone开发(6):处理屏幕方向的改变

俺们都知道,智能手机可以通过旋转手机来改变屏幕的显示方向,更多的时候,对于屏幕方向的改变,我们要做出相应的处理,例如,当手机屏幕方向从纵向变为横向时,可能要重新排列页面上的控件以适应显示区域的变化. 前面我们讨论过,Silverlight for Windows Phone的页面布局有三个常用的布局控件,那么,当屏幕方向改变后,我们所做的对布局的更改基础上是基于这几个容器进行的操作. 本文我将通过三个示例来分别说明. 开始之前,先说一下PhoneApplicationPage类的Orientat

js中,实现css格式的改变

js中,实现属性值的改变 (1)prop属性实现,html中标签的class属性值发生改变: 语法:$(元素标识).prop("class",类属性值); 例子:$("#num_"+currentId).prop("class","no-selected"); 类似的改变class属性: $("#index_" + index).removeClass("current"); $(&qu

Java重要技术(15)内省之属性改变事件和投票否决事件

1.1. 属性改变事件和投票否决事件 PropertyChangeSupport类为Java Bean支持属性改变事件提供了方便. /** * @Title: EventBean.java * @Package com.test.javatechnology.introspection * @Description: * @author http://www.cnblogs.com/coe2coe/ * @date 2017年3月25日 下午6:02:36 * @version V1.0 */

[读码时间] 图片列表:鼠标移入/移出改变图片透明度

说明:代码来自网络.注释为笔者学习时添加. <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>图片列表:鼠标移入/移出改变图片透明度</title> <style> ul,li{ /*去除内外边距,去除列表默认样式*/ margin:0; padding:0; list-style-type:none; } #imgList{ /

改变文件属性与权限

一.改变用户所属组 1.新建一个文件install.log [[email protected] ~]# touch install.log 2.ls-l 查看install.log文件所属组为root [[email protected] ~]# ls -ltotal 4-rw-------. 1 root root 1978 Dec 16 01:26 anaconda-ks.cfg-rw-r--r--. 1 root root 0 Dec 22 02:38 install.log 3.chg

活着就能改变世界

乔帮主说活着就要改变世界,其实活着就能改变世界,摘自老罗的一段话: 每一个生命来到世间,都注定改变世界,这是你的宿命,你别无选择.你要么把世界变得好一点,要么把世界变得坏一点.有些人不服气,说:“妈的我就不信了,我自杀.”你自杀就把这个 世界的自杀率改变了一点点.你如果走进社会,为了生存或是为了什么不要脸的理由,变成了一个 恶心的成年人社会中的一员,那你就把这个世界变得恶心了一点点.如果你一生耿直,刚正不阿, 没做任何恶心的事情,没有做任何对别人造成伤害的事情,一辈子拼了老命勉强把老婆.孩子.老

会飞的汽车真能改变世界吗?

日前,会飞的汽车正成为交通界的大热话题,据国外媒体报道,美国一家名叫"脱离地球"的公司已经推出一款比较靠谱的飞行汽车,这款汽车的外观主体和一般汽车相似,只是车门的部分多了两个可折叠的翅膀.正常情况下,翅膀可以折叠:想要飞行的时候,翅膀就会张开,相信一般读者都没有见过实体车辆,但估计大家都能想象出这款汽车的变身情景,毕竟,这个过程在变形金刚里已经演练了无数次.除此之外,汽车巨头丰田也早早涉猎该领域,并推出相似的概念汽车:在天津某汽车博览会上,中国汽车厂商也曾展出过"会飞的汽车&

Opencv图像识别从零到精通(9)----对比度亮度改变

一张图像来说,会有不同的亮暗程度,很多时候都要增强一下,增强的方法有很多,从大量可以说是线性变换和非线性变换,当然这是说空间域的,频率域的暂时不考虑. 线性变换增强,也是对点的操作,如下图 一.点操作,线性增强 两种常用的点过程(即点算子),是用常数对点进行 乘法 和 加法 运算: 两个参数  和  一般称作 增益 和 偏置 参数.我们往往用这两个参数来分别控制 对比度 和 亮度 . 你可以把  看成源图像像素,把  看成输出图像像素.这样一来,上面的式子就能写得更清楚些: 其中,  和  表示

5.10 String的特点一旦被赋值就不能改变

/*  * 字符串的特点:一旦被赋值,就不能改变.  */ public class StringDemo { public static void main(String[] args) { String s = "hello"; s += "world"; System.out.println("s:" + s); // helloworld } }