MIT算法导论-第10讲-2-3树、红黑树

(本讲内容属于算法第四版,未参照算法导论上的内容)。

平衡查找树之二三树(http://blog.csdn.net/caipeichao2/article/details/30089151)

平衡查找树的目标是实现查找、插入、删除操作在最坏情况下的复杂度均为logN。

本节将介绍二三查找树。

二三树中有两种节点:

  • 二节点对应一个键,有两个子节点

  • 三节点对应两个键,有三个子节点

二三查找树非常平衡,每个空节点到根节点的距离都是一样的 。

查找操作

在二三树中查找一个键的时候有以下规则:

  • 如果是二节点,二节点对应1个值,如果要查找的值比该节点对应的值小,就往左侧深入,反之亦成

  • 如果是三节点,三节点对应2个值,如果比两个值都小,就往左侧深入,如果介于两个值之间,就往中间深入,如果比两个值都大,就往右侧深入。

插入操作

根据查找操作的规则,先定位到需要插入的节点。如果是二节点,那么将二节点中增加一个键成为三节点。如果是三节点,在三节点中增加1个键成为四节点。由于四节点不允许在二三树中出现,因此需要分解成两个二节点,并且把中间的键提取到父节点中。下图展示四节点分解的过程:

现在要在这棵树中插入一个值7


首先根据查找操作的规则定位到要插入的节点,定位之后是如图所示的节点

由于该节点是三节点,因此插入一个键,使它成为四节点

由于四节点不允许在2-3树中存在,因此需要将其分解为两个二节点,并把中间的键7提到父节点中

这样插入操作就完成了


性能


2-3树的高度介于lgN和log_3(N)之间,因此能够保证所有的操作复杂度均在logN以下


实现


二三树的实现非常复杂,因为要判断每个节点的类型,插入节点的时候还需要判断插入节点的位置,需要考虑的情况非常多。


红黑树  (http://blog.csdn.net/caipeichao2/article/details/30089285)

红黑树就是将二三树表示成二叉树的形式,极大地简化了算法。


红黑树的基本思想就是将二三树中的三节点表示成两个二节点,而这两个二节点之间使用红色的连接,普通连接使用黑色的连接。


红黑树中的每个节点都有以下性质:

  • 没有一个节点同时拥有两个红连接

  • 每个空节点到根节点路径上黑色连接的数量都是相同的

  • 红连接只会出现在左边


下图展示了二三树和红黑树的等价表示


查找操作


与普通的二叉树查找树的算法一致,忽略节点的颜色即可。


旋转操作


有时候会出现下图红连接在右侧的情况。这时候就需要通过左旋操作,使其符合红黑树的性质。


下图展示了旋转之后的样子



代码如下:

[java]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. private Node rotateLeft(Node node) {  
  2.     Node right = node.right;  
  3.     node.right = right.left;  
  4.     right.left = node;  
  5.     int color = right.color;  
  6.     right.color = node.color;  
  7.     node.color = color;  
  8.     return right;  
  9. }  


有时候也会用到右旋操作。右旋操作的目的是为了方便某些操作,等操作结束之后再通过左旋恢复原样。


翻转操作


有时候会出现下图这种双重红连接的情况,这种情况对应的就是二三树中的四节点。此时之需要改变父节点的颜色即可。这种操作叫做翻转操作。



下图是变化之后的图


代码如下:

[java]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. private Node flipColor(Node node) {  
  2.     node.color = RED;  
  3.     node.left.color = BLACK;  
  4.     node.right.color = BLACK;  
  5.     return node;  
  6. }  


插入操作


在红黑树中进行插入操作时,与普通的二叉查找树一样。每次新增一个子节点时,需要将新增的节点标记成红色。再通过旋转、翻转操作,维持红黑树的性质。


每次插入之后,需要执行以下步骤:

  • 如果右子节点是红色的,左子节点是黑色的,执行左旋操作

  • 如果左子节点和它的子节点都是红的,执行右旋操作

  • 如果两个子节点都是红色的,执行翻转操作


代码如下:

[java]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. private Node put(Node node, Key key, Value value) {  
  2.     // 创建一个新的红色的节点  
  3.     if(node == null) {  
  4.         return new Node(key, value, RED);  
  5.     }  
  6.    
  7.     // 定位到需要插入的节点  
  8.     int compare = key.compareTo(node.key);  
  9.     if(compare < 0) {  
  10.         node.left = put(node.left, key, value);  
  11.     } else if(compare > 0) {  
  12.         node.right = put(node.right, key, value);  
  13.     } else {  
  14.         node.value = node.value;  
  15.         return node;  
  16.     }  
  17.    
  18.     // 调整红黑树,使其平衡  
  19.     if(isRed(node.right) && !isRed(node.left)) {  
  20.         return rotateLeft(node);  
  21.     }  
  22.     if(isRed(node.left) && isRed(node.left.left)) {  
  23.         return rotateRight(node);  
  24.     }  
  25.     if(isRed(node.left) && isRed(node.right)) {  
  26.         return flipColor(node);  
  27.     }  
  28.    
  29.     return node;  
  30. }  


性能


红黑树的插入、查找、删除操作的复杂度均为logN。


完整代码

[java]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. public class RedBlackTree<Key extends Comparable<Key>, Value> {  
  2.     private static final int RED = 1;  
  3.     private static final int BLACK = 0;  
  4.    
  5.     private class Node {  
  6.         Key key;  
  7.         Value value;  
  8.         Node left;  
  9.         Node right;  
  10.         int color;  
  11.    
  12.         Node(Key key, Value value, int color) {  
  13.             this.key = key;  
  14.             this.value = value;  
  15.             this.color = color;  
  16.         }  
  17.     }  
  18.    
  19.     private Node root;  
  20.    
  21.     public void insert(Key key, Value value) {  
  22.         root = put(root, key, value);  
  23.     }  
  24.    
  25.     private Node put(Node node, Key key, Value value) {  
  26.         // 创建一个新的红色的节点  
  27.         if(node == null) {  
  28.             return new Node(key, value, RED);  
  29.         }  
  30.    
  31.         // 定位到需要插入的节点  
  32.         int compare = key.compareTo(node.key);  
  33.         if(compare < 0) {  
  34.             node.left = put(node.left, key, value);  
  35.         } else if(compare > 0) {  
  36.             node.right = put(node.right, key, value);  
  37.         } else {  
  38.             node.value = node.value;  
  39.             return node;  
  40.         }  
  41.    
  42.         // 调整红黑树,使其平衡  
  43.         if(isRed(node.right) && !isRed(node.left)) {  
  44.             return rotateLeft(node);  
  45.         }  
  46.         if(isRed(node.left) && isRed(node.left.left)) {  
  47.             return rotateRight(node);  
  48.         }  
  49.         if(isRed(node.left) && isRed(node.right)) {  
  50.             return flipColor(node);  
  51.         }  
  52.    
  53.         return node;  
  54.     }  
  55.    
  56.     private boolean isRed(Node node) {  
  57.         // 空节点属于黑节点  
  58.         if(node == nullreturn false;  
  59.    
  60.         // 判断节点是否为红色  
  61.         return node.color == RED;  
  62.     }  
  63.    
  64.     private Node rotateLeft(Node node) {  
  65.         Node right = node.right;  
  66.         node.right = right.left;  
  67.         right.left = node;  
  68.         int color = right.color;  
  69.         right.color = node.color;  
  70.         node.color = color;  
  71.         return right;  
  72.     }  
  73.    
  74.     private Node rotateRight(Node node) {  
  75.         Node left = node.left;  
  76.         node.left = left.right;  
  77.         left.right = node;  
  78.         int color = left.color;  
  79.         left.color = node.color;  
  80.         node.color = color;  
  81.         return left;  
  82.     }  
  83.    
  84.     private Node flipColor(Node node) {  
  85.         node.color = RED;  
  86.         node.left.color = BLACK;  
  87.         node.right.color = BLACK;  
  88.         return node;  
  89.     }  
  90.    
  91.     public Value get(Key key) {  
  92.         Node node = root;  
  93.         while(node != null) {  
  94.             int compare = key.compareTo(node.key);  
  95.             if(compare < 0) {  
  96.                 node = node.left;  
  97.             } else if(compare > 0) {  
  98.                 node = node.right;  
  99.             } else {  
  100.                 return node.value;  
  101.             }  
  102.         }  
  103.    
  104.         // 没有找到  
  105.         return null;  
  106.     }  
  107. }  

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值