用红黑树实现Map

直接看代码:

/**
 * 直接把TreeMap当作一颗红黑树去实现,实现一个Map
 * @param <K>
 * @param <V>
 */
@SuppressWarnings({"all"})
public class TreeMap<K,V> implements Map<K,V>{

    private static final boolean RED = false ;

    private static final boolean BLACK = true ;

    private int size ;
    //确定根节点
    private Node<K,V> root ;

    //传入一个Comparator对象
    private Comparator<K> comparator ;

    public TreeMap(Comparator<K> comparator) {
        this.comparator = comparator ;
    }

    public TreeMap() {
        //调用的是本类的有参构造器,只不过传入的是空参
        this(null) ;
    }
    /**
     * 树节点元素的数量
     */
    public int size() {
        return size;
    }
    /**
     * 是否为空
     */
    public boolean isEmpty() {
        return size == 0 ;
    }
    /**
     * 清空所有元素
     */
    public void clear() {
        root = null ;
        size = 0 ;
    }
    /**
     * @return 返回值返回的是被覆盖掉的value值是什么
     */
    @Override
    public V put(K key, V value) {
        //key不可以为空
        elementNotNullCheck(key);

        //1.添加的是第一个节点
        if (root == null) {
            //对于根节点而言没有父节点
            root = new Node<>(key,value,null ) ;
            size ++ ;
            //添加root节点之后对其进行调整
            afterPut(root);
            return null;
        }
        //2.说明添加的不是树中第一个元素
        //2.1 确定根节点 root
        Node<K,V> node = root ;
        //2.2 假设出待插入节点对应的父节点的值
        Node<K,V> parent = null ;
        //2.3 记录最后一次比较器返回的值 来确定待插入节点插入到哪个方向
        int cmp = 0 ;
        //2.4 循环寻找待插入节点需要插入到的位置
        while (node != null) {
            //记录cmp
            cmp = compare(node.key,key) ;
            //记录parent
            parent = node ;
            if (cmp > 0) {
                //说明已存在节点元素值大,那么插到左边
                node = node.left ;
            } else if (cmp < 0) {
                node = node.right ;
            } else {
                //cmp == 0 如果自定义比较器比较出相等时,我们这里的处理方式是覆盖值操作
                node.key = key ;
                V oldValue = node.value ;
                node.value = value ;
                return oldValue ;//返回的是被覆盖的value值
            }
        }
        //3.退出循环之后,根据记录的parent值 进行创建待插入节点对应的Node对象
        Node<K,V> newNode = new Node<>(key,value,parent) ;
        //4.根据记录的cmp值和parent值,进行确定把待插入节点插入到哪一个位置
        if (cmp > 0) {
            parent.left = newNode ;
        } else {
            //cmp < 0 的情况,cmp == 0 直接就返回了已经
            parent.right = newNode ;
        }
        size ++ ;
        //添加node节点后 进行的调整
        afterPut(node); ;
        return null ;
    }

    /**
     * 添加操作可能毁坏红黑树的性质
     * afterPut修复红黑树的性质
     * @param node
     */
    private void afterPut(Node<K, V> node) {
        Node<K,V> parent = node.parent ;
        //1.情况一中的四种添加位置,是无需进行修复处理红黑树的。
        //1.1添加的是根节点
        if (parent == null) {
            black(node) ;//根节点直接染成黑色
            return;
        }
        //1.2如果父节点是黑色,直接返回
        if (isBlack(parent)) return ;
        //2.情况二中的八种添加位置,是需要进行修复处理红黑树的。
        //2.1拿到uncle节点进行判断 uncle节点:叔父节点即是父节点的兄弟节点
        Node<K,V> uncle = parent.sibling() ;
        //2.2祖父节点
        Node<K,V> grand = parent.parent ;
        //2.3 叔父节点为RED,只需进行染色 然后进行递归grand作为新添加的节点进行处理
        if (isRed(uncle)) {
            black(parent) ;
            black(uncle) ;
            //把祖父节点当作是新添加的节点进行处理
            red(grand) ;
            afterPut(grand); ;
            return;
        }
        //2.4 叔父节点不为RED,需要进行旋转处理
        if (parent.isLeftChild()) { //parent是grand的左子树节点 L
            if (node.isLeftChild()) { //node是parent的左子树节点 LL
                //LL和RR为一类,parent染成BLack,grand染成RED
                black(parent) ;
                red(grand) ;
                rotateRight(grand);
            } else { //LR
                //LR 和 RL为一类,新添加的节点染成Black,grand染成RED
                black(node) ;
                red(grand) ;
                rotateLeft(parent) ;
                rotateRight(grand) ;
            }
        } else { //R
            if (node.isLeftChild()) { //RL
                //LR 和 RL为一类,新添加的节点染成Black,grand染成RED
                black(node) ;
                red(grand) ;
                rotateRight(parent); ;
                rotateLeft(grand); ;
            } else {//RR
                //LL和RR为一类,parent染成BLack,grand染成RED
                black(parent) ;
                red(grand) ;
                rotateLeft(grand);
            }
        }
    }
    private int compare(K k1,K k2)  {
        if (comparator != null) {
            return comparator.compare(k1,k2) ;
        }
        //如果比较器为空,那么要进行强转化为Comparable类型进行使用对应比较逻辑
        return ((Comparable<K>)k1).compareTo(k2) ;
    }
    private void elementNotNullCheck(K key) {
        if (key == null) {
            throw new IllegalArgumentException("element must not be null") ;
        }
    }
    @Override
    public V get(K key) {
        Node<K,V> node = node(key) ;
        return node != null ? node.value : null;
    }

    @Override
    public V remove(K key) {
        return remove(node(key));
    }

    @Override
    public boolean containsKey(K key) {
        return node(key) != null ;
    }

    /**
     * 由于只有Key具有可比较性,所以寻找value看是否存在时
     * 我们需要遍历整个红黑树
     * 遍历时使用的是层序遍历
     */
    @Override
    public boolean containsValue(V value) {
        Queue<Node<K,V>> queue = new LinkedList<>() ;
        queue.offer(root) ;
        while (!queue.isEmpty()) {
            //队头出队
            Node<K,V> node = queue.poll() ;
            if (valEquals(node.value,value)) {
                return true ;//值相等 说明找到了 返回true
            }
            if (node.left != null) {
                queue.offer(node.left) ;
            }
            if (node.right != null) {
                queue.offer(node.right) ;
            }
        }
        return false ;
    }

    /**
     * 遍历 使用中序遍历:得出从小到大的结果 !
     * @param visitor
     */
    @Override
    public void traversal(Visitor<K, V> visitor) {
        if (visitor == null) {
            return;
        }
        traversal(root,visitor) ;
    }

    private void traversal(Node<K,V> node,Visitor<K,V> visitor) {
        if (node == null || visitor.stop) {
            return;
        }
        traversal(node.left,visitor) ;
        if (visitor.stop) {
            return;
        }
        visitor.visit(node.key,node.value) ;
        traversal(node.right,visitor) ;
    }
    /**
     * 比较v1与v2是否相等
     */
    private boolean valEquals(V v1,V v2) {
        return v1 == null ? v2 == null : v1.equals(v2) ;
    }
    private V remove(Node<K,V> node) {
        if (node == null) {
            return null;
        }
        V oldValue = node.value ;
        size -- ;
        //(1)删除的是度为2的节点
        if (node.hasTwoChildren()) {
            //找到该节点对应的前驱或后继节点
            Node<K,V> s = succeed(node) ;//找到后继节点
            //使用后继节点的值进行覆盖度为2的节点的值
            node.key = s.key ;
            node.value = s.value ;
            //把待删除的节点进行赋值为前驱或后继节点,使用后面的逻辑进行删除前驱后继节点
            node = s ;
        }
        //必须搞出一个replacement进行记录删除的是根节点左子树还是右子树的节点
        Node<K,V> replacement = node.left != null ? node.left : node.right ;
        //(2)删除的是度为1的节点
        if (replacement != null) {
            //统一进行更新删除节点度为1的操作之后的parent指向值
            replacement.parent = node.parent ;
            //2.1 如果是度为1的节点并且是root根节点
            if (node.parent == null) {
                root = replacement ;
            } else if (node == node.parent.left) {
                //2.2 如果是左子树上的度为1的节点
                node.parent.left = replacement ;
            } else if (node == node.parent.right) {
                //2.3 如果是右子树上的度为1的节点
                node.parent.right = replacement ;
            }
            //真正被删除的是度为1或2的前驱或后继节点
            afterRemove(node,replacement) ;
        } else if (node.parent == null) {// (3) 如果删除的是叶子节点并且为root根节点
            root = null ;
        } else {//(4) 如果删除的是叶子节点但是不为root根节点
            if (node == node.parent.right) {
                //4.1 如果是左子树上的度为0的节点
                node.parent.right = null ;
            } else {//node = node.parent.left
                //4.2 如果是右子树上的度为0的节点
                node.parent.left = null ;
            }
        }
        return oldValue ;
    }
    private void afterRemove(Node<K,V> node, Node<K,V> replacement) {
        //1。如果删除的节点是红色RED,删除之后无需处理
        if (isRed(node)) return;
        //2.用以取代被删除的node的子节点是RED时
        if (isRed(replacement)) {
            black(replacement) ;
            return;
        }
        Node<K,V> parent = node.parent ;
        //删除的是根节点
        if (parent == null) return;
        //3.删除的是黑色叶子节点
        //判断被删除的node是左还有右:
        //parent.left == null说明被删除的node节点是parent的左子树,
        // 那么被删除的node节点的兄弟节点即是parent的右子树,反之同理即可
        boolean left = parent.left == null ;
        Node<K,V> sibling = left ? parent.right : parent.left ;
        if (left) {//3.1被删除的节点在左边,那么兄弟节点sibling在右边
            if (isRed(sibling)) {
                //3.2.1删除的是黑色叶子节点并且sibling是红色,那么转换为sibling为黑色
                black(sibling) ;
                red(parent) ;
                rotateRight(parent) ;
                //更换兄弟
                sibling = parent.right ;
            }
            //3.2.2删除的是黑色叶子节点并且sibling为BLACK
            //3.2.2.1 兄弟节点没有一个是红色子节点,父节点要下根兄弟节点合并
            if (isBlack(sibling.right) && isBlack(sibling.left)) {
                //记录一下是否父节点也会下溢
                boolean parentBlack = isBlack(parent) ;
                black(parent) ;
                red(sibling) ;
                if (parentBlack) {
                    afterRemove(parent,null) ;
                }
            } else {  //3.2.2.2 兄弟节点至少有1个红色子节点,被删除节点向兄弟节点借用元素
                //兄弟节点的左边是黑色,说明右边是红色子节点
                if (isBlack(sibling.right)) {
                    rotateLeft(sibling) ;
                    sibling = parent.right ;//旋转后要更新兄弟节点
                    color(sibling,colorOf(parent)) ;
                    black(sibling.right) ;
                    black(parent) ;
                }
                //兄弟节点的左边是红色,说明左边是红色子节点
                color(sibling,colorOf(parent)) ;
                black(sibling.right) ;
                black(parent) ;
                rotateRight(parent);
            }
        } else {//3.2被删除的节点在右边,那么兄弟节点sibling在左边
            if (isRed(sibling)) {
                //3.2.1删除的是黑色叶子节点并且sibling是红色,那么转换为sibling为黑色
                black(sibling) ;
                red(parent) ;
                rotateRight(parent) ;
                //更换兄弟
                sibling = parent.left ;
            }
            //3.2.2删除的是黑色叶子节点并且sibling为BLACK
            //3.2.2.1 兄弟节点没有一个是红色子节点,父节点要下根兄弟节点合并
            if (isBlack(sibling.left) && isBlack(sibling.right)) {
                //记录一下是否父节点也会下溢
                boolean parentBlack = isBlack(parent) ;
                black(parent) ;
                red(sibling) ;
                if (parentBlack) {
                    afterRemove(parent,null) ;
                }
            } else {  //3.2.2.2 兄弟节点至少有1个红色子节点,被删除节点向兄弟节点借用元素
                //兄弟节点的左边是黑色,说明右边是红色子节点
                if (isBlack(sibling.left)) {
                    rotateLeft(sibling) ;
                    sibling = parent.left ;//旋转后要更新兄弟节点
                    color(sibling,colorOf(parent)) ;
                    black(sibling.left) ;
                    black(parent) ;
                }
                //兄弟节点的左边是红色,说明左边是红色子节点
                color(sibling,colorOf(parent)) ;
                black(sibling.left) ;
                black(parent) ;
                rotateRight(parent);
            }
        }
    }
    /**
     * 找到node节点对应的前驱节点:
     * 即是二叉树上的元素进行中序遍历顺序之后得出的一串数字节点中,node节点所在其中位置的前一个节点
     * @param node
     * @return
     */
    private Node<K,V> predecessor(Node<K,V> node) {
        if (node == null) {
            return null ;
        }
        Node<K,V> p = node ;
        if (p.left != null) {
            p = p.left ;
            while (p.right != null) {
                p = p.right ;
            }
            //说明p==null,说明一个右子节点为空,
            // 那么最后一次不为空的node.right即为node的前驱节点
            return p ;
        }
        //说明p.left == null,没有进入if语句
        //我们就要找父节点
        //条件为:父节点不为空,并且。。
        while ( p.parent != null &&  p == p.parent.left) {
            p = p.parent ;
        }
        //到这里 有两种情况:
        //1.p.parent == null (说明该node没有前驱节点 返回p.parent(null)即可)
        //2. p == p.parent.right(找到该node的前驱节点,返回前驱节点:p.parent)
        return p.parent ;
    }

    /**
     * 找到后继节点
     * @param node
     * @return
     */
    private Node<K,V> succeed(Node<K,V> node) {
        if (node == null) {
            return null ;
        }
        //1,
        Node<K,V> p = node ;
        if (p.right != null) {
            p = p.right ;
            while (p.left != null) {
                p = p.left ;
            }
            return p ;
        }
        //p.right == null
        //目的是找node节点的后继节点,所以要找第一个比它大的
        //所以在父节点不为空 并且一直在变化的父节点的右子树 、
        // 如果它发现在左子树后,那么就要退出循环,说明找到了第一个比它大的节点
        if (p.parent != null && p == p.parent.right) {
            p = p.parent ;
        }
        //p.parent == null
        //p == p.parent.left
        return p.parent ;
    }
    /**
     * 对传入的节点进行染色处理
     * @param node 想要染色的节点
     * @param color 把该节点node染成color颜色
     * @return 返回染色后的节点
     */
    private Node<K,V> color(Node<K,V> node, boolean color) {
        if (node == null) {
            return node ;
        }
        node.color = color ;
        return node ;
    }

    /**
     * 把该node节点染成RED
     */
    private Node<K,V> red(Node<K,V> node) {
        return color(node,RED) ;
    }

    /**
     * 把该node节点染成BLACK
     */
    private Node<K,V> black(Node<K,V> node) {
        return color(node,BLACK) ;
    }

    /**
     * 传入一个node节点,进行返回该节点是什么颜色
     */
    private boolean colorOf(Node<K,V> node) {
        //如果node为null,即是null节点 即是BLACK
        // 【对null节点进行特殊处理的原因是:我们一开始并没有对null节点的颜色进行设置】
        //如果不为空,那么返回node实际的颜色即可
        return node == null ? BLACK : node.color ;
    }

    /**
     * 判断该node节点颜色color是否为BLACK
     */
    private boolean isBlack(Node<K,V> node) {
        return colorOf(node) == BLACK ;
    }

    /**
     * 判断该node节点颜色color是否为RED
     */
    private boolean isRed(Node<K,V> node) {
        return colorOf(node) == RED ;
    }

    /**
     * 左旋转
     * @param grand 想要进行旋转的节点,传过来的是什么,什么节点就进行左旋转
     */
    private void rotateLeft(Node<K,V> grand) {
        //进行左旋转,说明是RR,可以确定parent
        Node<K,V> parent = grand.right ;
        //child即是T1
        Node<K,V> child = parent.left ;
        grand.right =  child; //1.
        parent.left = grand ; //2.
        //执行旋转之后的逻辑
        afterRotate(grand,parent,child);
    }
    /**
     * 右旋转
     * @param grand 想要进行旋转的节点,传过来的是什么,什么节点就进行右旋转
     */
    private void rotateRight(Node<K,V> grand) {
        //1.进行右旋转,说明是LL,那么可以确定parent
        Node<K,V> parent = grand.left ;
        //2.找出T2
        Node<K,V> child = parent.right ;
        //3.对g执行右旋转
        grand.left = child ; //3.1
        parent.right = grand ;//3.2
        //执行旋转之后的逻辑
        afterRotate(grand,parent,child);
    }
    /**
     * 旋转之后统一要做的逻辑
     * @param grand (最低的失去平衡的节点)
     * @param parent (parent是grand的子节点)
     * @param child 旋转过程中父节点改变的节点(T1或T2)
     */
    private void afterRotate(Node<K,V> grand, Node<K,V> parent, Node<K,V> child) {
        //3.让parent成为这颗子树的根节点
        //3.1parent先连上grand的父节点
        parent.parent = grand.parent ;
        //3.2 grand再去连上parent
        if (grand.isLeftChild()) {
            //如果grand是父节点的左子树
            grand.parent.left = parent ;
        } else if (grand.isRightChild()) {
            //如果grand是父节点的右子树
            grand.parent.right = parent ;
        } else {//既不是父节点的左子树又不是右子树,那么说明grand即是root根节点
            root = parent ;
        }
        //4.更新child(T1)的parent
        if (child != null) {
            child.parent = grand ;
        }
        //5.更新grand的parent
        grand.parent = parent ;
    }

    /**
     * 找到相同的key 返回对应的Node节点
     * @param key
     * @return
     */
    private Node<K,V> node(K key) {
        Node<K,V> node = root ;
        while (node != null) {
            int cmp = compare(key,node.key) ;
            if (cmp == 0) {
                return node ;
            }
            if (cmp > 0) {
                //cmp > 0 说明element大,那么在右子树
                node = node.right ;
            } else {
                //cmp < 0
                node = node.left ;
            }
        }
        return null ;//说明没找到,那么返回null
    }
    private static class Node<K,V> {
        K key ;
        V value ;
        boolean color = RED;
        Node<K,V> left ;//左子节点
        Node<K,V> right ;//右子节点
        Node<K,V> parent ;//父节点
        int height ;
        public Node(K key, V value , Node<K,V> parent) {
            this.key = key ;
            this.value = value ;
            this.parent = parent ;//除了根节点,其余节点都具有父节点,所以要在构造方法传入
        }
        //判断是否为叶子节点
        public boolean isLeaf() {
            return left == null && right == null ;
        }
        //判断是否具有两个子节点
        public boolean hasTwoChildren() {
            return left != null && right != null ;
        }
        //说明是parent的左子树
        public boolean isLeftChild() {
            return parent != null && this == parent.left ;
        }
        //说明是parent的右子树
        public boolean isRightChild() {
            return parent != null && this == parent.right ;
        }
        /**
         * 找出兄弟节点
         * @return
         */
        public Node<K,V> sibling() {
            if (isLeftChild()) {
                //说明该节点是父节点的左子树
                return parent.right ;//说明该节点的兄弟节点即是父节点的右子树
            }
            if (isRightChild()) {
                //说明该节点是父节点的右子树
                return parent.left ;//说明该节点的兄弟节点即是父节点的左子树
            }
            //说明该节点既不是父节点的左子树 也不是父节点的右子树
            //那么说明该节点没有父节点,那么就没有兄弟节点
            return null ;
        }
    }
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
当然,我可以帮你实现一个基于红黑树Map数据结构。下面是一个简单的C++代码示例: ```cpp #include <iostream> enum Color { RED, BLACK }; template <typename K, typename V> struct Node { K key; V value; Color color; Node* left; Node* right; Node* parent; // 构造函数 Node(const K& k, const V& v, Color c = RED) : key(k), value(v), color(c), left(nullptr), right(nullptr), parent(nullptr) {} }; template <typename K, typename V> class RBTreeMap { public: RBTreeMap() : root(nullptr) {} // 向红黑树中插入键值对 void insert(const K& key, const V& value) { Node<K, V>* node = new Node<K, V>(key, value); if (root == nullptr) { root = node; node->color = BLACK; } else { Node<K, V>* curr = root; Node<K, V>* parent = nullptr; while (curr != nullptr) { parent = curr; if (key < curr->key) curr = curr->left; else if (key > curr->key) curr = curr->right; else { curr->value = value; // 更新值 return; } } node->parent = parent; if (key < parent->key) parent->left = node; else parent->right = node; insertFixup(node); } } // 查找指定键的值 V find(const K& key) const { Node<K, V>* curr = root; while (curr != nullptr) { if (key < curr->key) curr = curr->left; else if (key > curr->key) curr = curr->right; else return curr->value; } return V(); // 未找到,返回默认值 } private: Node<K, V>* root; // 对红黑树进行修复以维持红黑树的性质 void insertFixup(Node<K, V>* node) { while (node->parent != nullptr && node->parent->color == RED) { if (node->parent == node->parent->parent->left) { Node<K, V>* uncle = node->parent->parent->right; if (uncle != nullptr && uncle->color == RED) { node->parent->color = BLACK; uncle->color = BLACK; node->parent->parent->color = RED; node = node->parent->parent; } else { if (node == node->parent->right) { node = node->parent; rotateLeft(node); } node->parent->color = BLACK; node->parent->parent->color = RED; rotateRight(node->parent->parent); } } else { Node<K, V>* uncle = node->parent->parent->left; if (uncle != nullptr && uncle->color == RED) { node->parent->color = BLACK; uncle->color = BLACK; node->parent->parent->color = RED; node = node->parent->parent; } else { if (node == node->parent->left) { node = node->parent; rotateRight(node); } node->parent->color = BLACK; node->parent->parent->color = RED; rotateLeft(node->parent->parent); } } } root->color = BLACK; } // 左旋 void rotateLeft(Node<K, V>* node) { Node<K, V>* rightChild = node->right; node->right = rightChild->left; if (rightChild->left != nullptr) rightChild->left->parent = node; rightChild->parent = node->parent; if (node->parent == nullptr) root = rightChild; else if (node == node->parent->left) node->parent->left = rightChild; else node->parent->right = rightChild; rightChild->left = node; node->parent = rightChild; } // 右旋 void rotateRight(Node<K, V>* node) { Node<K, V>* leftChild = node->left; node->left = leftChild->right; if (leftChild->right != nullptr) leftChild->right->parent = node; leftChild->parent = node->parent; if (node->parent == nullptr) root = leftChild; else if (node == node->parent->left) node->parent->left = leftChild; else node->parent->right = leftChild; leftChild->right = node; node->parent = leftChild; } }; int main() { RBTreeMap<int, std::string> map; map.insert(1, "one"); map.insert(2, "two"); map.insert(3, "three"); map.insert(4, "four"); std::cout << map.find(2) << std::endl; // 输出: two std::cout << map.find(5) << std::endl; // 输出: 空字符串(默认值) return 0; } ``` 这个示例使用红黑树实现了一个简单的Map数据结构。你可以使用`insert`方法向Map中插入键值对,使用`find`方法查找指定键的值。注意,这只是一个简单的实现,还可以根据需要进行扩展和优化。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值