从二叉搜索树到AVL树再到红黑树 B树
这几种树都属于数据结构中较为复杂的,在平时面试中,经常会问理解用法,但一般不会问具体的实现,所以今天来梳理一下这几种树之间的区别与联系,感谢知乎用户@Cailiang,这篇文章参考了他的专栏。
二叉查找树
是一棵空树,或是具有下列性质的二叉树:
若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值; 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;它的左、右子树也分别为二叉排序树。
插入数据:
1 如果根节点为空,则将插入的节点作为根节点
2 否则和根节点比较(我们是通过key来比较,所以 K 必须是实现了Comparable接口的对象)如果比根节点小,则新节点会插入到左子树中,如果比根节点大,则新节点会插入到右子树中,如果和根节点相等,则更新根节点的值
通过key查询:
1 如果根节点为空,返回 null
2 如果,key 和根节点的key相同,则返回根节点
3 如果,key比根节点的key小,则递归查找根节点的左节点
4 如果,key比根节点的key大,则递归查找根节点的右节点
删除节点:
1 将要删除的节点没有子节点 ----> 直接删除
2 将要删除的节点下有一个子节点 -----> 将要被删除的节点的子节点挂靠到将要被删除的节点的父节点上即可
3 将要删除的节点下有两个子节点 ----> 在将要被删除的节点的右子树中找到一个最小节点,然后用找到的最小节点与需要删除的节点替换。最后再将最小节点进行删除。(这里你可能有其它的方案,我们这里这么做的原因是一,右子树的最小节点一定没有左节点,处理起来会简单一些,二,这样可以保持树的高度不变,甚至是降低树的高度,树的高度越低意味着操作的时间复杂度越低。所以实现删除一个节点的原则是,用最小的代价实现删除的功能,并且保持树的高度不变甚至降低树的高度)
搜索二叉树的增删改查的性能都不错,但是在一些特殊情况下,搜索二叉树的性能可能由对数型变成线性,性能大大降低,比如插入的一组数据是有序的,那么二叉搜索树的结构将变成一个链表时间复杂度变为 O(N),也就是说插入一组有序或局部有序的数据将会导致二叉搜索树不平衡,树的高度会很大,时间复杂度会趋近于 O(N)。
为了解决这个问题,引出了平衡二叉树(AVL)。
平衡二叉树,首先是一棵二叉查找树,但是它满足一点重要的特性:每一个结点的左子树和右子树的高度差最多为1。这个高度差限制就完全规避了上述的最坏情况,因此查找、插入和删除的时间复杂度都变成了O(lg n)。
AVL树虽然是一个完美平衡的二叉排序树,并且保证插入,删除,查找的时间复杂度为 lg n, 但是AVL树的应用却不广泛,原因就是维护一颗AVL树操作太复杂,成本太高。当我们对一颗AVL树进行插入或删除的操作时,我们需要不断的回朔来修改平衡因子(左子树的高度减去右字树的高度),需要通过左旋转,右旋转来保证每个节点的左右子树的高度差不超过1。这些复杂的操作甚至抵消了AVL树给我们带来的性能上的提升,所以我们一般不会使用AVL树。
红黑树
吸取了AVL树和2-3树的思想,但放弃了AVL树完美平衡的特性,改为局部平衡或完美黑色平衡;放弃了2-3树3节点,改为通过颜色(红色和黑色)来区分不同的节点类型,这样就降低了维护平衡的成本和实现的复杂度,可以直接使用二叉排序树的查询方法来查询无需任何修改。
恢复红黑属性需要少量(O(log n))的颜色变更(这在实践中是非常快速的)并且不超过三次树旋转(对于插入是两次)。插入和删除为 O(log n) 次,但是这导致了非常复杂的操作。
红黑树的性质:
1 所有节点都是红色或者黑色
2 根节点为黑色
3 所有的 NULL 叶子节点都是黑色
4 如果该节点是红色的,那么该节点的子节点一定都是黑色
5 所有的 NULL 节点到根节点的路径上的黑色节点数量一定是相同的
B-Tree
前面介绍了AVL树,红黑树,它们都属于二叉树,即每个节点最多只能拥有2个子节点,而B-tree(B树)的每个节点可以拥有2个以上的子节点,所以简单概括一下:B-tree就是一颗多路平衡查找树,它广泛应用于数据库索引和文件系统中。
首先我们介绍下 m 阶B-tree的特性,那么这个 m 阶是怎么定义的呢?这里我们以一个节点能拥有的最大子节点数来表示这颗树的阶数。举个例子,如果一个节点最多有 n 个key,那么这个节点最多就会有 n+1 个子节点,这棵树就叫做 n+1(m=n+1)阶树。一颗 m 阶B-tree包括以下5条特性:
1 每个节点最多有 m 个子节点
2 除根节点和叶子节点,其它每个节点至少有 [m/2] (向上取整的意思)个子节点
3 若根节点不是叶子节点,则其至少有2个子节点
4 所有NULL节点到根节点的高度都一样
5 除根节点外,其它节点都包含 n 个key,其中 [m/2] -1 <= n <= m-1
B+ Tree是B Tree的一个变种,不同之处在于:
第一:在 B-Tree中一个含有n个子树的节点有n-1个关键字(key)。而在 B+Tree中一个含有n个子树的节点有n个关键字(key)。
为什么在拥有同样子树的情况下B+Tree的节点多需要一个key呢?那是因为 B+Tree的节点会存储该节点的子树中最小的key。
第二:B-Tree的每个节点都包含了关键字(key)以及指向包含这些关键字记录的指针。而 B+Tree非叶子节点仅用来索引,数据都保存在叶子节点中。
在叶子节点中存储了所有的关键字信息,以及指向包含这些关键字记录的指针。而且这些叶子节点构成一个有序链表,即每个叶子节点会有一个指针指向其兄弟节点。在非叶子节点中只存储了关键字信息。
下面这张图画的非常好:

在数据库索引的实现中,大部分采用的是B+Tree而不是B-Tree,这又是为什么呢?
原因有二,其一是由于B+Tree 在非叶子节点中只存储了关键字信息,而没有存储指向包含这些关键字记录的指针,所以在树的高度相同时,B+Tree往往能比B-Tree存储更多的关键字信息。更最要的原因是因为 B+Tree在叶子节点中存储了所有的关键字信息,以及指向包含这些关键字记录的指针。而且这些叶子节点构成一个有序链表,这样B+Tree在实现范围查询的时候就比较容易,只需要遍历这个有序链表就行。而B-tree要实现范围查询则比较困难,但范围查询又是数据库中比较常用的功能,所以数据库中大部分采用的是B+Tree而不是B-Tree。当然B-Tree也有强于B+tree的地方,例如在随机查询中,由于B-Tree的每个节点都包含了关键字(key)以及指向包含这些关键字记录的指针,所以B-Tree可能中途就查询到需要的数据,不需要遍历到叶子节点。而B+Tree由于只在叶子节点中存储了所有的关键字信息,以及指向包含这些关键字记录的指针。在非叶子节点中只存储了关键字信息,没有存储指向包含这些关键字记录的指针,所以B+Tree一定要遍历到叶子节点才能获取到包含这些关键字记录的指针。所以B-Tree的随机查询性能会高于B+Tree。
在B+树的基础上又演变出B*树,B*Tree在非叶子结点中也增加了指向兄弟节点的指针,并且它将非叶子节点上存储的关键字个数的最小值提高到 (2/3) * m,这样的话就提高了空间利用率。
 public class BinarySerachTree<K extends Comparable<K>, V> {
     private class Node {
         private K key; // 存储的key
         private V value; // 存储的值
         private Node leftNode; // 左节点
         private Node rightNode; // 右节点
         public Node(K key, V value, Node leftNode, Node rightNode) {
             super();
             this.key = key;
             this.value = value;
             this.leftNode = leftNode;
             this.rightNode = rightNode;
         }
         @Override
         public String toString() {
             return "{\"key\":" + this.key + ", \"value\":" + "\"" + this.value + "\"" + ", \"leftNode\":"
                     + this.leftNode + ", \"rightNode\":" + this.rightNode + "}";
         }
     }
     private Node root; // 根节点
     public void put(K key, V value) {
         Node newNode = new Node(key, value, null, null);
         if (null == root) {
             root = newNode;
         } else {
             upsert(null, root, newNode);
         }
     }
     private void upsert(Node parent, Node current, Node newNode) {
         if (null == current) {
             if (newNode.key.compareTo(parent.key) > 0) {
                 parent.rightNode = newNode;
             } else {
                 parent.leftNode = newNode;
             }
         } else {
             int result = newNode.key.compareTo(current.key);
             if (result == 0) {
                 current.value = newNode.value;
             }
             parent = current;
             if (result > 0) {
                 upsert(parent, parent.rightNode, newNode);
             }
             if (result < 0) {
                 upsert(parent, parent.leftNode, newNode);
             }
         }
     }
     public Node get(K key) {
         if (null != key) {
             return find(key, root); // 从根节点开始找
         }
         return null;
     }
     private Node find(K key, Node root) {
         if (null != root) {
             int result = key.compareTo(root.key);
             if (result == 0) {
                 return root;
             }
             if (result > 0) {
                 return find(key, root.rightNode);
             }
             if (result < 0) {
                 return find(key, root.leftNode);
             }
         }
         return null;
     }
     public boolean delete(K key) {
         if (null != key) {
             if (null != root) {
                 return deleteNode(key, root, null);
             }
         }
         return false;
     }
     private boolean deleteNode(K key, Node current, Node parent) {
         if (null != current) {
             if (key.compareTo(current.key) > 0) {
                 return deleteNode(key, current.rightNode, current);
             }
             if (key.compareTo(current.key) < 0) {
                 return deleteNode(key, current.leftNode, current);
             }
             if (key.compareTo(current.key) == 0) {
                 if ((null != current.leftNode) && (null != current.rightNode)) { // 将要删除的节点下有两个子节点
                     dleTwoChildrenNode(current);
                     return true;
                 } else {
                     if ((null == current.leftNode) && (null == current.rightNode)) { // 将要删除的节点没有子节点
                         if (current.key.compareTo(parent.key) > 0) {
                             parent.rightNode = null;
                         } else {
                             parent.leftNode = null;
                         }
                         return true;
                     } else { // 将要被删除的节点的子节点挂靠到将要被删除的节点的父节点上即可
                         Node childNode = (null == current.leftNode) ? current.rightNode : current.leftNode;
                         if (current.key.compareTo(parent.key) > 0) {
                             parent.rightNode = childNode;
                         } else {
                             parent.leftNode = childNode;
                         }
                         return true;
                     }
                 }
             }
         }
         return false;
     }
     /**
      * 处理被删除节点有两个子节点的情况
      * @param parent
      * 将要被删除的节点
      */
     private void dleTwoChildrenNode(Node parent) {
         Node parentRight = parent.rightNode;
         Node tmp = parentRight.leftNode;
         if (null == tmp) {
             parent.value = parentRight.value;
             parent.key = parentRight.key;
             parent.rightNode = parentRight.rightNode;
         } else {
             Node tmpParent = parentRight;
             while (null != tmp.leftNode) {
                 tmpParent = tmp;
                 tmp = tmp.leftNode;
             }
             parent.value = tmp.value;
             parent.key = tmp.key;
             tmpParent.leftNode = tmp.rightNode;
         }
     }
     public static void main(String[] args) {
         BinarySerachTree<Integer, String> bst = new BinarySerachTree<Integer, String>();
         bst.put(100, "v100");
         bst.put(50, "v50");
         bst.put(150, "v150");
         bst.put(20, "v20");
         bst.put(85, "v85");
         bst.put(10, "v10");
         bst.put(15, "a15");
         bst.put(75, "v75");
         bst.put(95, "v95");
         bst.put(65, "v65");
         bst.put(76, "v76");
         bst.put(60, "v60");
         bst.put(66, "v66");
         bst.put(61, "v61");
         System.out.println(bst.get(100));// 打印根节点
     }
 }
BinarySerachTree
 public class RedBlackTree<K extends Comparable<K>, V> {
     private static final byte RED = 1;
     private static final byte BLACK = 0;
     private class Node {
         private byte color;
         private K key; // 存储的key
         private V value; // 存储的值
         private Node leftNode; // 左节点
         private Node rightNode; // 右节点
         private Node parentNode; // 父节点
         public Node(K key, V value, Node leftNode, Node rightNode, byte color, Node parentNode) {
             super();
             this.key = key;
             this.value = value;
             this.leftNode = leftNode;
             this.rightNode = rightNode;
             this.color = color;
             this.parentNode = parentNode;
         }
         @Override
         public String toString() {
             return "{" + "\"key\":" + this.key + ", " + "\"value\":" + "\"" + this.value + "\"" + ", " + "\"color\":"
                     + this.color + ", " + "\"leftNode\":" + this.leftNode + "," + "\"rightNode\":" + this.rightNode
                     + "}";
         }
     }
     private Node root; // 根节点
     public Node getRoot() {
         return this.root;
     }
     private void leftRotation(Node h) {
         Node m = h.rightNode;
         // 1. 将 k 节点设置为 h 的右节点
         h.rightNode = m.leftNode;
         if (null != m.leftNode) {
             m.leftNode.parentNode = h;
         }
         // 2. 将 h 的父节点 赋给 m 的父节点,之后分 3 种情况讨论
         m.parentNode = h.parentNode;
         if (null == m.parentNode) { // I: 说明 h 原来是根节点,现在将 m 设置为新的根节点
             root = m;
         } else {
             if (h.key.compareTo(h.parentNode.key) < 0) { // II: 说明 h 原来是它父节点的左孩子,现在将 m 设置为新的左孩子
                 h.parentNode.leftNode = m;
             } else {
                 h.parentNode.rightNode = m; // III: 说明 h 原来是它父节点的右孩子,现在将 m 设置为新的右孩子
             }
         }
         // 3. 将 h 挂靠在 m 的左孩子上
         m.leftNode = h;
         h.parentNode = m;
     }
     private void rightRotation(Node m) {
         Node h = m.leftNode;
         // 1. 将 k 设置为 m 的左节点
         m.leftNode = h.rightNode;
         if (null != h.rightNode) {
             h.rightNode.parentNode = m;
         }
         // 2. 将 m 的父节点 赋给 h 的父节点,之后分 3 种情况讨论
         h.parentNode = m.parentNode;
         if (null == m.parentNode) { // I: 说明 m 原来是根节点,现在将 h 设置为新的根节点
             root = h;
         } else {
             if (m.key.compareTo(m.parentNode.key) < 0) { // II: 说明 m 原来是它父节点的左孩子,现在将 h 设置为新的左孩子
                 m.parentNode.leftNode = h;
             } else {
                 m.parentNode.rightNode = h; // III: 说明 m 原来是它父节点的右孩子,现在将 h 设置为新的右孩子
             }
         }
         // 3. 将 m 挂靠在 h 的右孩子上
         h.rightNode = m;
         m.parentNode = h;
     }
     /**
      * 插入新的节点,如果指定的key已经存在,则更新原来的值
      *
      * @param key
      * @param value
      */
     public void put(K key, V value) {
         Node newNode = new Node(key, value, null, null, RED, null);
         if (null == root) {
             root = newNode;
             root.color = BLACK;
         } else {
             upsert(null, root, newNode);
         }
     }
     private void upsert(Node parent, Node current, Node newNode) {
         if (null == current) {
             if (newNode.key.compareTo(parent.key) > 0) {
                 parent.rightNode = newNode;
             } else {
                 parent.leftNode = newNode;
             }
             newNode.parentNode = parent;
             upsertFix(newNode); // 插入新节点后 对红黑树进行修复
         } else {
             int result = newNode.key.compareTo(current.key);
             if (result == 0) {
                 current.value = newNode.value;
             }
             parent = current;
             if (result > 0) {
                 upsert(parent, parent.rightNode, newNode);
             }
             if (result < 0) {
                 upsert(parent, parent.leftNode, newNode);
             }
         }
     }
     private void upsertFix(Node newNode) {
         Node parent = newNode.parentNode;
         if (RED == parent.color) { // 父节点如果是黑节点 则不需要处理
             Node grandfather = parent.parentNode;
             if (parent == grandfather.leftNode) { // 1. 父节点原来是 左节点
                 Node uncle = grandfather.rightNode;
                 if ((null != uncle) && (RED == uncle.color)) { // case 3: 叔叔节点是红色
                     uncleRedFix(newNode);
                 } else { // 叔叔节点为 NULL 或者 是黑色节点
                     if (newNode.key.compareTo(parent.key) < 0) { // case 1: 叔叔节点是黑色,插入到左子树中
                         leftNodeFix(grandfather, parent);
                     } else { // case 2: 叔叔节点是黑色,插入到右子树中
                         leftRotation(parent);
                         leftNodeFix(grandfather, newNode); // 我们将 parent 节点作为“新插入的节点”,这样 真正新插入的节点 newNode 就是父节点
                     }
                 }
             } else { // 1. 父节点原来是 右节点
                 Node uncle = grandfather.leftNode;
                 if ((null != uncle) && (RED == uncle.color)) { // case 3: 叔叔节点是红色
                     uncleRedFix(newNode);
                 } else { // 叔叔节点为 NULL 或者 是黑色节点
                     if (newNode.key.compareTo(parent.key) > 0) { // case 1: 叔叔节点是黑色,插入到右子树中
                         rightNodeFix(grandfather, parent);
                     } else { // case 2: 叔叔节点是黑色,插入到左子树中
                         rightRotation(parent);
                         rightNodeFix(grandfather, newNode); // 我们将 parent 节点作为“新插入的节点”,这样 真正新插入的节点 newNode 就是父节点
                     }
                 }
             }
         }
     }
     /**
      * 处理 父节点原来是 左节点 的 case 1 的情况: 叔叔节点是黑色,插入到左子树中
      *
      * @param grandfather
      * @param parent
      */
     private void leftNodeFix(Node grandfather, Node parent) {
         parent.color = BLACK;
         grandfather.color = RED;
         rightRotation(grandfather);
     }
     /**
      * 处理 父节点原来是 右节点 的 case 1 的情况: 叔叔节点是黑色,插入到右子树中
      *
      * @param grandfather
      * @param parent
      */
     private void rightNodeFix(Node grandfather, Node parent) {
         parent.color = BLACK;
         grandfather.color = RED;
         leftRotation(grandfather);
     }
     /**
      * 处理 case 3: 叔叔节点是红色
      *
      * @param newNode
      */
     private void uncleRedFix(Node newNode) {
         Node parent = newNode.parentNode;
         if ((null != parent) && (RED == parent.color)) {
             Node grandfather = parent.parentNode;
             Node uncle = grandfather.leftNode;
             if (parent == grandfather.leftNode) {
                 uncle = grandfather.rightNode;
             }
             parent.color = BLACK;
             uncle.color = BLACK;
             if (root != grandfather) {
                 grandfather.color = RED;
                 upsertFix(grandfather);
             }
         }
     }
     /**
      * 删除 叶子节点 后的修复过程
      *
      * @param deletedNode
      *            被删除的节点
      * @param deletedNodeParent
      *            被删除节点的父节点
      */
     private void deleteLeafFix(Node deletedNode) {
         while ((deletedNode != root) && (BLACK == deletedNode.color)) {
             Node parent = deletedNode.parentNode;
             Node brother = getBrother(deletedNode);
             if (deletedNode.key.compareTo(parent.key) > 0) { // 删除的是右叶子节点
                 if (RED == brother.color) { // case5: 如果该兄弟节点是红色的,那么根据红黑树的特性可以得出它的一定有两个黑色的子节点
                     brother.color = BLACK;
                     brother.rightNode.color = RED;
                     rightRotation(parent);
                     break;
                 } else {
                     if ((null == brother.leftNode) && (null == brother.rightNode)) { // case4: 兄弟节点是黑色的,且没有子节点
                         brother.color = RED; // 将兄弟节点设为红色,将父节点设为当前节点递归, 直到根节点,或遇到红色节点,
                         deletedNode = parent;
                     } else {
                         if ((null != brother.leftNode) && (RED == brother.leftNode.color)) {// case1:
                                                                                             // 兄弟节点是黑色的,且有一个左节点(可以断定
                                                                                             // 左节点是红色的)
                             // case3: 兄弟节点是黑色的,且有两个节点(可以断定 左右节点都是红色的) 这个和情况 1 是一样的
                             brother.color = parent.color;
                             parent.color = BLACK;
                             brother.leftNode.color = BLACK;
                             rightRotation(parent);
                             break;
                         } else {// case2: 兄弟节点是黑色的,且有一个右节点(可以断定 右节点是红色的)
                             brother.rightNode.color = BLACK;
                             brother.color = RED;
                             leftRotation(brother);
                         }
                     }
                 }
             } else {// 删除的是左叶子节点
                 if (RED == brother.color) { // case5 : 如果该兄弟节点是红色的,那么根据红黑树的特性可以得出它的一定有两个黑色的子节点
                     brother.color = BLACK;
                     brother.leftNode.color = RED;
                     leftRotation(parent);
                     break;
                 } else {
                     if ((null == brother.leftNode) && (null == brother.rightNode)) { // case4: 兄弟节点是黑色的,且没有子节点
                         brother.color = RED; // 将兄弟节点设为红色,将父节点设为当前节点递归, 直到根节点,或遇到红色节点,
                         deletedNode = parent;
                     } else {
                         if ((null != brother.rightNode) && (RED == brother.rightNode.color)) { // case1 :
                                                                                                 // 兄弟节点是黑色的,且有一个右节点(可以断定
                                                                                                 // 右节点是红色的)
                             // case3 : 兄弟节点是黑色的,且有两个节点(可以断定 左右节点都是红色的) 这个和情况 1 是一样的
                             brother.color = parent.color;
                             parent.color = BLACK;
                             brother.rightNode.color = BLACK;
                             leftRotation(parent);
                             break;
                         } else { // case2: 兄弟节点是黑色的,且有一个左节点(可以断定 左节点是红色的)
                             brother.leftNode.color = BLACK;
                             brother.color = RED;
                             rightRotation(brother);
                         }
                     }
                 }
             }
         }
         deletedNode.color = BLACK;
     }
     private Node getBrother(Node node) {
         if (null == node) {
             return null;
         }
         Node parent = node.parentNode;
         if (null == parent) {
             return null;
         }
         if (node.key.compareTo(parent.key) > 0) {
             return parent.leftNode;
         } else {
             return parent.rightNode;
         }
     }
     public boolean delete(K key) {
         if (null != key) {
             if (null != root) {
                 return deleteNode(key, root, null);
             }
         }
         return false;
     }
     private boolean deleteNode(K key, Node current, Node parent) {
         if (null != current) {
             if (key.compareTo(current.key) > 0) {
                 return deleteNode(key, current.rightNode, current);
             }
             if (key.compareTo(current.key) < 0) {
                 return deleteNode(key, current.leftNode, current);
             }
             if (key.compareTo(current.key) == 0) {
                 if ((null != current.leftNode) && (null != current.rightNode)) { // 将要删除的节点下有两个子节点
                     dleTwoChildrenNode(current);
                     return true;
                 } else {
                     if ((null == current.leftNode) && (null == current.rightNode)) { // 将要删除的节点没有子节点
                         deleteLeafFix(current);
                         if (current.key.compareTo(parent.key) > 0) {
                             parent.rightNode = null;
                         } else {
                             parent.leftNode = null;
                         }
                         return true;
                     } else { // 将要删除的节点下有一个子节点,
                         dleOneChildNode(current);
                         return true;
                     }
                 }
             }
         }
         return false;
     }
     private void dleOneChildNode(Node delNode) {
         Node replaceNode = (null == delNode.leftNode) ? delNode.rightNode : delNode.leftNode;
         deltetLeafNode(delNode, replaceNode);
     }
     /**
      * 处理被删除节点有两个子节点的情况
      *
      * @param target
      *            将要被删除的节点
      */
     private void dleTwoChildrenNode(Node target) {
         Node replaceNode = successor(target);
         if ((null == replaceNode.rightNode) && (null == replaceNode.leftNode)) {
             deltetLeafNode(target, replaceNode);
         } else {
             target.key = replaceNode.key;
             target.value = replaceNode.value;
             dleOneChildNode(replaceNode);
         }
     }
     private void deltetLeafNode(Node target, Node replaceNode) {
         target.key = replaceNode.key;
         target.value = replaceNode.value;
         deleteLeafFix(replaceNode);
         if (replaceNode == replaceNode.parentNode.rightNode) {
             replaceNode.parentNode.rightNode = null;
         } else {
             replaceNode.parentNode.leftNode = null;
         }
     }
     // 找后继结点。即,查找"红黑树中数据值大于该结点"的"最小结点"
     private Node successor(Node node) {
         if (node == null) {
             return null;
         }
         if (null != node.rightNode) { // 获取 后继节点
             Node p = node.rightNode;
             while (null != p.leftNode) {
                 p = p.leftNode;
             }
             return p;
         } else {
             Node p = node.parentNode;
             Node ch = node;
             while (p != null && ch == p.rightNode) {
                 ch = p;
                 p = p.parentNode;
             }
             return p;
         }
     }
     public static void main(String[] args) {
         RedBlackTree<Integer, String> bst = new RedBlackTree<Integer, String>();
         bst.put(100, "v100");
         bst.put(50, "v50");
         bst.put(150, "v150");
         bst.put(20, "v20");
         bst.put(85, "v85");
         bst.put(10, "v10");
         bst.put(15, "a15");
         bst.put(75, "v75");
         bst.put(95, "v95");
         bst.put(65, "v65");
         bst.put(76, "v76");
         bst.put(60, "v60");
         bst.put(66, "v66");
         bst.put(61, "v61");
         // 当前节点是左节点 的 5中情况
         // bst.delete(15); // 1. 兄弟节点是黑色的,且有一个右节点(可以断定 右节点是红色的)
         // 2. 兄弟节点是黑色的,且有一个左节点(可以断定 左节点是红色的
         // bst.put(140, "v140");
         // bst.delete(95);
         // 4. 兄弟节点是黑色的,且没有子节点
         // bst.delete(66);
         // 5. 如果该兄弟节点是红色的,那么根据红黑树的特性可以得出它的一定有两个黑色的子节点
         // bst.delete(95);
         // bst.delete(15);
         System.out.println(bst.getRoot());
     }
 }
RedBlackTree
从二叉搜索树到AVL树再到红黑树 B树的更多相关文章
- 数据结构中很常见的各种树(BST二叉搜索树、AVL平衡二叉树、RBT红黑树、B-树、B+树、B*树)
		
数据结构中常见的树(BST二叉搜索树.AVL平衡二叉树.RBT红黑树.B-树.B+树.B*树) 二叉排序树.平衡树.红黑树 红黑树----第四篇:一步一图一代码,一定要让你真正彻底明白红黑树 --- ...
 - 数据结构中常见的树(BST二叉搜索树、AVL平衡二叉树、RBT红黑树、B-树、B+树、B*树)
		
树 即二叉搜索树: 1.所有非叶子结点至多拥有两个儿子(Left和Right): 2.所有结点存储一个关键字: 非叶子结点的左指针指向小于其关键字的子树,右指针指向大于其关键字的子树: 如: BST树 ...
 - 算法二叉搜索树之AVL树
		
最近学习了二叉搜索树中的AVL树,特在此写一篇博客小结. 1.引言 对于二叉搜索树而言,其插入查找删除等性能直接和树的高度有关,因此我们发明了平衡二叉搜索树.在计算机科学中,AVL树是最先发明的自平衡 ...
 - 算法进阶面试题04——平衡二叉搜索树、AVL/红黑/SB树、删除和调整平衡的方法、输出大楼轮廓、累加和等于num的最长数组、滴滴Xor
		
接着第三课的内容和讲了第四课的部分内容 1.介绍二叉搜索树 在二叉树上,何为一个节点的后继节点? 何为搜索二叉树? 如何实现搜索二叉树的查找?插入?删除? 二叉树的概念上衍生出的. 任何一个节点,左比 ...
 - 二叉搜索树、AVL平衡二叉搜索树、红黑树、多路查找树
		
1.二叉搜索树 1.1定义 是一棵二叉树,每个节点一定大于等于其左子树中每一个节点,小于等于其右子树每一个节点 1.2插入节点 从根节点开始向下找到合适的位置插入成为叶子结点即可:在向下遍历时,如果要 ...
 - 数据结构中的树(二叉树、二叉搜索树、AVL树)
		
数据结构动图展示网站 树的概念 树(英语:tree)是一种抽象数据类型(ADT)或是实作这种抽象数据类型的数据结构,用来模拟具有树状结构性质的数据集合.它是由n(n>=1)个有限节点组成一个具有 ...
 - 【数据结构与算法Python版学习笔记】树——平衡二叉搜索树(AVL树)
		
定义 能够在key插入时一直保持平衡的二叉查找树: AVL树 利用AVL树实现ADT Map, 基本上与BST的实现相同,不同之处仅在于二叉树的生成与维护过程 平衡因子 AVL树的实现中, 需要对每个 ...
 - 数据结构之二叉搜索树、AVL自平衡树
		
前言 最近在帮公司校招~~ 所以来整理一些数据结构方面的知识,这些知识呢,光看一遍理解还是很浅的,看过跟动手做过一遍的同学还是很容易分辨的哟~ 一直觉得数据结构跟算法,就好比金庸小说里的<九阳神 ...
 - 【数据结构05】红-黑树基础----二叉搜索树(Binary Search Tree)
		
目录 1.二分法引言 2.二叉搜索树定义 3.二叉搜索树的CRUD 4.二叉搜索树的两种极端情况 5.二叉搜索树总结 前言 在[算法04]树与二叉树中,已经介绍过了关于树的一些基本概念以及二叉树的前中 ...
 
随机推荐
- 使用那各VUE的打印功能(print.js)出现多打印一个空白页的问题
			
最近这段时间,用VUE写东西,有个打印功能. 百度了一下,铺天盖地的VUE打印的两种实现方法. 很感激这些千篇一律的帖子,虽然不知道他们是否真的用过,还是只是复制粘贴. 至少这些帖子告诉我,是有两个可 ...
 - Java语言概述-JavaSE
			
代码虐我千百遍,我视代码如初恋 初级学习思想: 先了解Java 下载中英文文档对照学习 多看,多学 多敲,狂练 多标注注释 总结—创造 https://baike.baidu.com/(Java百度百 ...
 - NFS工作原理简述
			
1.首先用户访问网站程序,由程序在NFS客户端发出存取NFS文件的请求,NFS客户端上的RPC服务通过网络向NFS服务器的RPC服务的111端口发出NFS文件存取的询问请求. 2.NFS服务器的RPC ...
 - OpenMP笔记(五)
			
任务调度主要用于并行的for循环中,当循环中每次迭代的计算量不相等时,如果简单地给各个线程分配相同次数的迭代的话,会造成各个线程计算负载不均衡,这会使得有些线程先执行完,有些后执行完,造成某些CPU核 ...
 - 记一次Java面试问题点总结
			
引言 昨日接了一个阿里外包的电话面试,问了一些技术问题感觉到自己是真的菜,接触Java开发已经也有一段时间,技术方面说来惭愧,一直以来只是局限于框架工具的用法,也没有进行了解其实现的原理,更重要的是一 ...
 - [代码审计]PCWAP
			
为什么想要审计这套源码呐?之前看到某大佬在做反钓鱼网站的时候,发现钓鱼网站的后台用的就是PCWAP,所以我觉得有必要审计一下,顺便记录,打击网络犯罪! 0x00 PCAWAP: PCWAP手机网站建站 ...
 - ElasticSearch 分组查询
			
curl -XPOST 'localhost:9200/bank/_search?pretty' -d ' { "size": 0, "aggs": { &qu ...
 - html 基础笔记
			
html 1,一套规则,浏览器认识的规则 2,开发者: 学习Html规则 开发后台程序: -写Html文件(充当模板的作用) -数据库获取数据,然后替换到html文件的指定位置(web框架) 3,本地 ...
 - linux  select函数
			
/**两个线程一个负责监听客户端,一个负责读客户端请求. 服务器模型,*主控线程负责accept监听链接的客户端,*把客户端fd放入任务队列中(),分离子线程则从任务队列取出所有的*客户端描述加入se ...
 - ZJNU 1129 - The sum problem——中级
			
枚举区间可能的长度len,将m减去1~len构成的序列和后如果结果是len的倍数,则可以构成答案区间. /* Written By. StelaYuri */ #include<stdio.h& ...