08 树的进阶 平衡树 红黑树 B-树 B+树

目录

平衡树

概念

2-3查找树

概念

插入

2-3树的性质

不做实现

红黑树

概念

红黑树的性质

红黑树结点API

平衡化

概念

左旋

右旋

颜色翻转

插入

概念

往单个2-结点中插入新键

往一个3-结点插入新键

红黑树实现

代码

B-树

定义

应用场景

B+树

B+树的应用

给数据库中的表建立索引


  1. 平衡树

    1. 概念

      1. 如果我们往一个二叉查找树中依次放入元素7 6 5 4 3 2 1, 那么树的深度就有7层, 这个时候就是最坏情况, 查找的效率依旧很低, 所以我们要引入一些平衡树来解决这种问题
  2. 2-3查找树

    1. 概念

      1. 08 树的进阶 平衡树 红黑树 B-树 B+树_第1张图片
      2. 2-结点
        1. 有两个子结点一个键的结点
      3. 3-结点
        1. 有三个子结点两个键的结点
    2. 插入

      1. 如果一个4-结点(里面有三个键)有4个子结点, 那么当它向上提的时候, 需要把剩余的那个3-结点分成两个2-结点
      2. 08 树的进阶 平衡树 红黑树 B-树 B+树_第2张图片
    3. 2-3树的性质

      1. 任意空链接到根节点的路径长度都是相等的
      2. 只有要分解的结点是根结点的时候, 树的高度才会加一
      3. 2-3查找树和普通二叉查找树的最大区别是, 普通二叉查找树是自顶向下增长, 2-3树是自底向上生长
        1. 这也就是2-3树是平衡树的原因 
    4. 不做实现

      1. 因为结点分为2-结点 3-结点, 比较复杂
      2. 但是这种思想很重要
  3. 红黑树

    1. 概念

      1. 基于2-3树的思想实现的
      2. 红黑树通过左旋 右旋 颜色翻转(平衡二叉树)来降低树的高度, 从而提高查询的效率
      3. 底层依旧是用标准的二叉查找树(完全由2-结点构成)实现的, 只不过用一些额外的信息(替换3-结点)来表示2-3树
        1. 红链接: 将两个2-结点连接起来形成3-结点
        2. 黑链接: 普通的连接
      4. 08 树的进阶 平衡树 红黑树 B-树 B+树_第3张图片
    2. 红黑树的性质

      1. 红链接均为左链接
      2. 没有任何一个结点同时连两个红链接
        1. 如果同时连了两个那就是4-结点了
      3. 红黑树是完美黑色平衡的, 即: 任意空链接到根结点的路径上的黑链接的数量相同
        1. 这正好照应了2-3树上的一个性质: 任意空链接到根结点的路径长度都是相同的
    3. 红黑树结点API

      1. 08 树的进阶 平衡树 红黑树 B-树 B+树_第4张图片
      2. color为true代表代表指向自己的链接是红色的
    4. 平衡化

      1. 概念

        1. 对红黑树进行一些增删改的操作之后, 很可能会出现红色的右链接或两条连续的红色链接
      2. 左旋

        1. 概念
          1. 若此结点的左链接为黑色, 右链接为红色, 此时需要左旋
          2. 08 树的进阶 平衡树 红黑树 B-树 B+树_第5张图片
      3. 右旋

        1. 定义
          1. 出现两个连续的红色左链接, 需要右旋
          2. 08 树的进阶 平衡树 红黑树 B-树 B+树_第6张图片
        2. 右旋之后依旧是不符合红黑树的定义的, 因为此时出现了右边的红色链接, 但是我们在右旋的过程中暂时先不解决这个问题, 留到后面使用颜色翻转解决
    5. 颜色翻转

      1. 08 树的进阶 平衡树 红黑树 B-树 B+树_第7张图片
    6. 插入

      1. 概念

        1. 插入的时候期望组成3-结点, 所以插入的新节点统统使用红链接, 如果不符合红黑树的定义在进行转换
      2. 往单个2-结点中插入新键

        1. 插入的是小值
          1. 使用红链接插到左边
        2. 插入的是大值
          1. 使用红链接插到右边, 然后左旋
      3. 往一个3-结点插入新键

        1. 插入的是大值
          1. 需要用到颜色翻转
        2. 插入的是小值
          1. 先用右旋, 再用颜色翻转
        3. 插入的是中间值
          1. 先左旋, 再右旋, 最后颜色翻转
    7. 红黑树实现

      1. 代码

        1. package lq.tree;
          
          public class RedBlackTree, Value> {
              //根节点
              private Node root;
              //记录树中元素的个数
              private int N;
              //红色链接
              private static final boolean RED = true;
              //黑色链接
              private static final boolean BLACK = false;
          
          
              //结点类
              private class Node {
                  //存储键
                  public Key key;
                  //存储值
                  private Value value;
                  //记录左子结点
                  public Node left;
                  //记录右子结点
                  public Node right;
                  //由其父结点指向它的链接的颜色
                  public boolean color;
          
                  public Node(Key key, Value value, Node left, Node right, boolean color) {
                      this.key = key;
                      this.value = value;
                      this.left = left;
                      this.right = right;
                      this.color = color;
                  }
              }
          
          
              //获取树中元素的个数
              public int size() {
                  return N;
              }
          
          
              /**
               * 判断当前节点的父指向链接是否为红色
               *
               * @param x
               * @return
               */
              private boolean isRed(Node x) {
                  if (x == null) {
                      return BLACK;
                  }
                  return x.color == RED;
              }
          
              /**
               * 左旋转
               *
               * @param h
               * @return
               */
              private Node rotateLeft(Node h) {
                  //获取h结点的右子节点, 记为x
                  Node x = h.right;
                  //让h结点的右子节点=x结点的左子节点
                  h.right = x.left;
                  //让x结点的左子节点=h结点
                  x.left = h;
                  h.color = x.color;
                  x.color = RED;
                  return x;
              }
          
              /**
               * 右旋
               *
               * @param h
               * @return
               */
              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;
              }
          
              /**
               * 颜色反转,相当于完成拆分4-节点
               *
               * @param h
               */
              private void flipColors(Node h) {
                  h.color = RED;
                  h.left.color = BLACK;
                  h.right.color = BLACK;
              }
          
              /**
               * 在整个树上完成插入操作
               *
               * @param key
               * @param val
               */
              public void put(Key key, Value val) {
                  root = put(root, key, val);
                  root.color = BLACK;
              }
          
              /**
               * 在指定树中,完成插入操作,并返回添加元素后新的树
               *
               * @param h
               * @param key
               * @param val
               */
              private Node put(Node h, Key key, Value val) {
                  if (h == null) {
                      N++;
                      return new Node(key, val, null, null, RED);
                  }
                  int compare = key.compareTo(h.key);
                  if (compare < 0) {
                      h.left = put(h.left, key, val);
                  } else if (compare > 0) {
                      h.right = put(h.right, key, val);
                  } else {
                      h.value = val;
                  }
                  //先判断是否需要左旋
                  if (isRed(h.right) && !isRed(h.left)) {
                      h = rotateLeft(h);
                  }
                  //再判断是否需要右旋
                  if (isRed(h.left) && isRed(h.left.left)) {
                      h = rotateRight(h);
                  }
                  //最后判断是否需要颜色翻转
                  if (isRed(h.left) && isRed(h.right)) {
                      flipColors(h);
                  }
                  return h;
              }
          
              //根据key,从树中找出对应的值
              public Value get(Key key) {
                  return get(root, key);
              }
          
              //从指定的树x中,查找key对应的值
              public Value get(Node x, Key key) {
                  if (x == null) {
                      return null;
                  }
                  int compare = key.compareTo(x.key);
                  if (compare > 0) {
                      return get(x.right, key);
                  } else if (compare < 0) {
                      return get(x.left, key);
                  } else {
                      return x.value;
                  }
              }
          }
          

           

  4. B-树

    1. 定义

      1. B树就是B-树
      2. 一个节点中, 允许有多可key, 可以有3个 4个 5个 甚至更多, 具体多少不确定
      3. M阶B树
        1. 每个结点最多M-1个key
        2. 最多M个子结点
        3. 最少两个子结点
      4. 实际使用中阶数一般大于100
        1. 存储大量数据后高度依然比较小
    2. 应用场景

      1. 磁盘IO
  5. B+树

    1. B+树的应用

      1. 给数据库中的表建立索引

        1. 数据库的索引底层是使用B+树实现的
        2. 非常适合区间查询

你可能感兴趣的:(数据结构与算法)