文章标题

来源:互联网 发布:淘宝开发者平台 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;    }}

其他的方法与普通的二叉树相同.
错误很多,我备忘一下.

原创粉丝点击