我的第一篇博客——红黑树的基本操作
2016-04-07 19:18
351 查看
RBTree是相对平衡的二叉查找树(bst),与AVL平衡二叉树相比,其”平衡“程度没有那么严格,因而在维护红黑树结构时需要较小的代价,总体性能较好(但是在有特殊查找要求时,考虑使用AVL_tree也是一个不错的选择。)下面我将写写RBTree的性质与基本操作:
首先来简单定义RBTree的节点,方便叙述
然后来了解RBTree的性质(专业定义请看算法导论红黑树章节)
由RBTree的性质,其颜色约束可以把RBTree的高度限制在log2^n以内,达到到降低时间复杂度的目的。
好了,现在就来看看基本的insert/delete/search 操作,因为RBTree是基于bst的,其search操作与bst的search一样,insert/delete也是在bst的insert/delete操作的基础上改进。由于intsert/delete时可能破坏RBTree的结构,所以需要维持其结构,一般通过改变相关节点的颜色或旋转来维护。
认识旋转操作:leftRotate(X)/rightRotate(Y)
分析insert操作(插入的节点颜色为红色,用n表示)
1. n == root //空树时插入,只破坏性质1
n.color = BLACK; return;
2. n != root //非空树时插入
a. if n.p == BLACK return; //性质1-4均没有被破坏
b. if n.p == RED //直接破坏了性质3,需要维护,在维护过程中只需关注n以及n的父亲·爷爷·叔叔,其中P:父亲 G:爷爷 U:叔叔,情况如下
由于前两种情况(LL/LR)与后两情况(RR/RL)镜面对称,讨论前两情况即可,代码实现时left与right互换即可。于是我们来看看前两情况的各种着色可能及应对策略
P.color=BLACK; U.color=BLACK; G.color=RED; N=G;进入下一次迭代
rightRotate(G); P.color=BLACK; G.color=RED;进入下一次迭代
leftRotate(P); N<->P; rightRotate(G); P.color=BLACK; G.color=RED;进入下一次迭代
注意迭代终止条件为:上述1或2.a //while(n!=root && n.p.color==BLACK)
分析delete操作(删除节点为min,链接到n.p的节点为con)
1.删除点颜色为红色,return;
2.不为黑色则,把链接接上去的节点看成既具有自己的colo属性,也有一额外的“黑色”用来提高黑色节点数目,现在只要想办法把这个“黑色”转为某节点的color属性即可:
1. if con.color ==RED
con.color = BLACK; return;
2.关注con 及其父亲·叔叔·侄儿,情况如下:(灰色表示颜色不确定)
下面给出部分函数java代码实现:
h
首先来简单定义RBTree的节点,方便叙述
class RBTreeNode{ int key; int color; //颜色值 1:BLACK 2:RED RBTreeNode p; //父节点 RBTreeNode left; //左孩子 RBTreeNode right; //右孩子 }
然后来了解RBTree的性质(专业定义请看算法导论红黑树章节)
1. root.color == BLACK //根节点颜色为黑 2. node.color == BLACK OR RED //每个节点的颜色为黑色或红色 3. if n.color == RED //一个节点为红色,则两个孩子(非空的话)颜色为黑色 n.left.color == BLACK n.right.color == BLACK 4. node--->leaf 的路径上黑色节点数目相同 //任一节点到其叶子节点的路径上黑色节点的数目相同
由RBTree的性质,其颜色约束可以把RBTree的高度限制在log2^n以内,达到到降低时间复杂度的目的。
好了,现在就来看看基本的insert/delete/search 操作,因为RBTree是基于bst的,其search操作与bst的search一样,insert/delete也是在bst的insert/delete操作的基础上改进。由于intsert/delete时可能破坏RBTree的结构,所以需要维持其结构,一般通过改变相关节点的颜色或旋转来维护。
认识旋转操作:leftRotate(X)/rightRotate(Y)
分析insert操作(插入的节点颜色为红色,用n表示)
1. n == root //空树时插入,只破坏性质1
n.color = BLACK; return;
2. n != root //非空树时插入
a. if n.p == BLACK return; //性质1-4均没有被破坏
b. if n.p == RED //直接破坏了性质3,需要维护,在维护过程中只需关注n以及n的父亲·爷爷·叔叔,其中P:父亲 G:爷爷 U:叔叔,情况如下
由于前两种情况(LL/LR)与后两情况(RR/RL)镜面对称,讨论前两情况即可,代码实现时left与right互换即可。于是我们来看看前两情况的各种着色可能及应对策略
P.color=BLACK; U.color=BLACK; G.color=RED; N=G;进入下一次迭代
rightRotate(G); P.color=BLACK; G.color=RED;进入下一次迭代
leftRotate(P); N<->P; rightRotate(G); P.color=BLACK; G.color=RED;进入下一次迭代
注意迭代终止条件为:上述1或2.a //while(n!=root && n.p.color==BLACK)
分析delete操作(删除节点为min,链接到n.p的节点为con)
1.删除点颜色为红色,return;
2.不为黑色则,把链接接上去的节点看成既具有自己的colo属性,也有一额外的“黑色”用来提高黑色节点数目,现在只要想办法把这个“黑色”转为某节点的color属性即可:
1. if con.color ==RED
con.color = BLACK; return;
2.关注con 及其父亲·叔叔·侄儿,情况如下:(灰色表示颜色不确定)
下面给出部分函数java代码实现:
class RBTreeNode{ final static int BLACK = 1,RED = 0; int key; int color; //颜色值 1:BLACK 0:RED RBTreeNode p; //父节点 RBTreeNode left; //左孩子 RBTreeNode right; //右孩子 RBTreeNode(int key,int color,RBTreeNode p,RBTreeNode left,RBTreeNode right){ this.key = key; this.color = color; this.p = p; this.left = left; this.right = right; } } public class RBTree { private RBTreeNode root; RBTree(){ root = null;} void leftRotate(RBTreeNode x){//左旋转 RBTreeNode y = x.right; if(x == root) root = y; else { if(x.p.left ==x) x.p.left = y; else x.p.right = y; } y.p = x.p; x.right = y.left; y.left = x; x.p = y; if(x.right != null) x.right.p = x; } void rightRotate(RBTreeNode x){//右旋转 RBTreeNode y = x.left; if(x == root) root = y; else { if(x.p.left ==x) x.p.left = y; else x.p.right = y; } y.p = x.p; x.left= y.right; y.right = x; x.p = y; if(x.left !=null) x.left.p = x; } void fixUpInsert(RBTreeNode n){ //插入后维持性质 while(root != n && n.p.color == RBTreeNode.RED){ if(n.p.p.left == n.p){ //LL LR if( n.p.right !=null && n.p.right.color == RBTreeNode.RED){ //情况2.1 n.p.color = RBTreeNode.BLACK; n.p.color = RBTreeNode.BLACK; n.p.p.color = RBTreeNode.RED; n = n.p.p; } else { if(n.p.right == n){ leftRotate(n.p); n = n.left; } rightRotate(n.p.p); n.p.color = RBTreeNode.BLACK; n.p.right.color = RBTreeNode.RED; } } else{ //RR RL if( n.p.left !=null && n.p.left.color == RBTreeNode.RED){ n.p.color = RBTreeNode.BLACK; n.p.color = RBTreeNode.BLACK; n.p.p.color = RBTreeNode.RED; n = n.p.p; } else { if(n.p.left == n){ rightRotate(n.p); n = n.right; } leftRotate(n.p.p); n.p.color = RBTreeNode.BLACK; n.p.left.color = RBTreeNode.RED; } } } root.color = RBTreeNode.BLACK; } void fixUpDelete(RBTreeNode n){ //删除后维持性质 //自己根据delete分析码出来吧 } void insert(int key){ //插入操作 if(root == null){ //空树时插入 root = new RBTreeNode(key,RBTreeNode.BLACK,null,null,null); } else{ //非空树时插入 RBTreeNode temRoot = root; RBTreeNode temRootPa =null; boolean markL = false; while(temRoot !=null){ //找到即将插入的位置 markL = false; if(temRoot.key > key){ temRootPa = temRoot; temRoot = temRoot.left; markL = true; } else { temRootPa = temRoot; temRoot = temRoot.right; } } if(markL){ temRootPa.left = new RBTreeNode(key,RBTreeNode.RED,temRootPa,null,null); fixUpInsert(temRootPa.left); //插入后维持性质 } else { temRootPa.right = new RBTreeNode(key,RBTreeNode.RED,temRootPa,null,null); fixUpInsert(temRootPa.right); //插入后维持性质 } } } RBTreeNode getMin(RBTreeNode tem){ while(tem.left != null) tem = tem.left; return tem; } void delete(int key){ RBTreeNode temRoot = root; while(temRoot !=null){ //寻找要删除的节点 if(temRoot.key > key) temRoot = temRoot.left; else if(temRoot.key < key) temRoot = temRoot.right; else break; } if(temRoot == null) return; //没有找到要删除的节点 if(temRoot.left !=null && temRoot.right !=null){ //待删除节点有两个孩子 RBTreeNode min = getMin(temRoot.right); //找到右子树中最小元素节点 temRoot.key = min.key; //把待删除点转移到右子树中最小元素节点 if(min.p.right == min) min.p.right = min.right; //删除min节点 else min.p.left = min.right; fixUpDelete(min.right); //删除后维持性质 } else { RBTreeNode con = (temRoot.left != null ? temRoot.left : temRoot.right); //确定连接上去的节点 if(temRoot ==root) //待删点为root root = con; else{ if(temRoot.p.left == temRoot) temRoot.p.left =con; else temRoot.p.right = con; } fixUpDelete(con); //删除后维持性质 } } }
h
相关文章推荐
- redis中5种数据类型介绍、命令(二)
- scikit-learn的主要模块和基本使用
- 【多重背包】HDU2191悼念512汶川大地震遇难同胞——珍惜现在,感恩生活【模板】
- [BZOJ2132]圈地计划(最小割)
- apache跨域问题
- 脱离 Spring 实现复杂嵌套事务,之三(REQUIRES_NEW - 独立事务)
- 脱离 Spring 实现复杂嵌套事务,之四(NESTED - 嵌套事务)
- C++实现——由年月日推算是星期几
- C# 网络编程之基于SMTP发送电子邮件
- Android官方开发文档Training系列课程中文版:通过NFC共享文件之发送文件到另一台设备
- 剑指offer-面试题2.实例Singleton模式
- [UOJ35]后缀排序
- ssh 登录出现的几种错误以及解决办法
- 脱离 Spring 实现复杂嵌套事务,之二(REQUIRED - 加入已有事务)
- 链表--单链表
- 问卷
- does not match bootstrap parameter
- 4.8 团队分析
- 脱离 Spring 实现复杂嵌套事务,之一(必要的概念)
- JavaScript之流程控制