文章标题
来源:互联网 发布:淘宝开发者平台 api 编辑:程序博客网 时间:2024/05/24 02:59
package Search;import edu.princeton.cs.algs4.Queue;import java.util.*;//红黑二叉查找树的实现.这个和二三查找树之间的连接不太好找,总是不能一一对应起来,不知道详细的对应步骤.public class redblackSearchtree<Key extends Comparable<Key>, Value> { private static final boolean RED = true; private static final boolean BLACK = false; Node root; public class Node { Key key; Value val; Node left, right; int N; boolean color; Node(Key key, Value val, int N, boolean color) { this.key = key; this.val = val; this.N = N; this.color = color; } } // 判断指向一个结点的链接是否是红色的. private boolean isRed(Node x) { if (x == null) return false; return x.color == RED; } // 旋转的实现,这是算法的核心部分. // 向左旋转,将右边的红链接的树旋转到左边 Node rotateLeft(Node h) { Node x = h.right; h.right = x.left; x.left = h; x.color = h.color; // 将原根节点的链接颜色赋给新 根节点,以确保其保持不变. h.color = RED; x.N = h.N; h.N = 1 + size(h.left) + size(h.right); // 需要重写一下size方法,因为继承过来的方法对现在的Node // 不适用 return x; } // 向右旋转 Node rotateRight(Node h) { Node x = h.left; h.left = x.right; x.left = h; x.color = h.color; h.color = RED; x.N = h.N; h.N = 1 + size(h.left) + size(h.right); // 需要重写一下size方法,因为继承过来的方法对现在的Node // 不适用 return x; } // 转换颜色,作用于左右链接都是红色的时候.将他们全部转换为黑色. // 颜色取反 void flipColors(Node h) { h.color = !h.color; h.left.color = !h.left.color; h.right.color = !h.right.color; // 将父节点的链接设为红色(其实不太懂为什么),百度得知这是为了将一个4-结点的中键上移,将其转化为红色后,就和上面的2-结点组合成一个新的3-结点.这就和2-3树中的内容相对应起来了 } // 插入操作 public Node put(Node h, Key key, Value val) { if (h == null) // 只要Node h 未实例化,那么他就是null,每进行一次插入都会进行这个操作,并且默认将链接设置为红色. return new Node(key, val, 1, RED); // 先进行查找,查找到进行更新,直到查找到一个null值,递归调用,使得树可以成功构造 int cmp = key.compareTo(h.key); if (cmp < 0) h.left = put(h.left, key, val); else if (cmp > 0) h.right = put(h.right, key, val); else h.val = val; // 进行旋转操作,使一个普通二叉树转换为红黑二叉树. if (isRed(h.right) && !isRed(h.left)) h = rotateLeft(h); // 如果右链接是红色的,左转 if (isRed(h.left) && isRed(h.left.left)) h = rotateRight(h); // 如果出现两层红链接,右转. if (isRed(h.left) && isRed(h.right)) flipColors(h); // 如果出现4-结点,转换为三个2-结点(左右都是红色的) h.N = size(h.left) + size(h.right) + 1; // 更新结点总数 return h; } // 删除最小键的代码 // 这是难点,一直没有搞太清楚 // 这个方法定义了两种情况,第一种情况直接颜色反转,使两个黑链接变为红链接,此时就变成了4-结点. // 第二种情况,在这个结点是2-结点的前提下,如果这个结点的右子树的左子树为红色,则将这个结点借给当时查找的左子树. private Node moveRedLeft(Node h) { // 假设结点h为红色,h.left和h.left.left都是黑色.表示当前结点就是根节点,最小结点是这个结点的左子树 // 在满足这个的前提下首先flipColors(h);使其变为3-结点,此时就可以进行删除了 flipColors(h); // flipColors完成之后,还需要进行下一步检查,即判断时候情况更为复杂,无论哪种情况 flipColors都是要执行的. if (isRed(h.right.left)) { h.right = rotateRight(h.right); h = rotateLeft(h); // 原来一直在这一步上很困惑,不知道怎么弄.看了源码才想明白. // 书本上少打了这一步,这是挺重要的一步,使借来链之后的树恢复正常状态.因为前面做了一步反转颜色 // 另外,结点和他的链接是对应在一起的..即一个结点的链接是红色的,那么无论怎么旋转他都是红色的. flipColors(h); } return h; } public void deleteMin() { if (!isRed(root.left) && !isRed(root.right)) root.color = RED; root = deleteMin(root); if (!isEmpty()) root.color = BLACK; } private Node deleteMin(Node h) { // 直到遇到h.left== null,这说明没有比当前键更小的了,返回null,表示这个已经被删除了. if (h.left == null) return null; // 这是重点方法,所做的转换都在这一步,这是从上向下的转换,如果碰见一个2-结点,则将他们转换成3-或者4-结点 if (!isRed(h.left) && !isRed(h.left.left)) h = moveRedLeft(h); // 递归调用这个方法,直到遇见null值. h.left = deleteMin(h.left); // 返回一个平衡操作后的树,此时树就被重构了一次.每递归一次就平衡一次. return balance(h); } // 删除最大键的代码 private Node moveRedRight(Node h) { // 首先反转颜色. flipColors(h); // 和删除最小键一样,如果最大的那个子树为2-,那么由于上面的反转过了, //如果left.left是红色的,那么说明左边是一个4-结点,右边是一个1-几点,需要将两个都变成3-结点.这次从左边借来一个链 if (isRed(h.left.left)) { h = rotateRight(h); flipColors(h); } return h; } private void deleteMax() { //如果为空 if(isEmpty()) throw new NoSuchElementException("BST underflow"); //如果左右子树都不为红色 if(!isRed(root.left) && !isRed(root.right)) root.color = RED; root = deleteMax(root); if(!isEmpty()) root.color = BLACK; } private Node deleteMax(Node h){ //只要是红色的链接都进行右旋转 if(isRed(h.left)) h = rotateRight(h); //这是已经找到并删除最大键 if(h.right == null) return null; //如果当前结点的右结点和右结点的左节点的链接都是黑色,则从左边树中借一个到右边树中 if (!isRed(h.right) && !isRed(h.right.left)) h = moveRedRight(h); h.right = deleteMax(h.right); return balance(h); } //删除具体键的方法 //这个实在太难,必须找时间好好看一下. public void delete(Key key){ if(key == null) throw new IllegalArgumentException("argument to delete() is null"); //如果不存在这个key,返回 if(!contains(key)) return; //先初始化根节点,将根节点转化为红色(后面可以balance回来) if(!isRed(root.left) && !isRed(root.right)) root.color = RED; root = delete(root,key); if(!isEmpty) root.color = BLACK; } private Node delete(Node h,Key key){ //如果查找的键小于当前键,向左移动,继续查找. if(key.compareTo(h.key)<0){ if(!isRed(h.left) && !isRed(h.left.left)) h = moveRedLeft(h); h.left = delete(h.left,key); //如果查找的值大于等于当前键 }else{ //这一步旋转是因为要删除比当前键更大的key,在右子树中必须有红色链接. if(isRed(h.left)) h = rotateRight(h); //这是已经在树底查找到了该键,删除了这个,那么只有左节点可以继任 //将这一步放在前面可能也是因为如果到达树底,那么左右子树就是黑色的.无法判断第三步 if(key.compareTo(h.key) == 0 && (h.right ==null)) return null; //待删除的键不在树底的情况下.先进行旋转,确保右边必有红子树,因为如果刚好下一个就是要删除的2-结点,就必须借来一个结点使他成为一个3-结点,如此才能删除. if(!isRed(h.right) && !isRed(h.right.left)) h = moveRedRight(h); if(key.compareTo(h.key )==0){ Node x = min(h.right); h.key = x.key; h.key = x.key; h.val = x.val; // h.val = get(h.right, min(h.right).key); // h.key = min(h.right).key; h.right = deleteMin(h.right); } //在右子树中继续搜索. else h.right = delete(h.right, key); } return balance(h);} // 返回平衡后的树,递归调用. private Node balance(Node h) { // assert (h != null); if (isRed(h.right)) h = rotateLeft(h); if (isRed(h.left) && isRed(h.left.left)) h = rotateRight(h); if (isRed(h.left) && isRed(h.right)) flipColors(h); h.size = size(h.left) + size(h.right) + 1; return h; }}
其他的方法与普通的二叉树相同.
错误很多,我备忘一下.
阅读全文
0 0
- 文章标题文章标题文章标题文章标题文章标题文章标题文章标题文章标题文章标题文章标题文章标题文章标题文章标题文章标题文章标题文章标题文章标题
- 文章标题
- 文章标题
- 文章标题
- 文章标题 文章标题 文章标题 文章标题
- 文章标题
- 文章标题
- 文章标题
- 文章标题
- 文章标题
- 文章标题
- 文章标题
- 文章标题
- 文章标题
- 文章标题
- 文章标题
- 文章标题
- 文章标题
- 坚持or放弃?
- java多线程服务器,实现客户端间聊天
- docker 启动mysql
- LA 3135 Argus
- 文件操作
- 文章标题
- 2017-12-9 pat b review&think
- deepin Linux 安装nodejs
- Least squares
- CF-897B.Chtholly's request
- 字典操作
- Javascript——For循环语句
- numpy中argsort函数用法
- 理解RESTful