简介
红黑树(Red Black Tree) 是一种自平衡二叉查找树,是在计算机科学中用到的一种数据结构,典型的用途是实现关联数组。
它是在1972年由Rudolf Bayer发明的,当时被称为平衡二叉B树(symmetric binary B-trees)。后来,在1978年被 Leo J. Guibas 和 Robert Sedgewick 修改为如今的“红黑树”。
红黑树和AVL树类似,都是在进行插入和删除操作时通过特定操作保持二叉查找树的平衡,从而获得较高的查找性能。
它虽然是复杂的,但它的最坏情况运行时间也是非常良好的,并且在实践中是高效的: 它可以在O(log n)时间内做查找,插入和删除,这里的n 是树中元素的数目。
它的统计性能要好于平衡二叉树(有些书籍根红黑树据作者姓名,Adelson-Velskii和Landis,将其称为AVL-树),因此,红黑树在很多地方都有应用。在C++ STL中,很多部分(包括set, multiset, map, multimap)应用了红黑树的变体(SGI STL中的红黑树有一些变化,这些修改提供了更好的性能,以及对set操作的支持)。其他平衡树还有:AVL,SBT,伸展树,TREAP 等等。
红黑树是每个节点都带有颜色属性的二叉查找树,颜色或红色或黑色。在二叉查找树强制一般要求以外,对于任何有效的红黑树我们增加了如下的额外要求:
性质1. 节点是红色或黑色。
性质2. 根节点是黑色。
性质3 每个叶节点(NIL节点,空节点)是黑色的。
性质4 每个红色节点的两个子节点都是黑色。(从每个叶子到根的所有路径上不能有两个连续的红色节点)
性质5. 从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点。
这些约束强制了红黑树的关键性质: 从根到叶子的最长的可能路径不多于最短的可能路径的两倍长。结果是这个树大致上是平衡的。
因为操作比如插入、删除和查找某个值的最坏情况时间都要求与树的高度成比例,这个在高度上的理论上限允许红黑树在最坏情况下都是高效的,而不同于普通的二叉查找树。
要知道为什么这些特性确保了这个结果,注意到性质4导致了路径不能有两个毗连的红色节点就足够了。最短的可能路径都是黑色节点,最长的可能路径有交替的红色和黑色节点。因为根据性质5所有最长的路径都有相同数目的黑色节点,这就表明了没有路径能多于任何其他路径的两倍长。
其在jdk中经典的使用在于TreeMap, jdk1.8 中对于HashMap加入了红黑树相关的优化 [如果某个桶中的记录过 大的话(当前是TREEIFY_THRESHOLD = 8),HashMap会动态的使用一个专门的treemap实现来替换掉它。这样做的结果会更好,是O(logn),而不是糟糕的O(n)。]
其实对于这个数据结构, 我现在就只记得他的添加元素, 删除元素需要维护其性质的话, 情况比较多, 比较复杂, 还有其适合于查找[给定的元素, 比给定元素大的最小元素, 比给定元素小的最大的元素] 或者 以及需要将输入构造为有序的序列的场景
下面的参考代码是参考 “” 的来编写的
至于 其他的介绍, 这里就不介绍了, 因为有很多相关的播客嘛, 这个当时可是花了我三四天的时间啊。。
参考中 也有一些不错的播客,
jdk 中TreeMap的实现是相当简洁优雅的。。
参考代码
/**
* file name : Test18BRTree.java
* created at : 9:12:45 PM Jun 1, 2015
* created by
*/
package com.hx.test04;
import com.hx.util.Log;
import com.hx.util.Tools;
public class Test18BRTree {
// 红黑树
// 参考 :
public static void main(String []args) {
// int[] arr = new int[] {34, 18, 28, 22, 32, 38, 27, 19, 17, 11, 38, 37, 48, 48, 6 };
int[] arr = new int[] {34, 18, 28, 22 };
BlackRedTree brt = new BlackRedTree();
for(int i=0; i<arr.length; i++) {
brt.add(arr[i]);
}
Log.log(brt);
}
// 红黑树
// 1.节点是红色或黑色。
// 2.根是黑色。
// 3.所有叶子(外部节点)都是黑色。
// 4.每个红色节点的两个子节点都是黑色。(从每个叶子到根的所有路径上不能有两个连续的红色节点)
// 5.从每个叶子到根的所有路径都包含相同数目的黑色节点。
static class BlackRedTree {
// 根节点
Node root;
// 初始化
public BlackRedTree() {
root = null;
}
// 添加一个元素 先插入在检查是否需要转动结点
// 如果root为null 则说明该树还没有结点 新建一个root结点, 设置其颜色为BLACK
// 否则 插入一个结点 在检查是否 需要扭转结点 来使得满足其性质
public void add(int val) {
if(root == null) {
root = new Node();
root.setColor(Node.BLACK);
root.val = val;
} else {
Node node = root;
Node newNode = insert(val);
fixAfterInsert01(newNode);
// fixAfterInsert02(newNode);
}
}
public void remove(int val) {
Node[] tmp = delete(val);
// 如果没有找到对应值的结点, 或者该节点为root 并且没有左右孩子
// 不知道delete中对tmp的控制 和这里的确保deleted不为叶子结点的控制是否有问题, 一会儿再来看看吧
if(tmp != null) {
fixAfterDelete01(tmp[0], tmp[1]);
}
}
// 移除结点node
// 方式二 : 将node结点的左子树的最大结点[或者右子树的最小节点] 替换掉node, 然后该结点的左子树接到该节点的父节点上
private Node remove1(Node node) {
if(isLeft(node) ) {
return removeForNode1Left(node);
} else {
return removeForNode1Right(node);
}
}
// 删除root结点 方式一 : 找出root结点左孩子的最大值 替换掉root结点, 然后该结点的左子树接到该节点的父节点上
private Node removeForRoot0(Node node) {
return removeForNode1Left(root);
}
// 删除root结点 方式二 : 将root结点的右子树的最小结点 替换掉root, 然后该结点的右子树接到该节点的父节点上
private Node removeForRoot1(Node node) {
return removeForNode1Right(root);
}
// 删除结点 方式二[Left] : 找出左孩子的最大值 替换掉删除结点, 然后该结点的左子树接到该节点的父节点上
private Node removeForNode1Left(Node node) {
Node nodeLeftMax = getMaxNode(node.left);
node.val = nodeLeftMax.val;
if(nodeLeftMax.parent == node) {
node.left = nodeLeftMax.left;
if(nodeLeftMax.left != null) {
nodeLeftMax.left.parent = node;
}
} else {
nodeLeftMax.parent.right = nodeLeftMax.left;
if(nodeLeftMax.left != null) {
nodeLeftMax.left.parent = nodeLeftMax.parent;
}
}
return nodeLeftMax;
}
// 删除root结点 方式二[Right] : 将node结点的右子树的最小结点 替换掉node, 然后该结点的右子树接到该节点的父节点上
private Node removeForNode1Right(Node node) {
Node nodeRightMin = getMinNode(node.right);
node.val = nodeRightMin.val;
if(nodeRightMin.parent == node) {
node.right = nodeRightMin.right;
if(nodeRightMin.right != null) {
nodeRightMin.right.parent = node;
}
} else {
nodeRightMin.parent.left = nodeRightMin.right;
if(nodeRightMin.right != null) {
nodeRightMin.right.parent = nodeRightMin.parent;
}
}
return nodeRightMin;
}
// 获取node结点的最大子节点
private Node getMaxNode(Node node) {
Node tmp = node;
while(tmp.right != null) {
tmp = tmp.right;
}
return tmp;
}
// 获取node结点的最小子节点
private Node getMinNode(Node node) {
Node tmp = node;
while(tmp.left != null) {
tmp = tmp.left;
}
return tmp;
}
// 遍历树 获取val对应的结点
public Node get(int val) {
Node node = root;
while(node != null) {
if(val < node.val ) {
node = node.left;
} else if(val > node.val ) {
node = node.right;
} else {
return node;
}
}
return null;
}
// 检查是否需需要扭转结点 来满足其性质
// 令cur为加入的结点, parent为加入结点的父节点, gParent为加入节点的祖父节点, uncle为cur的舅舅结点[父节点的兄弟结点]
// 如果cur结点不是root 并且是红色
// 如果parent 是黑色的 则直接返回, 插入当前结点 没有破坏红黑树的性质
// 否则 这时候就说明了gParent节点不为空[因为parent节点为红色 不可能是root结点]
// 这是候 获取uncle结点
// 如果uncle结点为红色的时候[uncle结点为空 视为黑色] 设置gParent结点为红色, uncle, parent结点为黑色, 令更新cur为gParent结点 继续下一次的循环
// 如果uncle结点为 黑色 这时候有四种情况[因为uncle为黑色 调整之后 cur结点为black 所以会跳出while]
// 1) 如果parent结点是gParent结点的左结点 并且cur结点是parent结点的左结点 这时候将parent结点右旋转 并设置parent为黑色, 设置parent的两个子节点为红色
// 2) 如果parent结点是gParent结点的右结点 并且cur结点是parent结点的右结点 这时候将parent结点左旋转 并设置parent为黑色, 设置parent的两个子节点为红色
// 3) 如果parent结点是gParent结点的左结点 并且cur结点是parent结点的右结点 这时候先将cur结点左旋转, 在讲cur结点右旋转 并设置cur为黑色, 设置cur的两个子节点为红色
// 4) 如果parent结点是gParent结点的右结点 并且cur结点是parent结点的左结点 这时候先将cur结点右旋转, 在讲cur结点左旋转 并设置cur为黑色, 设置cur的两个子节点为红色
// 参考 : http://blog.163.com/fulei.20030727@126/blog/static/300662212007112205334389/
private void fixAfterInsert01(Node newNode) {
Node parent, gParent;
Node cur = newNode;
//we should fix until it is black colored
while(cur != root && isRed(cur) ) {
parent=cur.parent;
if(parent.isBlack() ) {
//that's fine, the insertion will not change any black height, and will not violate any rule.
return;
}
gParent = parent.parent;//we can assume the p is not null now.
Node uncle = getUncle(parent);
if(uncle != null && uncle.isRed() ) {
//re-coloring
gParent.setColor(Node.RED);
uncle.setColor(Node.BLACK);
parent.setColor(Node.BLACK);
//now the g maybe conflict with its parent;
cur = gParent;
} else {
if(isLeft(parent) ) {
if(isRight(cur)) {
//this case we should do left rotation, and then it will transform to next case
leftRotate(cur);
//transformed to next case
// cur = cur.left;
} else {
cur = parent;
}
rightRotate(cur);
} else {
if(isLeft(cur)) {
//this case we should do right rotation, and then it will transform to next case
rightRotate(cur);
//transformed to next case
// cur = cur.right;
} else {
cur = parent;
}
leftRotate(cur);
}
cur.setColor(Node.BLACK);
setNodeToColorIfNotNull(cur.left, Node.RED);
setNodeToColorIfNotNull(cur.right, Node.RED);
}
}
root.setColor(Node.BLACK);
}
// 思路 和上面基本一致, 不过这里面部分代码是 我之前自己敲的, 而上面是 参考网上一位网友的作品
// 这个算法 我是没有完成的, 是参考过上面的代码之后 改进的
// 我的主要的漏洞在于 对于叔叔结点为红色的处理,我不知要需要循环/ 递归 处理, 所以我当时的处理方式 就是直接旋转gParent结点了,,, 擦 没有参考别人的东西, 要思考多久才能想出来....
private void fixAfterInsert02(Node newNode) {
while(newNode != root && isRed(newNode)) {
Node last = newNode.parent;
// 如果 加入结点的父节点为黑色 说明满足BRTree的条件
if(last.isBlack() ) {
break;
} else {
Node uncle = getUncle(last);
// 如果叔叔结点时红色
if(uncle != null && uncle.isRed()) {
last.parent.setColor(Node.RED);
last.setColor(Node.BLACK);
uncle.setColor(Node.BLACK);
newNode = last.parent;
// 更新gParent, parent, uncle结点的颜色 并旋转gParent结点 [思路就错了,,,]
// Node lpParent = last.parent.parent;
//
// if(lpParent != null) {
// last.transColor();
// last.parent.transColor();
Node uncle = getUncle(last);
// if(uncle != null) {
// uncle.transColor();
// }
//
// lpParent.transColor();
// lpParent.parent.transColor();
// if(isLeft(lpParent) ) {
// if(lpParent.parent != null) {
// transColorForAllSubTree(lpParent.parent.right);
// }
// rightRotate(lpParent);
// } else {
// if(lpParent.parent != null) {
// transColorForAllSubTree(lpParent.parent.left);
// }
// leftRotate(lpParent);
// }
// } else {
// if(last.isRed) {
// newNode.setColor(Node.BLACK);
// }
// }
// useless 当现在 root结点的左子树和右子树 深度相差大于1的时候 将其深度大的一方向深度小的一方旋转..
// int maxLeft = this.getMaxDepth(root.left, 0);
// int maxRight = this.getMaxDepth(root.right, 0);
Log.log(maxLeft, maxRight);
// if(maxLeft > maxRight + 1) {
// root.transColor();
// root.left.transColor();
// rightRotate(root.left);
// } else if(maxRight > maxLeft + 1) {
// root.transColor();
// root.right.transColor();
// leftRotate(root.right);
// }
// 如果叔叔结点时黑色
} else {
if(isLeft(last) && isLeft(newNode) ) {
rightRotate(last);
newNode = last;
} else if(isRight(last) && isRight(newNode) ) {
leftRotate(last);
newNode = last;
} else if(isLeft(last) && isRight(newNode) ) {
leftRotate(newNode);
rightRotate(newNode);
} else {
rightRotate(newNode);
leftRotate(newNode);
}
newNode.setColor(Node.BLACK);
setNodeToColorIfNotNull(newNode.left, Node.RED);
setNodeToColorIfNotNull(newNode.right, Node.RED);
}
}
// end of while
}
root.setColor(Node.BLACK);
}
// 删除结点之后的调整操作
// 如果删除结点是红色 则不影响性质
// 否则 deleted 结点必然只有一个孩子结点 或者没有子节点[详见delete方法]
// 获取deleted 结点的子节点
// 如果其 没有子节点 则加入一个临时结点 该临时结点的父节点指向deleted的父节点[主要是为了之后的调整操作占一个位置]
// 然后进行调整根据son结点是parent结点的左孩子 或者右孩子进行调整
// 最后 如果添加了临时结点 则 删除临时结点
private void fixAfterDelete01(Node tar, Node deleted) {
if(deleted.isRed()) {
return ;
} else {
Node son = null;
boolean isLeft = false;
boolean isSonExists = false;
son = getSonIfOnlyOneNode(deleted);
if(son != null) {
isSonExists = true;
isLeft = isLeft(son);
// 处理删除结点没有孩子的情况 添加一个临时结点
} else {
Node tmpNode = new Node(deleted.parent, -1);
tmpNode.setColor(Node.BLACK);
isLeft = deleted.val < deleted.parent.val;
if(isLeft) {
deleted.parent.left = tmpNode;
} else {
deleted.parent.right = tmpNode;
}
son = tmpNode;
}
// 如果删除的结点是黑色 并且其孩子结点是红色, 直接领该孩子结点为黑色 即调整完毕
if(son.isRed() ) {
son.setColor(Node.BLACK);
return ;
}
// Log.log(son);
if(isLeft ) {
fixAfterDelete01Left(son);
} else {
fixAfterDelete01Right(son);
}
// Log.log(son);Log.horizon();
// 如果删除的结点 没有子节点, 则这里最后的时候 删除临时结点
if(!isSonExists) {
if(isLeft ) {
son.parent.left = null;
} else {
son.parent.right = null;
}
}
}
}
// 如果son是parent结点的左结点 调整方案如下
// 先获取parent, sibling结点[相对于son结点]
// 步骤1 : 如果sibling结点是红色 则将sibling结点和parent结点互换颜色, 并对sibling结点进行左旋转操作, 更新sibling结点
// 步骤2 :如果sibling的左右子节点均为黑色[sibling结点为空也算] 设置sibling结点的颜色为黑色, 并调整son结点为其父节点
// 如果现在的son结点为红色, 则说明调整完成 返回
// 否则更新parent, sibling结点 继续循环, 执行步骤1, 步骤2, ...
// 如果son结点回溯到了 root结点 则调整完成
// 步骤3 : 如果sibling的左右子节点不全为黑色, 如果sibling的子节点 左红右黑 将sibling左子结点设置为黑色, 并设置sibling结点的颜色为红色, 并将sibling左子结点右旋转, 更新sibling结点
// 步骤4 :此时sibling结点的右节点必然为红[可能经过步骤3的处理, 也可能没有] 将sibling结点的右子节点设置为黑色, 交换sibling, parent结点的颜色, 在讲sibling结点左旋转, 完成调整
// 参考 :
private void fixAfterDelete01Left(Node son) {
// 接下来的情况是删除结点为黑色 并且son结点为黑色的情况
Node parent = son.parent;
Node sibling = getUncle(son);
while(son != root) {
// step 1 : 如果son的兄弟结点为红色的话 交换sibling和parent的颜色 并将sibling结点左旋转
// 更新sibling结点为son现在的兄弟节点
if(sibling.isRed() ) {
boolean isRed = parent.isRed();
parent.setColor(sibling.isRed() );
sibling.setColor(isRed );
leftRotate(sibling);
sibling = getUncle(son);
}
// step 2 : 如果 sibling的左右结点均为黑色
if(sibling == null || (isBlack(sibling.left) && isBlack(sibling.right)) ) {
sibling.setColor(Node.RED);
son = son.parent;
if(son.isRed() ) {
son.setColor(Node.BLACK);
return ;
}
parent = son.parent;
sibling = getUncle(son);
} else {
break;
}
}
// son 结点回溯到了root结点 直接返回
if(son == root) {
return ;
}
// step 3 : sibling的子节点不是全黑色 而是左红右黑
if(isRed(sibling.left) && isBlack(sibling.right) ) {
setNodeToColorIfNotNull(sibling.left, Node.BLACK);
sibling.setColor(Node.RED);
rightRotate(sibling.left);
sibling = getUncle(son);
}
// step 4 : 处理w右子节点y为红色的情况,此时w的左子节点x可黑可红
setNodeToColorIfNotNull(sibling.right, Node.BLACK);
boolean isRed = parent.isRed();
parent.setColor(sibling.isRed() );
sibling.setColor(isRed );
leftRotate(sibling);
}
// 如果son是parent结点的右结点 调整方案和son为parent结点的左结点的调整是对称的, 所以逻辑基本一致
private void fixAfterDelete01Right(Node son) {
// 接下来的情况是删除结点为黑色 并且son结点为黑色的情况
Node parent = son.parent;
Node sibling = getUncle(son);
while(son != root) {
// step 1 : 如果son的兄弟结点为红色的话 交换sibling和parent的颜色 并将sibling结点左旋转
// 更新sibling结点为son现在的兄弟节点
if(sibling.isRed() ) {
boolean isRed = parent.isRed();
parent.setColor(sibling.isRed() );
sibling.setColor(isRed );
rightRotate(sibling);
sibling = getUncle(son);
}
// step 2 : 如果 sibling的左右结点均为黑色
if(sibling == null || (isBlack(sibling.left) && isBlack(sibling.right)) ) {
sibling.setColor(Node.RED);
son = son.parent;
if(son.isRed() ) {
son.setColor(Node.BLACK);
return ;
}
parent = son.parent;
sibling = getUncle(son);
} else {
break;
}
}
// son 结点回溯到了root结点 这说明调整完成 直接返回
if(son == root) {
return ;
}
// step 3 : sibling的子节点不是全黑色 而是左红右黑
if(isRed(sibling.right) && isBlack(sibling.left) ) {
setNodeToColorIfNotNull(sibling.left, Node.RED);
sibling.setColor(Node.BLACK);
leftRotate(sibling.right);
sibling = getUncle(son);
}
// step 4 : 处理w右子节点y为红色的情况,此时w的左子节点x可黑可红
setNodeToColorIfNotNull(sibling.left, Node.BLACK);
boolean isRed = parent.isRed();
parent.setColor(sibling.isRed() );
sibling.setColor(isRed );
rightRotate(sibling);
}
// 获取deleted结点的子节点 优先获取左子结点
private Node getSonIfOnlyOneNode(Node deleted) {
return deleted.left != null ? deleted.left : deleted.right;
}
// 将node结点视为红色的情况
private boolean isRed(Node node) {
return node != null && node.isRed();
}
// 将node结点视为黑色的情况
private boolean isBlack(Node node) {
return node == null || node.isBlack();
}
// 设置 node结点的颜色为isRed
private void setNodeToColorIfNotNull(Node node, boolean isRed) {
if(node != null) {
node.setColor(isRed);
}
}
// 以二叉排序树的规则 将val插入树中 不做任何其他操作
// 返回 插入的结点
private Node insert(int val) {
Node node = root;
Node last = null;
while(node != null) {
last = node;
if(val < node.val ) {
node = node.left;
} else if(val > node.val ) {
node = node.right;
} else {
Log.log(val + " is already exists...");
return null;
}
}
Node newNode = null;
if(val < last.val) {
newNode = new Node(last, val);
last.left = newNode;
} else {
newNode = new Node(last, val);
last.right = newNode;
}
return newNode;
}
// 删除val对应的结点 返回需要删除的结点 和真正删除的结点
private Node[] delete(int val) {
Node node = get(val);
if(node == null) {
Log.log("val " + val + " is no exist...");
return null;
}
Node[] tmp = new Node[2];
tmp[0] = node; tmp[1] = node;
if(node.parent == null) {
if(isLeaf(node)) {
root = null;
tmp = null;
} else if(node.left != null) {
tmp[1] = removeForRoot0(node);
} else if(node.right != null) {
tmp[1] = removeForRoot1(node);
}
return tmp;
} else {
if(isLeaf(node)) {
if(isLeft(node)) {
node.parent.left = null;
} else {
node.parent.right = null;
}
} else if(node.left != null && node.right != null) {
// remove0(node);
tmp = new Node[2];
tmp[0] = node;
tmp[1] = remove1(node);
} else if(node.left != null) {
if(isLeft(node)) {
node.parent.left = node.left;
node.left.parent = node.parent;
} else {
node.parent.right = node.left;
node.left.parent = node.parent;
}
} else if(node.right != null) {
if(isLeft(node)) {
node.parent.left = node.right;
node.right.parent = node.parent;
} else {
node.parent.right = node.right;
node.right.parent = node.parent;
}
}
return tmp;
}
}
// // 更新整棵子树的结点的颜色
// private void transColorForAllSubTree(Node node) {
// node.transColor();
// if(node.left != null) {
// transColorForAllSubTree(node.left);
// }
// if(node.right != null) {
// transColorForAllSubTree(node.right);
// }
// }
// 右旋转last结点
private void rightRotate(Node last) {
Node lpParent = last.parent.parent;
boolean pIsLeft = false;
if(lpParent != null ) {
pIsLeft = isLeft(last.parent);
}
last.parent.parent = last;
last.parent.left = last.right;
if(last.right != null) {
last.right.parent = last.parent;
}
last.right = last.parent;
last.parent = lpParent;
if(lpParent == null ) {
root = last;
} else {
if(pIsLeft) {
lpParent.left = last;
} else {
lpParent.right = last;
}
}
}
// 左旋转last结点
private void leftRotate(Node last) {
Node lpParent = last.parent.parent;
boolean pIsLeft = false;
if(lpParent != null ) {
pIsLeft = isLeft(last.parent);
}
last.parent.parent = last;
last.parent.right = last.left;
if(last.left != null) {
last.left.parent = last.parent;
}
last.left = last.parent;
last.parent = lpParent;
if(lpParent == null ) {
root = last;
} else {
if(pIsLeft) {
lpParent.left = last;
} else {
lpParent.right = last;
}
}
}
// 获取整棵树的最大深度
public int getMaxDepth() {
return getMaxDepth(root, 0);
}
// assistMethod
private int getMaxDepth(Node node, int depth) {
if(node == null) {
return depth;
}
int maxLeft = -1, maxRight = -1;
if(node.left != null) {
maxLeft = getMaxDepth(node.left, depth+1);
} else {
maxLeft = depth;
}
if(node.right != null) {
maxRight = getMaxDepth(node.right, depth+1);
} else {
maxLeft = depth;
}
return Tools.getMax(maxLeft, maxRight);
}
// 判断 node结点是否是叶子结点
public boolean isLeaf(Node node) {
return node.left == null && node.right == null;
}
// 判断 node结点是否是其父节点的左孩子
public boolean isLeft(Node node) {
return node == node.parent.left;
}
// 判断 node结点是否是其父节点的右孩子
public boolean isRight(Node node) {
return node == node.parent.right;
}
// 获取 node结点的兄弟结点
public Node getUncle(Node node) {
if(node.parent == null) {
return null;
}
return node == node.parent.left ? node.parent.right : node.parent.left;
}
// 中序遍历 获取node的子树的字符串表示[得到的是一个有序序列] 存于sb中
public void headFirstForString(Node node, StringBuilder sb) {
if(node == null) {
sb.append("null");
} else {
if(node.left != null) {
headFirstForString(node.left, sb);
}
sb.append(node.val + " color: " + node.getColor() + " ; ");
// sb.append(node.val + " ");
if(node.right != null) {
headFirstForString(node.right, sb);
}
}
}
// Debug
public String toString() {
StringBuilder sb = new StringBuilder();
headFirstForString(root, sb);
return sb.toString();
}
}
// Node
static class Node {
// red, black的字符串表示
// red, black的颜色表示
private static final String RED_STR = "red";
private static final String BLACK_STR = "black";
private static final boolean RED = true;
private static final boolean BLACK = false;
// val 表示数据, parent表示该节点的父节点, isRed表示是否是红色
// left, right 表示该节点的左孩子, 右孩子
int val;
boolean isRed;
Node parent;
Node left, right;
// 初始化
public Node() {
isRed = RED;
}
public Node(Node parent, int val) {
this();
set(parent, null, null, val);
}
public Node(Node parent, Node left, Node right, int val) {
this();
set(parent, left, right, val);
}
public Node(Node other) {
this.isRed = other.isRed;
set(other.parent, other.left, other.right, other.val);
}
// setter
public void set(Node parent, Node left, Node right, int val) {
this.val = val;
this.parent = parent;
this.left = left;
this.right = right;
}
// 当前结点是否是红色
public boolean isRed() {
return isRed;
}
// 当前结点是否是黑色
public boolean isBlack() {
return !isRed;
}
// 更新当前结点的颜色
public void setColor(boolean isRed) {
this.isRed = isRed;
}
// 更新其颜色 为其他颜色 (红 -> 黑), (黑 -> 红)
public void transColor() {
isRed = !isRed;
}
// 获取当前结点的颜色字符串表示
private String getColor() {
if(isRed) {
return RED_STR;
} else {
return BLACK_STR;
}
}
// Debug
public String toString() {
return "val : " + val + " color : " + getColor();
}
}
}
效果截图
参考
[算法,红黑树] –2015.06.01
[红黑树 非常好]
[红黑树]