美文网首页
14_映射(Map)

14_映射(Map)

作者: 伶俐ll | 来源:发表于2020-09-09 12:17 被阅读0次

    Map在有些编程语言中也叫做字典(dictionary,比如Python、Ojective-C、Swift等)

    Map的每一个key是唯一的


    Snip20200906_1.png

    类似Set,Map可以直接使用链表,二叉搜索树(AVL树、红黑树)等数据结构来实现

    接口设计
    • int size(){}
    • boolean isEmpty(){}
    • void clear(){}
    • void put(K key,V value){}
    • V get(K key){}
    • void remove(K key){}
    • boolean containsKey(K key){}
    • boolean containsValue(V value){}
    • traversal(Visitor<K,V> visitor){}
    • public static abstract class Visitor<K,V>{ boolean stop; public abstract boolean visit(K key,V value); }
    代码实现
    package Map;
    
    import Set.LZLinkedListSet;
    import Set.RBTree.LZBinaryTree;
    import Set.RBTree.LZRBTree;
    
    import java.util.Comparator;
    import java.util.LinkedList;
    import java.util.Queue;
    
    public class LZTreeMap<K,V> {
    
        private static final boolean RED = false;
        private static final boolean BLACK = true;
    
        private int size;
        private Node<K,V> root;
    
        private Comparator<K> comparator;
    
        public LZTreeMap(){
            this(null);
        }
    
        public LZTreeMap(Comparator<K> comparator){
            this.comparator = comparator;
        }
    
        /**
         * Map的大小
         */
        public int size(){
            return size;
        }
    
        /**
         * Map是否为空
         */
        public boolean isEmpty(){
            return size == 0;
        }
    
        /**
         * 清空Map
         */
        public void clear(){
            root = null;
            size = 0;
        }
    
        public void put(K key,V value){
            keyNotNullCheck(key);
            //添加的是第一个节点
            if (root == null){
                root = new Node<K,V>(key,value,null);
                size++;
                //添加新节点之后的处理
                afterAdd(root);
                return;
            }
            //添加的不是第一个节点
            Node<K,V> parent = root;
            Node<K,V> node = root;
            int cmp = 0;
            while (node != null){
                cmp = (compare(key,node.key));
                parent = node;
                if (cmp<0){
                    node = node.left;
                }else if(cmp>0){
                    node = node.right;
                }else {
                    node.key = key;
                    node.value = value;
                    return;
                }
            }
    
            Node<K,V> newNode = new Node<>(key,value,parent);
            if (cmp>0){
                parent.right = newNode;
            }else {
                parent.left = newNode;
            }
            size++;
    
            //添加新节点之后的处理
            afterAdd(newNode);
        }
    
        public V get(K key){
            Node<K,V> node = node(key);
            if (node == null) return null;
            return node.value;
        }
    
        public void remove(K key){
            Node<K,V> node = node(key);
            if (node == null) return;
            size--;
    
            if (node.left != null && node.right != null){  //度为2的节点
                Node<K,V> s = successor(node); //找到后继节点
                node.key = s.key; //用后继节点的值覆盖度为2的节点的值
                node.value = s.value;
                node = s; //删除后继节点
            }
    
            //删除node节点,node节点的度必然是1或者0
            //删除叶子节点
            if (node.left == null && node.right == null){
                if (node.parent == null){
                    root = null;
                }else if(node == node.parent.left){
                    node.parent.left = null;
                }else {
                    node.parent.right = null;
                }
                //删除节点之后的处理
                afterRemove(node,null);
            }else { //删除度为1的节点
                Node<K,V> replacement = node.left != null?node.left:node.right;
                replacement.parent = node.parent;
                if (node.parent == null){
                    root = replacement;
                }else if (node == node.parent.left){
                    node.parent.left = replacement;
                }else {
                    node.parent.right = replacement;
                }
                //删除节点之后的处理
                afterRemove(node,replacement);
            }
    
        }
    
        public boolean containsKey(K key){
            return node(key) != null;
        }
    
        public boolean containsValue(V value){
            if (root == null) return false;
            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;
                if (node.left != null){
                    queue.offer(node.left);
                }
                if (node.right != null){
                    queue.offer(node.right);
                }
            }
            return false;
        }
    
        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);
        }
    
        public static abstract class Visitor<K,V>{
            boolean stop;
            public abstract boolean visit(K key,V value);
        }
    
        private boolean valEquals(V v1, V v2) {
            return v1 == null ? v2 == null : v1.equals(v2);
        }
    
        /**
         * key非空检查
         * @param key
         */
        private void keyNotNullCheck(K key){
            if (key == null){
                throw new IllegalArgumentException("key must not be null");
            }
        }
    
        /**
         * 根据key找到对应节点
         */
        private Node<K,V> node(K key){
            Node<K,V> node = root;
            while (node != null){
                int cmp = compare(node.key, key);
                if (cmp<0){
                    node = node.right;
                }else if(cmp>0){
                    node = node.left;
                }else {
                    return node;
                }
            }
            return null;
        }
    
        private int compare(K e1,K e2){
            if (comparator != null) return comparator.compare(e1,e2);
            return ((Comparable<K>)e1).compareTo(e2);
        }
    
        private void afterAdd(Node<K,V> node) {
            //判断情况1,是否是根节点
            if (node.parent == null) {
                black(node);
                return;
            }
    
            //判断情况2,父节点是否是黑色
            if(isBlack(node.parent)) return;
    
            //判断情况4,叔父节点是否是红色,如果是红色,不需要旋转,B树节点溢出
            if (isRed(node.parent.sibling())){
                black(node.parent);
                black(node.parent.sibling());
                Node<K,V> parent = red(node.parent.parent);
                afterAdd(parent);
                return;
            }
    
            //符合情况3,旋转,B树节点不会溢出
            Node<K,V> parent = node.parent;
            Node<K,V> grand = red(parent.parent); //将祖父节点染成红色
    
            if (parent.isLeftChild()){  //L
                if (node.isLeftChild()){ //LL
                    black(parent);
                }else { //LR
                    black(node);
                    rotateLeft(parent);
                }
                rotateRight(grand);
            }else { //R
                if (node.isLeftChild()){ //RL
                    black(node);
                    rotateRight(parent);
                }else {  //RR
                    black(parent);
                }
                rotateLeft(grand);
            }
        }
    
        private void afterRemove(Node<K,V> node, Node<K,V> replaceNode) {
    
            //如果符合情况1,被删除节点是红色节点,那么直接删除即可
            if (isRed(node)) return;
    
            //如果符合情况2,被删除节点的替代节点是红色节点,那么将替代节点染成黑色即可
            if (isRed(replaceNode)){
                black(replaceNode);
                return;
            }
    
            //符合情况三,被删除节点是黑色叶子节点
            Node<K,V> parent = node.parent;
    
            //3.1 被删除节点是根节点,那么直接删除即可
            if (parent == null) return;
    
            // 判断被删除的node是左还是右
            boolean left = parent.left == null || node.isLeftChild();
            Node<K,V> sibling = left ? parent.right : parent.left;
            // 黑色叶子节点被删除后,会导致B树节点下溢
            if (left){  //被删除的节点在左边,兄弟节点在右边
                // 3.2 兄弟节点是红色
                // 染色:兄弟节点染成黑色,父节点染成红色
                // 旋转:父节点左旋,让兄弟节点的左子节点成为兄弟节点,让原来的兄弟节点成为祖父节点
                if (isRed(sibling)){
                    black(sibling);
                    red(parent);
                    rotateLeft(parent);
                    //更换兄弟节点
                    sibling = parent.right;
                }
    
                // 兄弟节点必然是黑色,
                // 3.3 如果兄弟节点至少有一个红色子节点,可以向兄弟节点借一个
                if (isRed(sibling.left) || isRed(sibling.right)){
                    // 兄弟节点的左侧是红色子节点,
                    // 符合RL的情况,让兄弟节点先左旋,使红色子节点成为新的兄弟节点
                    if (isBlack(sibling.right)){
                        rotateLeft(sibling);
                        sibling = parent.right;
                    }
    
                    //染色
                    color(sibling,colorOf(parent));
                    black(parent);
                    black(sibling.right);
    
                    //父节点右旋
                    rotateLeft(parent);
                }else{
                    // 3.4 兄弟节点没有一个红色子节点,父节点向下合并
                    boolean parentBlack = isBlack(parent);
                    red(sibling);
                    black(parent);
                    //如果parent是黑色节点,会导致parent也下溢
                    if (parentBlack){
                        afterRemove(parent,null);
                    }
                }
            }else { //被删除的节点在右边,兄弟节点在左边
    
                // 3.2 兄弟节点是红色
                // 染色:兄弟节点染成黑色,父节点染成红色
                // 旋转:父节点右旋,让兄弟节点的右子节点成为兄弟节点,让原来的兄弟节点成为祖父节点
                if (isRed(sibling)){
                    black(sibling);
                    red(parent);
                    rotateRight(parent);
                    //更换兄弟节点
                    sibling = parent.left;
                }
    
                // 兄弟节点必然是黑色,
                // 3.3 如果兄弟节点至少有一个红色子节点,可以向兄弟节点借一个
                if (isRed(sibling.left) || isRed(sibling.right)){
                    // 兄弟节点的右侧是红色子节点,
                    // 符合LR的情况,让兄弟节点先左旋,使红色子节点成为新的兄弟节点
                    if (isBlack(sibling.left)){
                        rotateLeft(sibling);
                        sibling = parent.left;
                    }
    
                    //染色
                    color(sibling,colorOf(parent));
                    black(parent);
                    black(sibling.left);
    
                    //父节点右旋
                    rotateRight(parent);
                }else{
                    // 3.4 兄弟节点没有一个红色子节点,父节点向下合并
                    boolean parentBlack = isBlack(parent);
                    red(sibling);
                    black(parent);
                    //如果parent是黑色节点,会导致parent也下溢
                    if (parentBlack){
                        afterRemove(parent,null);
                    }
                }
            }
        }
    
        //左旋
        private void rotateLeft(Node<K,V> grand){
            Node<K,V> parent = grand.right;
            Node<K,V> child = parent.left;
            grand.right = child;
            parent.left = grand;
            afterRote(grand,parent,child);
        }
    
        //右旋
        private void rotateRight(Node<K,V> grand){
            Node<K,V> parent = grand.left;
            Node<K,V> child = parent.right;
            grand.left = child;
            parent.right = grand;
            afterRote(grand,parent,child);
        }
    
        private void afterRote(Node<K,V> grand, Node<K,V> parent, Node<K,V> child){
    
            // 更新parent的父节点
            parent.parent = grand.parent;
    
            if (grand.isLeftChild()){
                grand.parent.left = parent;
            }else if(grand.isRightChild()){
                grand.parent.right = parent;
            }else {
                root = parent;
            }
    
            //更新child
            if (child != null){
                child.parent = grand;
            }
    
            //更新grand的父节点
            grand.parent = parent;
        }
    
        /**
         * 找到后继节点
         */
        public Node<K,V> successor(Node<K,V> node){
            if(node == null) return null;
            if (node.right != null){
                Node<K,V> rightNode = node.right;
                while (rightNode.left != null){
                    rightNode = rightNode.left;
                }
                return rightNode;
            }
    
            while (node.parent != null && node == node.parent.right){
                node = node.parent;
            }
            return node.parent;
        }
    
        /**
         * 将节点染色
         * @param node
         * @param color
         * @return 染色后的节点
         */
        private Node<K,V> color(Node<K,V> node, boolean color){
            if (node == null) return node;
            node.color = color;
            return node;
        }
    
        /**
         * 将节点染成红色
         * @param node
         * @return
         */
        private Node<K,V> red(Node<K,V> node){
            return color(node,RED);
        }
    
        /**
         * 将节点染成黑色
         * @param node
         * @return
         */
        private Node<K,V> black(Node<K,V> node){
            return color(node,BLACK);
        }
    
        /**
         * 返回节点的颜色
         * @param node
         * @return
         */
        private boolean colorOf(Node<K,V> node){
            return node == null ? BLACK: node.color;
        }
    
        /**
         * 判断节点是否是黑色节点
         * @param node
         * @return
         */
        private boolean isBlack(Node<K,V> node){
            return colorOf(node) == BLACK;
        }
    
        /**
         * 判断节点是否是红色节点
         * @param node
         * @return
         */
        private boolean isRed(Node<K,V> node){
            return colorOf(node) == RED;
        }
    
        private static class Node<K,V>{
            public K key;
            public V value;
            public Node<K,V> left;
            public Node<K,V> right;
            public Node<K,V> parent;
            boolean color = RED;
            public Node(K key, V value, Node<K,V> parent){
                this.key = key;
                this.value = value;
                this.parent = parent;
            }
    
            /**
             * 是否是叶子节点
             * @return
             */
            public boolean isLeaf() {
                return left == null && right == null;
            }
    
            /**
             * 是否有两个子树
             * @return
             */
            public boolean hasTwoChildren() {
                return left != null && right != null;
            }
    
            /**
             * 是否是父节点的左子节点
             * @return
             */
            public boolean isLeftChild() {
                return parent != null && this == parent.left;
            }
    
            /**
             * 是否是父节点的右子节点
             * @return
             */
            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;
            }
    
        }
    
    }
    
    TreeMap分析
    • 时间复杂度(平均)
      添加、删除、搜索的平均时间复杂度都是O(logn)
    • 特点
      • key必须具备可比较性
      • 元素的分布是有顺序的

    在实际应用中,很多时候的需求是,Map中存储的元素不需要讲究顺序、key不需要具备可比较性,

    如果不考虑顺序、不考虑key的可比较性,Map有更好的实现方案,平均时间复杂度可以达到O(1),那就是采取哈希表来实现Map

    相关文章

      网友评论

          本文标题:14_映射(Map)

          本文链接:https://www.haomeiwen.com/subject/fenqektx.html