赞
踩
红黑树本身并不复杂,只是在插入删除的时候情况比较多,如果强行记忆的话会显得比较困难,而且容易忘记。所以以前对红黑树一直没有很好的掌握。恰好这次借着复习数据结构的机会,静下心来仔细的学习了一下红黑树,并用Java实现了一番。所以用这篇文章把我对红黑树的操作的理解记录下来,在理解的基础上记忆会容易得多,这样以后就不用重复学习啦!
红黑树是一颗二叉查找树,且具有如下特性:
(1) 每个节点或者是黑色,或者是红色。
(2) 根节点是黑色。
(3) 每个叶子节点是黑色。 [注意:这里叶子节点,是指为空的叶子节点!]
(4) 如果一个节点是红色的,则它的子节点必须是黑色的。
(5) 从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑色节点。
通过上面的定义,可以看到红黑树本质上还是一颗二叉查找树,所以,对红黑树的插入删除操作都可以分为两阶段来完成,首先,将红黑树看成一颗普通的二叉查找树完成插入删除操作,然后,通过旋转以及颜色调整来使得操作后的树满足红黑树的所有特性即可。
旋转操作是基础操作,后面的一些情况处理就是基于旋转来完成的。所以,首先我们需要理解左旋右旋的工作方式。
对x点进行右旋:
对y点进行左旋:
说明:以下的讨论,在如下前提下进行:插入的节点为S(son),S的父亲节点为F(father),F的父亲节点为G(grandfather),而F的兄弟节点为U(uncle)。并且F为G的左儿子。(F为G的右儿子对称操作即可)
Step 1:执行二叉搜索树插入逻辑
Step 2:将插入的节点着色为红色
我们希望这次插入尽可能不破坏红黑树的性质,这样我们就可以不用进行额外的调整操作!所以,根据特性(5),我们显然不能将节点涂成黑色,因为这样所有经过点S的路径的黑色节点都多了一个,就破坏了特性(5),所以我们将S的颜色首先着色为红。
Step 3:进行适当得旋转以及颜色调整
如果插入节点的父节点是红,那么违反了特性(4),所以需要进行调整,共有三个case
说明:S的父亲节点为P,S的兄弟节点为B,B的左儿子为BL,B的右儿子为BR,且S为P的左儿子。(S为P的右儿子对称操作即可)
二叉查找树的删除操作相对来说还是比较复杂,涉及到替代节点的选择等一些情况,如果不了解这一过程的可以自己去百度一下。我们假设想要删除的节点是X,找到的替代节点为Y(选择方式是X的右子树的最小点),那么我们可以将Y的值拷贝到X,然后将这个Y删除,将Y删除,也会需要有一个人顶替现在的Y的位置,我们设为S,接下来的讨论就会围绕着S展开。
由于我们将Y删除了,并且用S代替了Y的位置,如果Y原来是红色的,那么并不会破坏红黑树的颜色平衡,我们不需要额外的操作,删除成功!如果Y是黑色的,那么相当于经过Y的路径(现在是经过S)都会少了一个黑。那么我们怎么把这个黑弥补回来呢?那就是给S再加上一个黑,用来弥补删除Y而损失的那个黑。这时候,相当于S节点拥有了两个颜色,违反了特性(1),所以,我们现在想办法把这个多出来的黑色删除掉!
如果原来S是红色的,那么我们直接不要S的红色,将S设置为黑色即可,因为丢弃红色是不会打破红黑树的颜色平衡的,我们这一操作时安全的。可是,如果S原来就是黑色呢?那就要看S是不是根了,如果是根,将黑色丢弃了也没啥,相当于所有的路径都减少了一个黑,也是不影响平衡的,最糟糕的情况在于S原来时黑,且不是根,这时候,我们就要进行一些旋转以及颜色调整来恢复红黑树的特性了!
step 3旋转以及颜色调整
共有四个case:
节点:
package RBTree; import java.util.Objects; public class RBTreeNode { private final boolean RED = false; private final boolean BLACK = true; private int key; private boolean color; private RBTreeNode left; private RBTreeNode right; private RBTreeNode parent; public RBTreeNode(int key) { this.key = key; this.color = RED; } public int getKey() { return key; } public void setKey(int key) { this.key = key; } public boolean getColor() { return color; } public void setColor(boolean color) { this.color = color; } public RBTreeNode getLeft() { return left; } public void setLeft(RBTreeNode left) { this.left = left; } public RBTreeNode getRight() { return right; } public void setRight(RBTreeNode right) { this.right = right; } public RBTreeNode getParent() { return parent; } public void setParent(RBTreeNode parent) { this.parent = parent; } @Override public String toString() { return "RBTreeNode{" + ",key=" + key + ", color=" + color + '}'; } }
树:
package RBTree; public class RBTree { RBTreeNode root; private final boolean RED = false; private final boolean BLACK = true; public RBTreeNode query(int key) { RBTreeNode tmp = root; while (tmp != null) { if (tmp.getKey() == key) return tmp; else if (tmp.getKey() > key) tmp = tmp.getLeft(); else tmp = tmp.getRight(); } return null; } public void insert(int key) { RBTreeNode node = new RBTreeNode(key); if (root == null) { root = node; node.setColor(BLACK); return; } RBTreeNode parent = root; RBTreeNode son = null; if (key <= parent.getKey()) { son = parent.getLeft(); } else { son = parent.getRight(); } //find the position while (son != null) { parent = son; if (key <= parent.getKey()) { son = parent.getLeft(); } else { son = parent.getRight(); } } if (key <= parent.getKey()) { parent.setLeft(node); } else { parent.setRight(node); } node.setParent(parent); //fix up insertFix(node); } private void insertFix(RBTreeNode node) { RBTreeNode father, grandFather; while ((father = node.getParent()) != null && father.getColor() == RED) { grandFather = father.getParent(); if (grandFather.getLeft() == father) { //F为G左儿子的情况,如之前的分析 RBTreeNode uncle = grandFather.getRight(); if (uncle != null && uncle.getColor() == RED) { setBlack(father); setBlack(uncle); setRed(grandFather); node = grandFather; continue; } if (node == father.getRight()) { leftRotate(father); RBTreeNode tmp = node; node = father; father = tmp; } setBlack(father); setRed(grandFather); rightRotate(grandFather); } else { //F为G的右儿子的情况,对称操作 RBTreeNode uncle = grandFather.getLeft(); if (uncle != null && uncle.getColor() == RED) { setBlack(father); setBlack(uncle); setRed(grandFather); node = grandFather; continue; } if (node == father.getLeft()) { rightRotate(father); RBTreeNode tmp = node; node = father; father = tmp; } setBlack(father); setRed(grandFather); leftRotate(grandFather); } } setBlack(root); } public void delete(int key) { delete(query(key)); } private void delete(RBTreeNode node) { if (node == null) return; if (node.getLeft() != null && node.getRight() != null) { RBTreeNode replaceNode = node; RBTreeNode tmp = node.getRight(); while (tmp != null) { replaceNode = tmp; tmp = tmp.getLeft(); } int t = replaceNode.getKey(); replaceNode.setKey(node.getKey()); node.setKey(t); delete(replaceNode); return; } RBTreeNode replaceNode = null; if (node.getLeft() != null) replaceNode = node.getLeft(); else replaceNode = node.getRight(); RBTreeNode parent = node.getParent(); if (parent == null) { root = replaceNode; if (replaceNode != null) replaceNode.setParent(null); } else { if (replaceNode != null) replaceNode.setParent(parent); if (parent.getLeft() == node) parent.setLeft(replaceNode); else { parent.setRight(replaceNode); } } if (node.getColor() == BLACK) removeFix(parent, replaceNode); } //多余的颜色在node里 private void removeFix(RBTreeNode father, RBTreeNode node) { while ((node == null || node.getColor() == BLACK) && node != root) { if (father.getLeft() == node) { //S为P的左儿子的情况,如之前的分析 RBTreeNode brother = father.getRight(); if (brother != null && brother.getColor() == RED) { setRed(father); setBlack(brother); leftRotate(father); brother = father.getRight(); } if (brother == null || (isBlack(brother.getLeft()) && isBlack(brother.getRight()))) { setRed(brother); node = father; father = node.getParent(); continue; } if (isRed(brother.getLeft())) { setBlack(brother.getLeft()); setRed(brother); rightRotate(brother); brother = brother.getParent(); } brother.setColor(father.getColor()); setBlack(father); setBlack(brother.getRight()); leftRotate(father); node = root;//跳出循环 } else { //S为P的右儿子的情况,对称操作 RBTreeNode brother = father.getLeft(); if (brother != null && brother.getColor() == RED) { setRed(father); setBlack(brother); rightRotate(father); brother = father.getLeft(); } if (brother == null || (isBlack(brother.getLeft()) && isBlack(brother.getRight()))) { setRed(brother); node = father; father = node.getParent(); continue; } if (isRed(brother.getRight())) { setBlack(brother.getRight()); setRed(brother); leftRotate(brother); brother = brother.getParent(); } brother.setColor(father.getColor()); setBlack(father); setBlack(brother.getLeft()); rightRotate(father); node = root;//跳出循环 } } if (node != null) node.setColor(BLACK); } private boolean isBlack(RBTreeNode node) { if (node == null) return true; return node.getColor() == BLACK; } private boolean isRed(RBTreeNode node) { if (node == null) return false; return node.getColor() == RED; } private void leftRotate(RBTreeNode node) { RBTreeNode right = node.getRight(); RBTreeNode parent = node.getParent(); if (parent == null) { root = right; right.setParent(null); } else { if (parent.getLeft() != null && parent.getLeft() == node) { parent.setLeft(right); } else { parent.setRight(right); } right.setParent(parent); } node.setParent(right); node.setRight(right.getLeft()); if (right.getLeft() != null) { right.getLeft().setParent(node); } right.setLeft(node); } private void rightRotate(RBTreeNode node) { RBTreeNode left = node.getLeft(); RBTreeNode parent = node.getParent(); if (parent == null) { root = left; left.setParent(null); } else { if (parent.getLeft() != null && parent.getLeft() == node) { parent.setLeft(left); } else { parent.setRight(left); } left.setParent(parent); } node.setParent(left); node.setLeft(left.getRight()); if (left.getRight() != null) { left.getRight().setParent(node); } left.setRight(node); } private void setBlack(RBTreeNode node) { if(node != null) { node.setColor(BLACK); } } private void setRed(RBTreeNode node) { if(node != null) { node.setColor(RED); } } public void inOrder() { inOrder(root); } private void inOrder(RBTreeNode node) { if (node == null) return; inOrder(node.getLeft()); System.out.println(node); inOrder(node.getRight()); } }
我从这里:https://www.cnblogs.com/skywang12345/p/3624343.html学到了很多的思想,不过,他的图有好些画错了。
这里:http://www.cnblogs.com/geekma/archive/2012/06/27/2566226.html 有一个完整的红黑树操作过程的图示,可以当作很好的测试用例,看看自己的代码是否写对了。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。