数据结构之红黑树(一)
为什么是要使用红黑树?
二叉树在平均情况下的运行时间已经是对数级别,为什么还要使用红黑树?原因很简单,二叉树在最坏情况下的查找时间为线性级别,导致这种最坏情况的原因是数据的插入找特定的顺序(从大到小或者从小到大),致使数的高度过大,也就是树的不平衡,因而导致了查询效率的下降。
因此,改变这种最差情况的查找效率就是使不平衡的树变得平衡。通过一种2-3的查找树可以使树保持平衡,但是处理的情况过于多,实现比较复杂。
红黑树恰能通过添加节点的颜色来模拟这种2-3树。红黑树与普通二叉树的最大区别在于 ,它在原来的基础上为每个节点设立了颜色。红黑树应该满足如下特性:
1)红色节点均为左节点
2)红色节点不能和红色节点相连接
3)该树是完美黑色平衡,即任意空链接到根节点所经历的黑色节点的数量相同。
这样以来,仅仅改变树的构造方法,如添加节点和删除节点,而查找以及其他方法均与普通二叉查找树相同。
不过,红黑树的左旋、右旋和颜色变换等相关方法需要认证理解。
public class RedBlackBST<Key extends Comparable<Key>,Value> { private Node root;//红黑树的根节点 private static final boolean RED = true; private static final boolean BLACK = false; private class Node { Key key; Value value; Node left; Node right; boolean color;//为没个节点设置颜色 public Node(Key key, Value value, boolean color) { super(); this.key = key; this.value = value; this.color = color; } } private boolean isRed(Node node) { if(node == null) { return false; } return node.color; } /** * 添加节点 * @param key * @param value */ public void put(Key key,Value value) { root = put(root,key,value); root.color = BLACK; } /** * 向以h为根节点的子树中添加元素 */ private Node put(Node h, Key key, Value value) { if(h == null) {//递归到树的底部,创建新新节点,返回给上层递归函数,赋值给父节点 return new Node(key,value,RED); } //添加元素与二叉树添加元素相同 int cmp = key.compareTo(h.key); if(cmp < 0) { h.left = put(h.left,key,value); }else if(cmp > 0) { h.right = put(h.right,key,value); }else { h.value = value; } /** * 二叉树的变换 * 这是红黑树区别于二叉树的地方,添加完元素,对数进行变换。 * 对于不满足红黑树要求的地方进行变换。 * 主要有以下三种情况,顺序不能改变 */ if(isRed(h.right) && !isRed(h.left)) {//情况1:右红左不红,右旋转 h = rotateLeft(h); } if(isRed(h.left) && isRed(h.left.left)) {//情况2:左红,左左红,先右旋转,旋转完后满足情况3,进行颜色变换 h = rotateRight(h); } if(isRed(h.left) && isRed(h.right)) {//情况3:右红左红,变换颜色 flipColor(h); } return h; } /** *变化颜色 * @param h */ private void flipColor(Node h) { h.color = RED;//父节点变为红色 h.left.color = BLACK;//子节点变为黑色 h.right.color = BLACK;//子节点变为黑色 } /** * 右旋转 * @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; } /** * 左旋转 * @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; return h; } }
对于树的删除操作,在后续博文中介绍。