美文网首页
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