查找(平衡查找树)

平衡查找树

定义

父节点的左子树和右子树的高度之差不能大于1

2-3查找树

定义

2-3树运行每个节点保存1个或者两个的值。对于普通的2节点(2-node),他保存1个key和左右两个自己点。对应3节点(3-node),保存两个Key,2-3查找树的定义如下:

  1. 要么为空,要么:
  2. 对于2节点,该节点保存一个key及对应value,以及两个指向左右节点的节点,左节点也是一个2-3节点,所有的值都比key要小,右节点也是一个2-3节点,所有的值比key要大。
  3. 对于3节点,该节点保存两个key及对应value,以及三个指向左中右的节点。左节点也是一个2-3节点,所有的值均比两个key中的最小的key还要小;中间节点也是一个2-3节点,中间节点的key值在两个跟节点key值之间;右节点也是一个2-3节点,节点的所有key值比两个key中的最大的key还要大。

如果中序遍历2-3查找树,就可以得到排好序的序列。在一个完全平衡的2-3查找树中,根节点到每一个为空节点的距离都相同。

变换方法

  1. 向2-结点插入新键,将2-结点变成3-结点
  2. 向3-结点插入新键,将3-结点变成4-结点,然后转换成3个2-结点组成的2-3树,树的高度增加1
  3. 从下往上,依次变换,可以有中间状态,但是最后必须都为2-结点和3-结点
结点变换示意图

红黑树

思想

用标准的二叉查找树和一些额外的信息来表示2-3树。红链接将两个2-结点连接起来构成一个3-结点,黑链接则是2-3树中的普通链接。

定义

红黑树是含有红黑链接并且满足下列条件的二叉查找树:

  1. 红链接均为左链接
  2. 没有任何一个结点同时和两个红链接相连
  3. 该树是完美红黑平衡的,即任意空链接到根节点路径上的黑链接数量相同

相关操作

颜色表示

结点的数据结构:键+值+左右子树+结点总数+颜色
结点的颜色是指指向该节点链接的颜色

旋转

旋转原因:当出现红色右链接或者两条连续的红色链接时
旋转操作:左旋是将两个键中较小者作为根节点变为较大者作为根节点;右旋是将两个键中较大者作为根节点变为较小者作为根节点。旋转后返回一条链接重置父节点中相应的链接。

颜色变化

如果一个结点的两条链接都是红色的,使用flipColors将子结点的颜色由红变黑,同时父结点的颜色由黑变红

根节点的颜色总是黑色

在每次插入后,都需要将根结点的颜色设为黑色。

2-节点插入新键

左边:直接新增红色结点
右边:增加红色结点后,左旋并修正根节点的链接root = rotateLeft(root)

2-结点插入示意图
3-节点插入新键

右边:增加红色结点以后,进行颜色转换
左边:增加红色结点后,右旋,再进行颜色转换
中间:增加红色结点后,先左旋下层链接,在右旋上层链接,在进行颜色转换

3-结点插入示意图
红色链接在树中向上传递

插入点到根节点的路径上的每个结点必须完成以下操作:

  1. 如果右子结点是红色,而左子结点是黑色==> 左旋
  2. 如果左子结点是红色而它的左子节点是红色==> 右旋
  3. 如果左右子结点都是红色==>颜色转换
红色链接传递示例

代码实现

package edu.princeton.cs.algs4.chapter3;

/**
 * 使用红黑树实现的符号表
 * Created by tianxianhu on 2017/3/7.
 */
public class RedBlackBST<Key extends Comparable<Key>, Value> {

    private static final boolean RED = true;
    private static final boolean BLACK = false;

    private Node root;

    private class Node{
        Key key;
        Value val;
        Node left, right;
        int N;
        boolean color; // 其父节点指向它的链接的颜色

        Node (Key key, Value val, int N, boolean color) {
            this.key = key;
            this.val = val;
            this.N = N;
            this.color = color;
        }

    }

    private boolean isRed (Node x) {
        if (x == null)
            return false;
        return RED == x.color;
    }

    public int size() {
        return size(root);
    }

    private int size(Node x) {
        if (x == null)
            return 0;
        return x.N;
    }

    /**
     * 寻找key,找到则更新它的值,否则为它创建一个新节点,同时更新节点数量
     * 创建的节点默认为红色,可能会影响红黑树的平衡,需要进行调整
     * 1.如果左:黑 右:红 ==> 左旋 ==> 2
     * 2.如果左:红 左左:红 ==> 右旋 ==> 3
     * 3.如果左:红 右:红 ==> flip
     * @param key
     * @param val
     */
    public void put(Key key, Value val) {
        if (key == null) throw new IllegalArgumentException("first argument to put() is null");
        if (val == null) {
            delete(key);
            return;
        }
        root = put(root, key, val);
        root.color = BLACK; // 根节点的颜色始终是黑色的
    }

    private Node put(Node x, Key key, Value val) {
        if (x == null)
            return new Node(key, val, 1, RED);
        int cmp = key.compareTo(x.key);
        if (cmp < 0) {
            x.left = put(x.left, key, val);
        } else if (cmp > 0) {
            x.right = put(x.right, key, val);
        } else {
            x.val = val;
        }
        // 调整链接,保证红黑树的平衡
        if (isRed(x.right) && !isRed(x.left))
            x = rotateLeft(x);
        if (isRed(x.left) && isRed(x.left.left))
            x = rotateRight(x);
        if (isRed(x.left) && isRed(x.right))
            flipColors(x);
        x.N = size(x.left) + size(x.right) + 1;
        return x;
    }

    /**
     * 左旋改变h的右子节点
     * 1. 改变链接
     * 2. 改变颜色
     * 3. 更新计数
     * @param h
     * @return
     */
    private Node rotateLeft(Node h) {
        Node x = h.right;
        h.right = x.left;
        x.left = h;
        x.color = h.color;
        h.color = RED;
        x.N = h.N;
        h.N = 1 + size(h.left) + size(h.right);
        return x;
    }

    /**
     * 右旋转h的左链接
     * 1. 改变链接
     * 2. 改变颜色
     * 3. 更新计数
     */
    private Node rotateRight(Node h) {
        Node x = h.left;
        h.left = x.right;
        x.right = h;
        x.color = h.color;
        h.color = RED;
        x.N = h.N;
        h.N = 1 + size(h.left) + size(h.right);
        return x;
    }

    /**
     * 左子节点和右子节点都为红色的时候,将左右子节点变成黑色,根节点变成红色
     * @param x
     */
    private void flipColors(Node x) {
        if (x == null)
            return;
        x.left.color = BLACK;
        x.right.color = BLACK;
        x.color = RED;
    }

    //TODO: 待实现
    public void delete(Key key) {

    }
}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,658评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,482评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,213评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,395评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,487评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,523评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,525评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,300评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,753评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,048评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,223评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,905评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,541评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,168评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,417评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,094评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,088评论 2 352

推荐阅读更多精彩内容