(本讲内容属于算法第四版,未参照算法导论上的内容)。
平衡查找树之二三树(http://blog.csdn.net/caipeichao2/article/details/30089151)
平衡查找树的目标是实现查找、插入、删除操作在最坏情况下的复杂度均为logN。
本节将介绍二三查找树。
二三树中有两种节点:
-
二节点对应一个键,有两个子节点
-
三节点对应两个键,有三个子节点
二三查找树非常平衡,每个空节点到根节点的距离都是一样的 。
查找操作
在二三树中查找一个键的时候有以下规则:
-
如果是二节点,二节点对应1个值,如果要查找的值比该节点对应的值小,就往左侧深入,反之亦成
-
如果是三节点,三节点对应2个值,如果比两个值都小,就往左侧深入,如果介于两个值之间,就往中间深入,如果比两个值都大,就往右侧深入。
插入操作
根据查找操作的规则,先定位到需要插入的节点。如果是二节点,那么将二节点中增加一个键成为三节点。如果是三节点,在三节点中增加1个键成为四节点。由于四节点不允许在二三树中出现,因此需要分解成两个二节点,并且把中间的键提取到父节点中。下图展示四节点分解的过程:
现在要在这棵树中插入一个值7
首先根据查找操作的规则定位到要插入的节点,定位之后是如图所示的节点
由于该节点是三节点,因此插入一个键,使它成为四节点
由于四节点不允许在2-3树中存在,因此需要将其分解为两个二节点,并把中间的键7提到父节点中
这样插入操作就完成了
性能
2-3树的高度介于lgN和log_3(N)之间,因此能够保证所有的操作复杂度均在logN以下
实现
二三树的实现非常复杂,因为要判断每个节点的类型,插入节点的时候还需要判断插入节点的位置,需要考虑的情况非常多。
红黑树就是将二三树表示成二叉树的形式,极大地简化了算法。
红黑树的基本思想就是将二三树中的三节点表示成两个二节点,而这两个二节点之间使用红色的连接,普通连接使用黑色的连接。
红黑树中的每个节点都有以下性质:
-
没有一个节点同时拥有两个红连接
-
每个空节点到根节点路径上黑色连接的数量都是相同的
-
红连接只会出现在左边
下图展示了二三树和红黑树的等价表示
查找操作
与普通的二叉树查找树的算法一致,忽略节点的颜色即可。
旋转操作
有时候会出现下图红连接在右侧的情况。这时候就需要通过左旋操作,使其符合红黑树的性质。
下图展示了旋转之后的样子
代码如下:
- private Node rotateLeft(Node node) {
- Node right = node.right;
- node.right = right.left;
- right.left = node;
- int color = right.color;
- right.color = node.color;
- node.color = color;
- return right;
- }
有时候也会用到右旋操作。右旋操作的目的是为了方便某些操作,等操作结束之后再通过左旋恢复原样。
翻转操作
有时候会出现下图这种双重红连接的情况,这种情况对应的就是二三树中的四节点。此时之需要改变父节点的颜色即可。这种操作叫做翻转操作。
下图是变化之后的图
代码如下:
- private Node flipColor(Node node) {
- node.color = RED;
- node.left.color = BLACK;
- node.right.color = BLACK;
- return node;
- }
插入操作
在红黑树中进行插入操作时,与普通的二叉查找树一样。每次新增一个子节点时,需要将新增的节点标记成红色。再通过旋转、翻转操作,维持红黑树的性质。
每次插入之后,需要执行以下步骤:
-
如果右子节点是红色的,左子节点是黑色的,执行左旋操作
-
如果左子节点和它的子节点都是红的,执行右旋操作
-
如果两个子节点都是红色的,执行翻转操作
代码如下:
- private Node put(Node node, Key key, Value value) {
- // 创建一个新的红色的节点
- if(node == null) {
- return new Node(key, value, RED);
- }
- // 定位到需要插入的节点
- int compare = key.compareTo(node.key);
- if(compare < 0) {
- node.left = put(node.left, key, value);
- } else if(compare > 0) {
- node.right = put(node.right, key, value);
- } else {
- node.value = node.value;
- return node;
- }
- // 调整红黑树,使其平衡
- if(isRed(node.right) && !isRed(node.left)) {
- return rotateLeft(node);
- }
- if(isRed(node.left) && isRed(node.left.left)) {
- return rotateRight(node);
- }
- if(isRed(node.left) && isRed(node.right)) {
- return flipColor(node);
- }
- return node;
- }
性能
红黑树的插入、查找、删除操作的复杂度均为logN。
完整代码
- public class RedBlackTree<Key extends Comparable<Key>, Value> {
- private static final int RED = 1;
- private static final int BLACK = 0;
- private class Node {
- Key key;
- Value value;
- Node left;
- Node right;
- int color;
- Node(Key key, Value value, int color) {
- this.key = key;
- this.value = value;
- this.color = color;
- }
- }
- private Node root;
- public void insert(Key key, Value value) {
- root = put(root, key, value);
- }
- private Node put(Node node, Key key, Value value) {
- // 创建一个新的红色的节点
- if(node == null) {
- return new Node(key, value, RED);
- }
- // 定位到需要插入的节点
- int compare = key.compareTo(node.key);
- if(compare < 0) {
- node.left = put(node.left, key, value);
- } else if(compare > 0) {
- node.right = put(node.right, key, value);
- } else {
- node.value = node.value;
- return node;
- }
- // 调整红黑树,使其平衡
- if(isRed(node.right) && !isRed(node.left)) {
- return rotateLeft(node);
- }
- if(isRed(node.left) && isRed(node.left.left)) {
- return rotateRight(node);
- }
- if(isRed(node.left) && isRed(node.right)) {
- return flipColor(node);
- }
- return node;
- }
- private boolean isRed(Node node) {
- // 空节点属于黑节点
- if(node == null) return false;
- // 判断节点是否为红色
- return node.color == RED;
- }
- private Node rotateLeft(Node node) {
- Node right = node.right;
- node.right = right.left;
- right.left = node;
- int color = right.color;
- right.color = node.color;
- node.color = color;
- return right;
- }
- private Node rotateRight(Node node) {
- Node left = node.left;
- node.left = left.right;
- left.right = node;
- int color = left.color;
- left.color = node.color;
- node.color = color;
- return left;
- }
- private Node flipColor(Node node) {
- node.color = RED;
- node.left.color = BLACK;
- node.right.color = BLACK;
- return node;
- }
- public Value get(Key key) {
- Node node = root;
- while(node != null) {
- int compare = key.compareTo(node.key);
- if(compare < 0) {
- node = node.left;
- } else if(compare > 0) {
- node = node.right;
- } else {
- return node.value;
- }
- }
- // 没有找到
- return null;
- }
- }