最近学习了二叉搜索树中的红黑树,感觉收获颇丰,在此写一篇文章小结一下学到的知识,顺便手写一下Java代码。

1.引言

先来讲讲什么是二叉搜索树,二叉搜索树有如下特点:他是以一颗二叉树(最多有两个子结点)来组织的,对于树中的某个节点,其左子树的所有元素均小于该节点,其右子树的元素均大于该节点。我们知道一颗有N个节点的二叉树的高度至少为lgN,然后在树上的操作都与其高度有关,因此限制树的高度就显得非常有必要。当一个二叉搜索树的高度是lgN时,在该树上的插入删除搜索等操作均为O(lgN)的时间复杂度,但当二叉搜索树不小心插入成了链表,高度为N的时候,在树上的操作就变为O(N)了。因此我们有许多种平衡二叉树通过特定的方法来限制树的高度,红黑树就是其中的一种。红黑树(Red Black Tree) 是一种自平衡二叉查找树,是在计算机科学中用到的一种数据结构,它在每个节点上增加了一个存储位来表示节点的颜色,可以为红色或黑色。通过对任何一条从根到叶子的简单路径上各个节点的颜色进行约束,红黑树确保没有一条路径会比其他路径长出2倍,因此是近似于平衡的。

2.红黑树的性质

一颗红黑树是满足以下红黑性质的二叉搜索树:
  1. 每个节点是红色或黑色
  2. 根是黑色
  3. 叶节点(null)是黑色的
  4. 红色的节点的两个子结点均为黑色
  5. 对于每个节点,从该节点到其所有后代的简单路径上,均包含相同数目的黑色节点(我们把到叶节点的黑色节点数称为黑高)
Java中树的节点类如下:

[java] view plaincopy
  1. // 颜色枚举
  2. enum RBColor {
  3. RED, BLACK;
  4. }
  5. // 树节点类
  6. class RBTreeNode {
  7. RBTreeNode p = nullNode;    // 父节点
  8. RBTreeNode left = nullNode; // 左子节点
  9. RBTreeNode right = nullNode;    // 右子节点
  10. int val;    // 值
  11. RBColor color;  // 颜色
  12. public RBTreeNode() {};
  13. RBTreeNode(int val) {
  14. this.val = val;
  15. }
  16. @Override
  17. public String toString() {
  18. return " (" + val + " " +  color + ") ";
  19. }
  20. // 用于表示空叶节点的静态变量
  21. public static RBTreeNode nullNode = new RBTreeNode() {
  22. {
  23. color = RBColor.BLACK; // 叶结点为黑色
  24. }
  25. @Override
  26. public String toString() {
  27. return " (null " +  color + ") ";
  28. }
  29. };
  30. }

3.旋转

红黑树的关键操作在于其插入和删除操作,但在讲解这两步关键操作之前,我们得定义一些辅助方法来让我们更好的完成任务,该辅助方法就是树的旋转,示意图如下:
旋转由两种,分别是左旋和右旋,相信上图已经表示的非常明确,这里就不再细说,值得注意的是:在旋转操作中只有指针的改变,其他属性都保持不变。对旋转前后的树使用中序遍历将得到相同的结果。
下面是旋转的Java代码:
[java] view plaincopy
  1. /**
  2. * 左旋操作
  3. * @param root 根结点引用
  4. * @param node 旋转的节点
  5. * @return 根节点
  6. */
  7. public static RBTreeNode leftRotate(RBTreeNode root, RBTreeNode node) {
  8. if (node.right == RBTreeNode.nullNode)
  9. return root;    // 左旋需要拥有右节点
  10. RBTreeNode right = node.right;
  11. // 旋转节点的右子树变为右节点的左子树
  12. node.right = right.left;
  13. if (node.right != RBTreeNode.nullNode)
  14. node.right.p = node;
  15. // 用右节点代替旋转节点位置
  16. if (node.p != RBTreeNode.nullNode) {
  17. right.p = node.p;
  18. if (node.p.left == node)
  19. node.p.left = right;
  20. else
  21. node.p.right = right;
  22. } else {
  23. root = right; // 没有父节点的节点为根结点
  24. root.p = RBTreeNode.nullNode;
  25. }
  26. // 右节点的左子树变为旋转节点
  27. right.left = node;
  28. node.p = right;
  29. return root;
  30. }
  31. /**
  32. * 右旋操作
  33. * @param root 根结点引用
  34. * @param node 旋转节点
  35. * @return 根节点
  36. */
  37. public static RBTreeNode rightRotate(RBTreeNode root, RBTreeNode node) {
  38. if (node.left == RBTreeNode.nullNode)
  39. return root; // 右旋需要有左节点
  40. RBTreeNode left = node.left;
  41. // 旋转节点的左子树变为左节点的右子树
  42. node.left = left.right;
  43. if (node.left != RBTreeNode.nullNode)
  44. node.left.p = node;
  45. // 用左节点代替旋转节点
  46. if (node.p != RBTreeNode.nullNode) {
  47. left.p = node.p;
  48. if (node.p.left == node)
  49. node.p.left = left;
  50. else
  51. node.p.right = left;
  52. } else {
  53. root = left;
  54. root.p = RBTreeNode.nullNode;
  55. }
  56. // 左节点的右子树变为旋转节点
  57. left.right = node;
  58. node.p = left;
  59. return root;
  60. }

4.插入

终于来到红黑树的第一个关键步骤了:插入操作。
对与插入操作我们利用如下思想解决:我们先把红黑树看成一个普通的二叉搜索树,对其进行插入操作,插入完成后,我们把新加入的节点染成红色,此时红黑树的红黑性质被破坏,然后再通过特定的方法来维护红黑树的性质。
插入的Java代码如下:
[java] view plaincopy
  1. /**
  2. * 红黑树插入操作
  3. * @param root 根结点引用
  4. * @param insertNode 要插入的新节点
  5. * @return 根节点
  6. */
  7. public static RBTreeNode rbInsert(RBTreeNode root, RBTreeNode insertNode) {
  8. RBTreeNode position = root, parent = RBTreeNode.nullNode; // position为插入位置,parent为该位置的父节点
  9. while (position != RBTreeNode.nullNode) {
  10. parent = position;
  11. if (insertNode.val < position.val) // 比该节点元素小的节点应该插入其左子树
  12. position = position.left;
  13. else // 比该节点元素大的节点应该插入其右子树
  14. position = position.right;
  15. }
  16. insertNode.p = parent;
  17. if (parent == RBTreeNode.nullNode) // 没有父节点的节点为根结点
  18. root = insertNode;
  19. else if (insertNode.val < parent.val) // 插入为左节点
  20. parent.left = insertNode;
  21. else // 插入为右节点
  22. parent.right = insertNode;
  23. insertNode.color = RBColor.RED; // 把新插入的节点染成红色
  24. return rbInsertFixup(root, insertNode); // 修复插入时红黑树性质
  25. }
好,终于来到重点了,红黑树的插入操作前半部分与一般二叉搜索树别无二致,区别在于最后把新加入的节点染成红色和恢复红黑树性质的部分。我们先来思考一下往红黑树插入一个红节点会破坏红黑树的什么性质?首先性质1、3、5是不会受影响的,那么当我们插入的节点是红黑树的根结点时会影响性质2,根节点变成了红色,此时我们把根节点染成黑色即可。当我们插入节点的父节点是红色时会影响性质4,红色节点有一个为红色的子结点。对于以上这些影响我们分为3种情况来处理:

关键词:叔节点

下面我们假设插入的节点为z(红色),其父节点为x(红色,为祖父节点的左节点,右节点情况镜像处理即可),其叔节点为y(未知),祖父节点为w(黑色)

情况1:插入节点z的叔节点y为红色

此时的情况如图所示(下图省略了部分不关键的子树):
此时的处理方法很简单,我们只需把祖父节点的黑色“扒”下来放到父节点X和叔节点Y即可,此时对于节点Z就保持了红黑树的性质4,然而进行了此操作后我们还需要对祖父节点W进行继续遍历,因为此时祖父节点有可能违反了红黑树的性质。当我们遍历的祖父节点为根结点时,把根结点变为黑色即可。

情况2:插入节点z的叔节点y是黑色的,且z是一个右孩子

情况3:插入节点z的叔节点y是黑色的,且z是一个左孩子

这两种情况可以放一起讨论,因为我们会把情况2转化为情况3,示意图如下:

左上角为情况2,此时叔节点w为黑色,且插入节点z为父节点x的右孩子,此时我们对父节点x进行一次左旋,然后交换x和z的引用,即可转换为右上角的情况3.
右上角为情况3,此时叔节点w为黑色,且插入节点z为父节点x的左孩子,此时我们进行如下操作即可恢复红黑树的性质:
  1. 交换父节点x和祖父节点w的颜色
  2. 对祖父节点w进行右旋
上面的操作既修正了对性质4的违反,也没有引起对其他红黑树性质的违反,因此我们此时可以结束对红黑树的性质修复工作。

下面给出红黑树插入时性质修复的Java代码:
[java] view plaincopy
  1. /**
  2. * 修復插入時违反的红黑树性质
  3. * @param root 根节点引用
  4. * @param node 修复节点
  5. * @return 根节点
  6. */
  7. public static RBTreeNode rbInsertFixup(RBTreeNode root, RBTreeNode node) {
  8. // 修复节点不是根节点且为红色时
  9. RBTreeNode parent = node.p, grandParent, parentBorther;
  10. while(parent != RBTreeNode.nullNode && parent.color == RBColor.RED) {
  11. grandParent = parent.p;
  12. if (grandParent.left == parent) { // 父节点为左节点
  13. parentBorther = grandParent.right; // 叔节点为右节点
  14. if (parentBorther != RBTreeNode.nullNode && parentBorther.color == RBColor.RED) { // case 1
  15. grandParent.color = RBColor.RED; // 祖父节点改为红色
  16. parent.color = RBColor.BLACK; // 父节点和叔节点改为黑色
  17. parentBorther.color = RBColor.BLACK;
  18. node = grandParent; // 对祖父节点继续遍历
  19. } else {
  20. if (parent.right == node) { // case 2
  21. root = leftRotate(root, parent); // 对父节点左旋
  22. // 交换node和parent的引用
  23. RBTreeNode temp = node;
  24. node = parent;
  25. parent = temp;
  26. }
  27. // case 3
  28. grandParent.color = RBColor.RED; // 祖父染成红色
  29. parent.color = RBColor.BLACK; // 父节点染成黑色
  30. root = rightRotate(root, grandParent); // 对祖父右旋
  31. node = root; // 把节点置为根节点退出修复
  32. }
  33. } else { // 父节点为右节点,镜像处理
  34. parentBorther = grandParent.left;
  35. if (parentBorther != RBTreeNode.nullNode && parentBorther.color == RBColor.RED) { // case 1
  36. grandParent.color = RBColor.RED;
  37. parent.color = RBColor.BLACK;
  38. parentBorther.color = RBColor.BLACK;
  39. node = grandParent;
  40. } else {
  41. if (parent.left == node) { // case 2
  42. root = rightRotate(root, parent);
  43. RBTreeNode temp = node;
  44. node = parent;
  45. parent = temp;
  46. }
  47. // case 3
  48. grandParent.color = RBColor.RED;
  49. parent.color = RBColor.BLACK;
  50. root = leftRotate(root, grandParent);
  51. node = root;
  52. }
  53. }
  54. parent = node.p;
  55. }
  56. // 根节点染为黑色
  57. root.color = RBColor.BLACK;
  58. return root;
  59. }

5.删除

讲完插入,我们来讲讲删除操作。与插入类似,再删除前我们先把红黑树当成是一颗普通的二叉搜索树来处理删除节点的操作。但在把节点删除过后,由于删除节点会带走一种颜色,因此我们需要记录下被删除的颜色和删除颜色的位置,最后我们再考虑如何修复树的红黑性质。二叉搜索树删除节点分为三种情况,这里简单提一下:
  1. 删除节点没有子节点:直接把删除节点的位置置空即可
  2. 删除节点有一个子节点:用该子节点顶替删除节点的位置
  3. 删除节点有两个子节点:这是比较复杂的情况,此时我们要从删除节点的两边子树中寻找一个节点来顶替其位置,我们可以找右子树的最小节点或左子树的最大节点,本文给出的代码为寻找右子树的最小节点。同时在代码中我们把删除节点的颜色赋给顶替节点,从而使实际删除颜色的节点为顶替节点。
Java代码如下:

[java] view plaincopy
  1. /**
  2. * 红黑树删除操作
  3. * @param root 根节点引用
  4. * @param deleteNode 要删除的节点
  5. * @return 根节点
  6. */
  7. public static RBTreeNode rbDelete(RBTreeNode root, RBTreeNode deleteNode) {
  8. RBTreeNode replaceNode, fixNode = RBTreeNode.nullNode; // 顶替删除节点的代替节点、需要修复颜色的节点位置
  9. RBTreeNode fixNodeParent = deleteNode.p;
  10. RBColor deleteColor = deleteNode.color; // 记录被删除节点的颜色
  11. if (deleteNode.left == RBTreeNode.nullNode && deleteNode.right == RBTreeNode.nullNode) // 删除节点没有任何子结点
  12. replaceNode = RBTreeNode.nullNode;
  13. else if (deleteNode.right == RBTreeNode.nullNode) { // 处理只有左子节点的情况
  14. replaceNode = deleteNode.left;
  15. fixNode = replaceNode;
  16. } else if (deleteNode.left == RBTreeNode.nullNode) { //处理只有右子节点的情况
  17. replaceNode = deleteNode.right;
  18. fixNode = replaceNode;
  19. } else { // 处理有两个子节点的情况
  20. replaceNode = deleteNode.right;
  21. while (replaceNode.left != RBTreeNode.nullNode) // 找到右子树的最小节点
  22. replaceNode = replaceNode.left;
  23. fixNode = replaceNode.right; // 修复节点位置变为原顶替节点位置
  24. if (replaceNode.p == deleteNode) { // 特殊情况,右子树没有左节点
  25. if (fixNode != RBTreeNode.nullNode) // 修复节点不为空
  26. fixNode.p = replaceNode;
  27. fixNodeParent = replaceNode;
  28. } else {
  29. replaceNode.p.left = fixNode; // 修复节点顶替该节点的位置
  30. if (fixNode != RBTreeNode.nullNode) // 修复节点不为空
  31. fixNode.p = replaceNode.p;
  32. fixNodeParent = replaceNode.p;
  33. replaceNode.right = deleteNode.right;
  34. }
  35. // 用删除节点的颜色代替顶替节点的颜色,使得被删除颜色的节点实际变为顶替节点
  36. deleteColor = replaceNode.color;
  37. replaceNode.color = deleteNode.color;
  38. replaceNode.left = deleteNode.left;
  39. }
  40. if (replaceNode != RBTreeNode.nullNode) // 存在顶替节点
  41. replaceNode.p = deleteNode.p;
  42. if (deleteNode.p == RBTreeNode.nullNode) // 删除节点的父节点为空,是根节点
  43. root = replaceNode;
  44. else { // 删除节点不是根节点
  45. if (deleteNode.p.left == deleteNode)
  46. deleteNode.p.left = replaceNode;
  47. else
  48. deleteNode.p.right = replaceNode;
  49. }
  50. if (deleteColor == RBColor.BLACK) // 如果删除的颜色是黑色则需要进行修复
  51. root = rbDeleteFixup(root, fixNode, fixNodeParent);
  52. return root;
  53. }
接下来我们来考虑一下一上的删除操作会影响红黑树的什么性质。
首先,如果删除的节点颜色为红色,则不会影响任何红黑性质。但如果删除的颜色是黑色,则可能影响性质2(根节点是黑色的),也可能影响性质4(红色的节点的两个子结点均为黑色),也可能影响性质5(对于每个节点,从该节点到其所有后代的简单路径上,均包含相同数目的黑色节点)。那么当删除的节点颜色为黑色时,对于如何修复删除后的红黑性质,我们采用以下思考方式:
我们假设修复位置的节点具有两种颜色,该节点原来的颜色,以及我们被删除的黑色。那么:
  1. 如果该节点原来为红色,那么我们被删除的黑色可以直接覆盖其颜色不影响任何红黑性质
  2. 如果该节点是黑色同时他也是根节点,那么我们可以简单的“消除”掉节点上面的一层黑色
  3. 如果该节点是黑色,但不是根节点,我们只能通过旋转和重新着色的方法转换修复的位置或退出循环
以下把修复删除红黑性质的工作分为4中情况,此处假设修复位置节点为A(黑色,此处假设为父节点的左节点,右节点请镜像处理),其父节点为B,兄弟节点为C,兄弟节点的左子节点为D,兄弟节点的右子节点为E。

关键词:兄弟节点

情况1:A的兄弟节点为红色

如上图所示,此时我们先交换父节点B和兄弟节点C的颜色,然后对父节点B进行左旋,以上操作并不会影响红黑树性质,而我们也把情况1转化为了别的情况。

情况2:A的兄弟节点为黑色,其子节点均为黑色(下图灰色代表未知颜色)

此时的处理方法很简单,因为A节点和其兄弟节点C均为黑色,且C的子节点也均为黑色,因此我们可以把A节点和C节点的黑色上移到父节点B上,再把修复位置换为父节点B,针对父节点B继续进行修复。(如果父节点B是红色或根节点就可以停止修复了~)

情况3:A的兄弟节点为黑色,兄弟节点的左子节点为红色,右子节点为黑色

此时我们首先交换兄弟节点C与其左子红色节点D的颜色,然后对兄弟节点C进行右旋,把情况3转化为情况4继续处理。

情况4:A的兄弟节点为黑色,兄弟节点的右子节点为红色

此时我们进行如下变换操作:
  1. 把父节点B和兄弟节点的右子节点E染成黑色,兄弟节点C染成父节点颜色
  2. 对父节点B进行左旋
以上操作在没有破坏红黑树性质的情况下,消除了节点A的一重黑色,因此至此修复过程可以结束了。

删除时修复过程的Java代码如下:
[java] view plaincopy
  1. /**
  2. * 修复删除时破坏的红黑树性质
  3. * @param root 根引用
  4. * @param fixNode 修复位置
  5. * @param parent 修复位置的父节点(修复位置为叶结点时使用)
  6. * @return 根
  7. */
  8. public static RBTreeNode rbDeleteFixup(RBTreeNode root, RBTreeNode fixNode, RBTreeNode parent) {
  9. RBTreeNode brother;
  10. while (root != fixNode && fixNode.color == RBColor.BLACK) {
  11. parent = fixNode.p == null ? parent : fixNode.p; // 处理fixNode为nullNode情况
  12. if (fixNode == parent.left) { // 顶替位置在父节点左边
  13. brother = parent.right;
  14. if (brother.color == RBColor.RED) { // case 1
  15. // 交换父节点和兄弟节点的颜色
  16. RBColor temp = brother.color;
  17. brother.color = parent.color;
  18. parent.color = temp;
  19. // 父节点进行左旋
  20. root = leftRotate(root, parent);
  21. } else if (brother == RBTreeNode.nullNode) { // case 2
  22. // 兄弟节点为空,即为黑色,只需继续遍历父节点即可
  23. fixNode = parent;
  24. } else if (brother.left.color == RBColor.BLACK &&
  25. brother.right.color == RBColor.BLACK) { // case 2
  26. brother.color = RBColor.RED;
  27. fixNode = parent; // 继续遍历父节点
  28. } else { // case 3 and case 4
  29. if (brother.left.color == RBColor.RED &&
  30. brother.right.color == RBColor.BLACK) { // case 3
  31. // 兄弟节点染成红色,左子节点染成黑色
  32. brother.color = RBColor.RED;
  33. brother.left.color = RBColor.BLACK;
  34. // 兄弟节点右旋
  35. root = rightRotate(root, brother);
  36. brother = brother.p;
  37. }
  38. // case 4
  39. // 变色
  40. brother.color = parent.color;
  41. parent.color = RBColor.BLACK;
  42. brother.right.color = RBColor.BLACK;
  43. // 父节点左旋
  44. root = leftRotate(root, parent);
  45. break;
  46. }
  47. } else {
  48. brother = parent.left;
  49. if (brother.color == RBColor.RED) { // case 1
  50. // 交换父节点和兄弟节点的颜色
  51. RBColor temp = brother.color;
  52. brother.color = parent.color;
  53. parent.color = temp;
  54. // 父节点进行右旋
  55. root = rightRotate(root, parent);
  56. } else if (brother == RBTreeNode.nullNode) { // case 2
  57. // 兄弟节点为空,即为黑色,只需继续遍历父节点即可
  58. fixNode = parent;
  59. } else if (brother.left.color == RBColor.BLACK &&
  60. brother.right.color == RBColor.BLACK) { // case 2
  61. brother.color = RBColor.RED;
  62. fixNode = parent; // 继续遍历父节点
  63. } else { // case 3 and case 4
  64. if (brother.right.color == RBColor.RED &&
  65. brother.left.color == RBColor.BLACK) { // case 3
  66. // 兄弟节点染成红色,左子节点染成黑色
  67. brother.color = RBColor.RED;
  68. brother.right.color = RBColor.BLACK;
  69. // 兄弟节点右旋
  70. root = leftRotate(root, brother);
  71. brother = brother.p;
  72. }
  73. // case 4
  74. // 变色
  75. brother.color = parent.color;
  76. parent.color = RBColor.BLACK;
  77. brother.left.color = RBColor.BLACK;
  78. // 父节点左旋
  79. root = rightRotate(root, parent);
  80. break;
  81. }
  82. }
  83. }
  84. fixNode.color = RBColor.BLACK;
  85. return root;
  86. };

6.打印与测试函数

这里给出本人用来测试和打印红黑树的Java函数:
[java] view plaincopy
  1. public static void main(String[] args) {
  2. int num[] = new int[]{5, 4, 1, 6, 3, 2};
  3. List<RBTreeNode> list = new ArrayList<>();
  4. RBTreeNode root = RBTreeNode.nullNode;
  5. // 插入测试
  6. for (int i = 0; i < num.length; i++) {
  7. list.add(new RBTreeNode(num[i]));
  8. root = rbInsert(root, list.get(i));
  9. printRBTree(root);
  10. System.out.println("");
  11. }
  12. // 删除测试
  13. for (int i = 0; i < num.length; i++) {
  14. root = rbDelete(root, list.get(0));
  15. list.remove(0);
  16. printRBTree(root);
  17. System.out.println("");
  18. }
  19. }
  20. /**
  21. * 打印一颗红黑树
  22. * @param root 根节点的引用
  23. */
  24. public static void printRBTree(RBTreeNode root) {
  25. if (root == RBTreeNode.nullNode) {
  26. System.out.println("这是一颗空树");
  27. return;
  28. }
  29. Queue<RBTreeNode> q = new LinkedList<>();
  30. boolean allNull = false; // 是否全为空节点
  31. q.add(root);
  32. while (!allNull) { // 该行不是全为叶结点
  33. allNull = true;
  34. Queue<RBTreeNode> rowQ = new LinkedList<>(); // 用于存储一行的所有节点
  35. RBTreeNode node;
  36. while (!q.isEmpty()) {
  37. node = q.poll();
  38. System.out.print(node);
  39. if (node != RBTreeNode.nullNode) { // 该节点不是叶结点
  40. if (node.left != RBTreeNode.nullNode) {
  41. rowQ.add(node.left);
  42. allNull = false;
  43. } else
  44. rowQ.add(RBTreeNode.nullNode);
  45. if (node.right != RBTreeNode.nullNode) {
  46. rowQ.add(node.right);
  47. allNull = false;
  48. } else
  49. rowQ.add(RBTreeNode.nullNode);
  50. } else { // 该节点为叶节点
  51. rowQ.add(RBTreeNode.nullNode);
  52. rowQ.add(RBTreeNode.nullNode);
  53. }
  54. }
  55. q = rowQ;
  56. System.out.println("");
  57. }
  58. }

总结,没写不知道,一写吓一跳,用Java来实现红黑树还是有挺多麻烦点的:

  • 在Java中不知道如何修改根引用,所以最后都在函数上补了返回值
  • 刚开始没考虑null叶节点其实是算黑色节点的情况,后来补充了一个静态变量作为叶节点
  • 用静态变量当叶节点使得叶节点是共享的,不能修改叶节点的left,right,p指针,因此又再删除时添加了fixParent变量
  • 删除时拥有两个子树,但右子树没有左节点的情况是个坑……
总而言之,红黑树的5个性质,3种插入情况,4种删除情况记住就大概没什么问题了~

转载于:https://www.cnblogs.com/KingIceMou/p/6984138.html

算法导论之红黑树的学习相关推荐

  1. 《算法导论》红黑树详解(一):概念

    在学习红黑树之前,读者应先掌握二叉查找树的相关知识.学习红黑树或者二叉查找树,推荐大家看<算法导论>.<算法导论>原书第3版 高清PDF 带详细书签目录下载 密码:acis & ...

  2. 算法导论 之 红黑树 - 添加[C语言]

    作者:邹奇峰 邮箱:Qifeng.zou.job@hotmail.com 博客:http://blog.csdn.net/qifengzou 日期:2013.12.24 21:00 转载请注明来自&q ...

  3. java红黑树_JAVA学习-红黑树详解

    1.定义 红黑树是特殊的二叉查找树,又名R-B树(RED-BLACK-TREE),由于红黑树是特殊的二叉查找树,即红黑树具有了二叉查找树的特性,而且红黑树还具有以下特性: 1.每个节点要么是黑色要么是 ...

  4. 【数据结构和算法05】 红-黑树(转发)

    2019独角兽企业重金招聘Python工程师标准>>> [数据结构和算法05] 红-黑树(看完包懂~) 置顶 2016年04月13日 15:50:25 eson_15 阅读数:526 ...

  5. 算法-查找(红黑树)

    查找 符号表 最主要的目的是将一个键和一个值联系起来.用例能够将一个键值对插入符号表并希望在之后能够从符号表的所有键值对中按照键直接找到对应的值,即以键值对为单元的数据结构. 无序链表顺序查找 性能: ...

  6. 算法手札二:红黑树的插入原理,原理与实现篇

    红黑树的五大性质(性质四与性质五特别重要) 1. 节点必须是红色或者是黑色 2. 根节点是黑色的 3. 所有的叶子节点是黑色的. 4. 每个红色节点的两个子节点是黑色的,也就是不能存在父子两个节点全是 ...

  7. python 红黑树_python学习笔记|红黑树(性质与插入)

    定义 一种含有红黑节点并能自平衡的二叉查找树(BST) 性质 1.每个节点有红/黑标记位 2.根节点是黑色(硬性规定) 3.每个叶子节点(NIL)都是黑色的虚节点(由此引出性质5) 叶子节点 colo ...

  8. 红黑树原理学习以234树来学习(1)插入操作

    ** 前言: 234树在大部分的程序语言中实现比较困难,所以等价的用红黑树来实现. 核心思想:红黑树的红色节点上移到父亲节点就变成了一颗234树. 一:红黑树与234树的等价关系如下: 一个234树可 ...

  9. LLRB——红黑树的现代实现

    一.本文内容 以一种简明易懂的方式介绍红黑树背后的逻辑实现2-3-4树,以及红黑树的插入.删除操作,重点在2-3-4树与红黑树的对应关系上,并理清红黑树相关操作的来龙去脉.抛弃以往复杂的实现,而分析红 ...

最新文章

  1. 4000个“不会数学”的程序员出现大反转!居然能学AI,玩算法,搞逻辑!背后原因首次曝光...
  2. Android允许应用程序使用Http明文网络传输
  3. antd Datepicker组件报错 ——date.clone is not a function或者date1.isAfter is not a function
  4. 美团NLP中心算法实习生招聘
  5. kettle工具的设计原则
  6. 在腾讯云服务器上实现java web项目部署
  7. 实战Fibre Channel之六: 发起端和目的端常用命令
  8. Spring的核心思想,依赖注入
  9. 关于保留小数点后几位数字之我见
  10. python画图时常用的颜色——color=‘ ’
  11. 安卓系统修改开机LOGO
  12. SPSS实现单因素方差分析
  13. Multi-Object Trackers
  14. 程序媛的2013总结以及2014展望
  15. 一位37岁被裁技术高管给你提个醒:在职场,这件事越早做越好
  16. 中标麒麟5.0安装(内含安装包)-小白手把手史上最全教程!
  17. ModelAndView返回mav时,报404
  18. 从头开始做一个智能家居设备:MQTT协议及使用
  19. 咕泡p6java架构师五期涨薪班
  20. Python3.6+PyQT5+Pyserial 实现简单串口助手

热门文章

  1. Entity Framework CodeFirst For Oracle[转]
  2. 使用GPS实时记录运动路线
  3. 计算机能思考吗?图1专题6:“人脑是计算机吗?”
  4. 聚焦和增强卷积神经网络
  5. suse11.3下samba服务的配置
  6. 【Camera专题】Qcom-高通OTP完全调试指南-上
  7. Datawhale编程——动态规划DP
  8. 【前端大概一分钟】css隐藏滚动条同时可以滚动
  9. C# 笔记 .net与C#简单说明
  10. 【设计模式系列】OO设计原则之LSP-Liskov替换原则