美文网首页
映射和哈希表

映射和哈希表

作者: freemanIT | 来源:发表于2022-03-28 10:47 被阅读0次

Map

Map 在有些编程语言中也叫字典, dictionary, Python, OC, Swift

每一个Key 有唯一性, 对应一个Value

可以使用链表和平衡二叉搜索树等结构来实现

Map 接口

    public interface Map<K, V> {
    int size();
    boolean isEmpty();
    void clear();
    V put(K key, V value);
    V get(K key);
    V remove(K key);
    boolean containsKey(K key);
    boolean containsValue(V value);
    void traversal(Visitor<K, V> visitor);
    
    public static abstract class Visitor<K, V> {
        boolean stop;
        public abstract boolean visit(K key, V value);
    }
    
    
}

具体实现

使用红黑树

key 不能为null, 且有可比较性

每个节点中存储key 和value

遍历时使用中序遍历, 按照一定顺序输出

时间复杂度, 添加删除, 搜索都是O(logn)

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;
    private Comparator<K> comparator;
    
    public TreeMap() {
        this(null);
    }
    
    public TreeMap(Comparator<K> comparator) {
        this.comparator = comparator;
    }
    
    
    @Override
    public int size() {
        return size;
    }

    @Override
    public boolean isEmpty() {
        return size == 0;
    }

    @Override
    public void clear() {
        root = null;
        size = 0;
    }

    @Override
    public V put(K key, V value) {
        keyNotNullCheck(key);
        // 只有一个根节点
        if (root == null) {
            root = new Node<>(key, value, null);
            size++;
            afterPut(root);
            return null;
        }

        // 其他节点
        // 找到父节点
        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.right;
            } else if (cmp < 0) {
                node = node.left;
            } else {
                node.key = key;
                V oldValue = node.value;
                node.value = value;
                return oldValue;
            }
        }
        Node<K, V> newNode = new Node<>(key, value, parent);
        if (cmp > 0) {
            parent.right = newNode;
        } else {
            parent.left = newNode;
        }
        size++;
        afterPut(newNode);
        return 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;
    }

    @Override
    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 (valEaquals(value, node.value)) return true;
            if (node.left != null) {
                queue.offer(node.left);
            }
            
            if (node.right != null) {
                queue.offer(node.right);
            }
        }
        
        return false;
    }

    @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);
    }
    
    private boolean valEaquals(V v1, V v2) {
        return v1 == null ? v2 == null : v1.equals(v2);
    }
    
    
    private V remove(Node<K, V> node) {
        if (node == null) return null;
        size--;
        V oldValue = node.value;
        if (node.hasTwoChildren()) {// 度为2 的节点
            // 找到后继节点
            Node<K, V> s = successor(node);
            // 用后继节点的值覆盖度为2 的节点
            node.key = s.key;
            node.value = s.value;
            // 删除后继节点
            node = s;
        }

        // 删除node 的节点, 度为1 或者0
        Node<K, V> replacement = node.left != null ? node.left : node.right;
        if (replacement != null) { // 度为1 的节点
            // 更改parent
            replacement.parent = node.parent;
            if (node.parent == null) {// node 是度为1 的节点且是根节点
                root = replacement;
            } else if (node == node.parent.left) {
                node.parent.left = replacement;
            } else {
                node.parent.right = replacement;
            }
            // 被删除的节点调整
            afterRomove(replacement);
        } else if (node.parent == null) {// 叶子节点, 且为根节点
            root = null;
            // 被删除的节点调整
            afterRomove(node);
        } else {// node 是叶子节点, 但不是根节点
            if (node == node.parent.left) {
                node.parent.left = null;
            } else { // node == node.parent.right
                node.parent.right = null;
            }
            // 被删除的节点调整
            afterRomove(node);
        }
        return oldValue;
    }
    
    private void afterRomove(Node<K, V> node) {
        // 如果删除的节点是红色
//      if (isRed(node)) return;

        // 用以取代node 的子节点是红色
        if (isRed(node)) {
            black(node);
            return;
        }

        Node<K, V> parent = node.parent;
        // 删除的是根节点
        if (parent == null) return;

        // 删除的时黑色叶子节点
        // 判断被删除的node 是左还是右边
        boolean left = parent.left == null || node.isLeftChild();
        Node<K, V> sibling = left ? parent.right : parent.left;
        if (left) {// 被删除的节点在左边, 兄弟节点在右边
            if (isRed(sibling)) { // 兄弟节点是红色
                black(sibling);
                red(parent);
                rotateLeft(parent);
                sibling = parent.right;
            }

            // 兄弟节点必然是黑色
            if (isBlack(sibling.left) && isBlack(sibling.right)) {
                // 兄弟节点没有一个是红色子节点, 父节点要向下合并
                boolean parentBlack = isBlack(parent);
                black(parent);
                red(sibling);
                if (parentBlack) {
                    afterRomove(parent);
                }
            } else {// 兄弟节点至少有一个是红色子节点
                // 兄弟节点的左边是黑色, 兄弟要旋转
                if (isBlack(sibling.right)) {
                    rotateRight(sibling);
                    sibling = parent.right;
                }

                color(sibling, colorOf(parent));
                black(sibling.right);
                black(parent);
                rotateLeft(parent);
            }
        } else {// 被删除的节点在右边, 兄弟节点在左边
            if (isRed(sibling)) { // 兄弟节点是红色
                black(sibling);
                red(parent);
                rotateRight(parent);
                sibling = parent.left;
            }

            // 兄弟节点必然是黑色
            if (isBlack(sibling.left) && isBlack(sibling.right)) {
                // 兄弟节点没有一个是红色子节点, 父节点要向下合并
                boolean parentBlack = isBlack(parent);
                black(parent);
                red(sibling);
                if (parentBlack) {
                    afterRomove(parent);
                }
            } else {// 兄弟节点至少有一个是红色子节点
                // 兄弟节点的左边是黑色, 兄弟要旋转
                if (isBlack(sibling.left)) {
                    rotateLeft(sibling);
                    sibling = parent.left;
                }

                color(sibling, colorOf(parent));
                black(sibling.left);
                black(parent);
                rotateRight(parent);
            }
        }

    }
    
    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) {
                node = node.right;
            } else {
                node = node.left;
            }
        }
        return null;
    }
    
    private void afterPut(Node<K, V> node) {
        Node<K, V> parent = node.parent;
        // 如果是根节点
        if (parent == null) {
            black(node);
            return;
        }
        
        // 如果父节点是黑色, 直接返回
        if (isBlack(parent)) return;
        
        // 叔父节点
        Node<K, V> uncle = parent.sibling();
        // 祖父节点
        Node<K, V> grand = red(parent.parent);
        if (isRed(uncle)) { // 叔父节点是红色
            black(parent);
            black(uncle);
            // 把祖父节点当做是新添加的节点
            afterPut(red(grand));
            return;
        }
        
        // 叔父节点不是红色
        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 rotateLeft(Node<K, V> grand) {
        Node<K, V> parent = grand.right;
        Node<K, V> child = parent.left;
        grand.right = child;
        parent.left = grand;
        afterRotate(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;
        afterRotate(grand, parent, child);
    }
    
    private void afterRotate(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 { // grand 是根节点
            root = parent;
        }
        // 更新child 的parent
        if (child != null) {
            child.parent = grand;
        }
        // 更新grand 的parent
        grand.parent = parent;
        
    }
    
    private Node<K, V> color(Node<K, V> node, boolean color) {
        if (node == null) return node;
        node.color = color;
        return node;
    }
    
    private Node<K, V> red(Node<K, V> node) {
        return color(node, RED);
    }
    
    private Node<K, V> black(Node<K, V> node) {
        return color(node, BLACK);
    }
    
    private boolean colorOf(Node<K, V> node) {
        return node == null ? BLACK : node.color;
    }
    
    private boolean isRed(Node<K, V> node) {
        return colorOf(node) == RED;
    }
    
    private boolean isBlack(Node<K, V> node) {
        return colorOf(node) == BLACK;
    }
    
    private int compare(K e1, K e2) {
        if (comparator != null) {
            comparator.compare(e1, e2);
        }
        return ((Comparable<K>)e1).compareTo(e2);
    }
    
    private void keyNotNullCheck(K key) {
        if (key == null) {
            throw new IllegalArgumentException("key must not be null");
        }
    }
    
    private Node<K, V> predecessor(Node<K, V> node) {
        Node<K, V> p = node.left;
        // 前驱节点在左子树中, left.right.right.....
        if (p != null) {
            while (p.right != null) {
                p = p.right;
            }
            return p;
        }
        // 从父节点, 祖父节点中寻找前驱节点
        while (node.parent != null && node == node.parent.left) {
            node = node.parent;
        }
        return node.parent;
        
    }
    
    private Node<K, V> successor(Node<K, V> node) {
        Node<K, V> p = node.right;
        // 前驱节点在左子树中, right.left.left.....
        if (p != null) {
            while (p.left != null) {
                p = p.left;
            }
            return p;
        }
        // 从父节点, 祖父节点中寻找前驱节点
        while (node.parent != null && node == node.parent.right) {
            node = node.parent;
        }
        return node.parent;
        
    }
    
    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;
        
        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;
        }
        
        public boolean isLeftChild() {
            return parent != null && this == parent.left;
        }
        
        public boolean isRightChild() {
            return parent != null && this == parent.right;
        }
        
        public Node<K, V> sibling() {
            if (isLeftChild()) return parent.right;
            if (isRightChild()) return parent.left;
            return null;
        }
        
    }

}

哈希表

有些需求为, Map 中存储的元素不需要讲究顺序, Key不需要可比较性, 平均的复杂度可以达到O(1)

可以使用哈希表来实现Map

哈希表也叫散列函数, 是时间换空间的典型应用, 内部的数组元素, 也叫桶Bucket, 整个数组叫Buckets, 或者Bucket Array

哈希冲突

也叫作哈希碰撞, 两个不同key, 经过哈希计算出相同结果

解决哈希冲突的常见方法

  1. 开放定址法, 按照一定规则向其他地址探测, 直到遇到空桶
  2. 再哈希法, 设计多个哈希函数
  3. 链地址法, 比如通过链表将同一个index 的元素串起来

JDK1.8 的后续冲突解决方案

  1. 默认使用单向链表将元素串起来, 每次从链表头部遍历, 比双向链表少一个指针, 节省内存空间
  2. 在添加元素时, 可能会由单向链表转为红黑树存储元素
    1. 当哈希表容量 >= 64, 且单向链表节点数量大于8时
  3. 当红黑树节点数量少到一定程度时, 又会转为单向链表
  4. JDK1.8 中哈希表使用链表+红黑树解决哈希冲突
哈希表

哈希函数实现步骤

  1. 先生成key 的哈希值, 必须为整数
  2. 再让key 的哈希值跟数组的大小进行相关运算, 生成一个索引值
  3. 可以使用&位运算取代%运算, 数组长度为2的幂次方
  4. 良好的哈希函数, 哈希值均匀分布, 减少哈希冲突次数, 提升哈希表性能

生成key 的哈希值

key 的常见种类, 整数, 浮点数, 字符串, 自定义对象

尽量让每个key 的哈希值是唯一的, 让key 的所有信息参与运算

在Java 中 HashMap 的key 必须实现hashCode, equals 方法, 也允许key 为null

  1. 整数, 整数值作为哈希值

  2. 浮点数, 将存储的二进制格式转为整数值

  3. Long 和Double, 高32bit 和低32bit 混合计算出32bit 的哈希值, 充分利用所有信息计算出哈希值

long,double生成哈希值
  1. 字符串, 由若干字符组成, 例如jack, ((jn + a) * n + c) * n + k, jdk中n 为31, 31是一个奇素数, 31i, 优化为(i<<5)-i

自定义对象哈希值

public class Person {
    private int age;
    private float height;
    private String name;
    
    public Person(int age, float height, String name) {
        this.age = age;
        this.height = height;
        this.name = name;
    }
    
    /**
     * hash 冲突时, 比较2 个key 是否相等
     */
    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null || obj.getClass() != getClass()) return false;
        //if (obj == null | !(obj instanceof Person)) return false;
        Person person = (Person) obj;
        return person.age == age
                && person.height == height
                && (person.name == null ? name == null : person.name.equals(name));
    }
    
    /**
     * 计算索引时调用
     */
    @Override
    public int hashCode() {
        
        int hashCode = Integer.hashCode(age);
        hashCode = hashCode *31 + Float.hashCode(height);
        hashCode = hashCode * 31 + (name != null ? name.hashCode() : 0);
        
        return hashCode;
    }
    
}
  1. 自定义对象作为key, 最好同时重新hashCode, 和equals
  2. equals, 用以判断两个key 是否为同一个key
    1. 自反性, 对于任何非null 的x, x.equals(x), 必须返回true
    2. 对称性, 对于任何非null 的x, y, 如果y.equals(x) 返回true, x.equals(y) 必须返回true
    3. 传递性, 对于任何非null 的x, y, z, 如果x.equals(y), y.equals(z) 返回true, 那么x.equals(z) 必须返回true
    4. 一致性, 对于任何非null 的x, y, 只要equals 的比较操作在对象中所用的信息没有被修改, 多次调用equals 就会一致返回true, 或者一致返回false
    5. 对于任何非null 的x, x.equals(null) 必须返回false
  3. hashCode, 必须保证equals 为true 的2 个key 的哈希值一样, 反过来, hashCode 相等的key, 不一定equals 的true

哈希值相等, 索引必然相等

哈希值不等, 哈希函数求结果索引, 可能相等, 取模运算可能遇到相等情况

HashMap 的key 必须实现hashCode 和equals, 也允许key 为null

完整代码

public class HashMap<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>[] table;
    
    private static final int DEFAULT_CAPACITY = 1 << 4;
    private static final float DEFAULT_LOAD_FACTOR = 0.75f;
    
    public HashMap() {
        table = new Node[DEFAULT_CAPACITY];
    }
    
    @Override
    public int size() {
        return size;
    }

    @Override
    public boolean isEmpty() {
        return size == 0;
    }

    @Override
    public void clear() {
        if (size == 0) return;
        size = 0;
        for (int i = 0; i < table.length; i++) {
            table[i] = null;
        }
    }

    @Override
    public V put(K key, V value) {
        resize();
        int index = index(key);
        // 取出index 位置的红黑树根节点
        Node<K, V> root = table[index];
        if (root == null) {
            root = createNode(key, value, null);
            table[index] = root;
            size++;
            fixAfterPut(root);
            return null;
        }
        
        // 添加新的节点到红黑树上
        Node<K, V> parent = root;
        Node<K, V> node = root;
        int cmp = 0;
        K k1 = key;
        int h1 = hash(k1);
        Node<K, V> result = null;
        boolean searched = false; // 是否已经搜索过这个key
        do {
            parent = node;
            K k2 = node.key;
            int h2 = node.hash;
            
            if (h1 > h2) {
                cmp = 1;
            } else if (h1 < h2) {
                cmp = -1;
            } else if (Objects.equals(k1, k2)) {
                cmp = 0;
            } else if (k1 != null && k2 != null
                    && k1.getClass() == k2.getClass()
                    && k1 instanceof Comparable
                    && (cmp = ((Comparable) k1).compareTo(k2)) != 0) {
                // cmp > 0, < 0, == 0
                // 为0 时, 不考虑是同一个对象, 往后扫描
            } else if (searched) { // 扫描, 然后再根据内存地址大小决定左右, 已经扫描过这个key
                cmp = System.identityHashCode(k1) - System.identityHashCode(k2);
            } else { // searched == false, 未扫描过这个key
                if ((node.left != null && (result = node(node.left, k1)) != null)
                        || (node.right != null && (result = node(node.right, k1)) != null)) {
                    node = result;
                    cmp = 0;
                } else {// 不存在这个key
                    searched = true;
                    cmp = System.identityHashCode(k1) - System.identityHashCode(k2);
                }
            }
            
            
            if (cmp > 0) {
                node = node.right;
            } else if (cmp < 0) {
                node = node.left;
            } else {
                V oldValue = node.value;
                node.key = key;
                node.value = value;
                node.hash = h1;
                return oldValue;
            }
        } while (node != null);
        
        // 看看插入到父节点的哪个位置
        Node<K, V> newNode = createNode(key, value, parent);
        if (cmp > 0) {
            parent.right = newNode;
        } else {
            parent.left = newNode;
        }
        size++;
        fixAfterPut(newNode);
        return 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;
    }

    @Override
    public boolean containsValue(V value) {
        
        if (size == 0) return false;
        Queue<Node<K, V>> queue = new LinkedList<>();
        for (int i = 0; i < table.length; i++) {
            if (table[i] == null) continue;
            queue.offer(table[i]);
            while (!queue.isEmpty()) {
                Node<K, V> node = queue.poll();
                if (Objects.equals(value, node.value)) return true;
                if (node.left != null) {
                    queue.offer(node.left);
                }
                
                if (node.right != null) {
                    queue.offer(node.right);
                }
            }
        }
        
        return false;
    }

    @Override
    public void traversal(Visitor<K, V> visitor) {
        if (size == 0 || visitor == null) return;
        Queue<Node<K, V>> queue = new LinkedList<>();
        for (int i = 0; i < table.length; i++) {
            if (table[i] == null) continue;
            queue.offer(table[i]);
            while (!queue.isEmpty()) {
                Node<K, V> node = queue.poll();
                if (visitor.visit(node.key, node.value)) return;
                if (node.left != null) {
                    queue.offer(node.left);
                }
                
                if (node.right != null) {
                    queue.offer(node.right);
                }
            }
        }
    }
    
    public void print() {
        if (size == 0) return;
        
        for (int i = 0; i < table.length; i++) {
            final Node<K, V> root = table[i];
            System.out.println("[index = " + i + "]");
            BinaryTrees.println(new BinaryTreeInfo() {
                
                @Override
                public Object string(Object node) {
                    return node;
                }
                
                @Override
                public Object root() {
                    return root;
                }
                
                @Override
                public Object right(Object node) {
                    return ((Node<K, V>)node).right;
                }
                
                @Override
                public Object left(Object node) {
                    return ((Node<K, V>)node).left;
                }
            });
            System.out.println("-----------------------------------");
        }
        
    }
    
    protected Node<K, V> createNode(K key, V value, Node<K, V> parent) {
        return new Node<>(key, value, parent);
    }
    
    protected void afterRemove(Node<K, V> willNode, Node<K, V> removeNode) {}
    
    private void resize() {
        // 装填因子 <= 0.75
        if (size / table.length <= DEFAULT_LOAD_FACTOR) return;
        
        Node<K, V> []oldTable = table;
        table = new Node[oldTable.length << 1];
        
        Queue<Node<K, V>> queue = new LinkedList<>();
        for (int i = 0; i < oldTable.length; i++) {
            if (oldTable[i] == null) continue;
            queue.offer(oldTable[i]);
            while (!queue.isEmpty()) {
                Node<K, V> node = queue.poll();
                
                if (node.left != null) {
                    queue.offer(node.left);
                }
                
                if (node.right != null) {
                    queue.offer(node.right);
                }
                // 挪动放到最后
                moveNode(node);
            }
        }
    }
    
    private void moveNode(Node<K, V> newNode) {
        newNode.parent = null;
        newNode.left = null;
        newNode.right = null;
        newNode.color = RED;
        
        int index = index(newNode);
        // 取出index 位置的红黑树根节点
        Node<K, V> root = table[index];
        if (root == null) {
            root = newNode;
            table[index] = root;
            fixAfterPut(root);
            return;
        }
        
        // 添加新的节点到红黑树上
        Node<K, V> parent = root;
        Node<K, V> node = root;
        int cmp = 0;
        K k1 = newNode.key;
        int h1 = newNode.hash;
        do {
            parent = node;
            K k2 = node.key;
            int h2 = node.hash;
            
            if (h1 > h2) {
                cmp = 1;
            } else if (h1 < h2) {
                cmp = -1;
            } else if (k1 != null && k2 != null
                    && k1.getClass() == k2.getClass()
                    && k1 instanceof Comparable
                    && (cmp = ((Comparable) k1).compareTo(k2)) != 0) {
                // cmp > 0, < 0, == 0
                // 为0 时, 不考虑是同一个对象, 往后扫描
            } else {
                cmp = System.identityHashCode(k1) - System.identityHashCode(k2);
            }
            
            if (cmp > 0) {
                node = node.right;
            } else if (cmp < 0) {
                node = node.left;
            }
        } while (node != null);
        
        // 看看插入到父节点的哪个位置
        newNode.parent = parent;
        if (cmp > 0) {
            parent.right = newNode;
        } else {
            parent.left = newNode;
        }
        fixAfterPut(newNode);
    }
    
    protected V remove(Node<K, V> node) {
        if (node == null) return null;
        size--;
        Node<K, V> willNode = node;
        V oldValue = node.value;
        if (node.hasTwoChildren()) {// 度为2 的节点
            // 找到后继节点
            Node<K, V> s = successor(node);
            // 用后继节点的值覆盖度为2 的节点
            node.key = s.key;
            node.value = s.value;
            node.hash = s.hash;
            // 删除后继节点
            node = s;
        }

        // 删除node 的节点, 度为1 或者0
        Node<K, V> replacement = node.left != null ? node.left : node.right;
        int index = index(node);
        if (replacement != null) { // 度为1 的节点
            // 更改parent
            replacement.parent = node.parent;
            if (node.parent == null) {// node 是度为1 的节点且是根节点
                table[index] = replacement;
            } else if (node == node.parent.left) {
                node.parent.left = replacement;
            } else {
                node.parent.right = replacement;
            }
            // 被删除的节点调整
            fixAfterRomove(replacement);
        } else if (node.parent == null) {// 叶子节点, 且为根节点
            table[index] = null;
            // 被删除的节点调整
//          afterRomove(node);
        } else {// node 是叶子节点, 但不是根节点
            if (node == node.parent.left) {
                node.parent.left = null;
            } else { // node == node.parent.right
                node.parent.right = null;
            }
            // 被删除的节点调整
            fixAfterRomove(node);
        }
        afterRemove(willNode, node);
        return oldValue;
    }
    
    private Node<K, V> successor(Node<K, V> node) {
        if (node == null) return null;
        Node<K, V> p = node.right;
        // 前驱节点在左子树中, right.left.left.....
        if (p != null) {
            while (p.left != null) {
                p = p.left;
            }
            return p;
        }
        // 从父节点, 祖父节点中寻找前驱节点
        while (node.parent != null && node == node.parent.right) {
            node = node.parent;
        }
        return node.parent;
        
    }
    
    private Node< K, V> node(K key) {
        Node<K, V> root = table[index(key)];
        return root == null ? null : node(root, key);
    }
    
    private Node< K, V> node(Node<K, V> node, K k1) {
        int h1 = hash(k1);
        // 存储查找结果
        Node<K, V> result = null;
        int cmp = 0;
        while (node != null) {
            K k2 = node.key;
            int h2 = node.hash;
            // 先比较hash 值
            if (h1 > h2) {
                node = node.right;
            } else if (h1 < h2) {
                node = node.left;
            } else if (Objects.equals(k1, k2)) {
                return node;
            } else if (k1 != null && k2 != null 
                    && k1.getClass() == k2.getClass() 
                    && k1 instanceof Comparable
                    && (cmp = ((Comparable) k1).compareTo(k2)) != 0) {
                node = cmp > 0 ? node.right : node.left;
//              if (cmp > 0) {
//                  node = node.right;
//              } else if (cmp < 0) {
//                  node = node.left;
//              } else {
//                  return node;
//              }
                // hash 值相等, 不具备可比较性, 不是equals
            } else if (node.right != null && (result = node(node.right, k1)) != null) { 
                return result;
            } else { // 左边查找
                node = node.left;
            }
//          else if (node.left != null && (result = node(node.left, k1)) != null) { 
//              return result;
//          } else {
//              return null;
//          }
            
//          int cmp = compare(key, node.key, h1, node.hash);
//          if (cmp == 0) return node;
//          if (cmp > 0) {
//              node = node.right;
//          } else if (cmp < 0) {
//              node = node.left;
//          }
        }
        return null;
    }
    
    /**
     * 根据key 生成对应的索引, 在桶数组中的位置
     * @param key
     * @return 
     */
    private int index(K key) {
        return hash(key) & (table.length - 1);
    }
    
    private int hash(K key) {
        if (key == null) return 0;
        int hash = key.hashCode();
        return hash ^ (hash >>> 16);
    }
    
    private int index(Node<K, V> node) {
        return node.hash & (table.length - 1);
    }
    
    private void fixAfterRomove(Node<K, V> node) {
        // 如果删除的节点是红色
//      if (isRed(node)) return;

        // 用以取代node 的子节点是红色
        if (isRed(node)) {
            black(node);
            return;
        }

        Node<K, V> parent = node.parent;
        // 删除的是根节点
        if (parent == null) return;

        // 删除的时黑色叶子节点
        // 判断被删除的node 是左还是右边
        boolean left = parent.left == null || node.isLeftChild();
        Node<K, V> sibling = left ? parent.right : parent.left;
        if (left) {// 被删除的节点在左边, 兄弟节点在右边
            if (isRed(sibling)) { // 兄弟节点是红色
                black(sibling);
                red(parent);
                rotateLeft(parent);
                sibling = parent.right;
            }

            // 兄弟节点必然是黑色
            if (isBlack(sibling.left) && isBlack(sibling.right)) {
                // 兄弟节点没有一个是红色子节点, 父节点要向下合并
                boolean parentBlack = isBlack(parent);
                black(parent);
                red(sibling);
                if (parentBlack) {
                    fixAfterRomove(parent);
                }
            } else {// 兄弟节点至少有一个是红色子节点
                // 兄弟节点的左边是黑色, 兄弟要旋转
                if (isBlack(sibling.right)) {
                    rotateRight(sibling);
                    sibling = parent.right;
                }

                color(sibling, colorOf(parent));
                black(sibling.right);
                black(parent);
                rotateLeft(parent);
            }
        } else {// 被删除的节点在右边, 兄弟节点在左边
            if (isRed(sibling)) { // 兄弟节点是红色
                black(sibling);
                red(parent);
                rotateRight(parent);
                sibling = parent.left;
            }

            // 兄弟节点必然是黑色
            if (isBlack(sibling.left) && isBlack(sibling.right)) {
                // 兄弟节点没有一个是红色子节点, 父节点要向下合并
                boolean parentBlack = isBlack(parent);
                black(parent);
                red(sibling);
                if (parentBlack) {
                    fixAfterRomove(parent);
                }
            } else {// 兄弟节点至少有一个是红色子节点
                // 兄弟节点的左边是黑色, 兄弟要旋转
                if (isBlack(sibling.left)) {
                    rotateLeft(sibling);
                    sibling = parent.left;
                }

                color(sibling, colorOf(parent));
                black(sibling.left);
                black(parent);
                rotateRight(parent);
            }
        }

    }
    
    private void fixAfterPut(Node<K, V> node) {
        Node<K, V> parent = node.parent;
        // 如果是根节点
        if (parent == null) {
            black(node);
            return;
        }
        
        // 如果父节点是黑色, 直接返回
        if (isBlack(parent)) return;
        
        // 叔父节点
        Node<K, V> uncle = parent.sibling();
        // 祖父节点
        Node<K, V> grand = red(parent.parent);
        if (isRed(uncle)) { // 叔父节点是红色
            black(parent);
            black(uncle);
            // 把祖父节点当做是新添加的节点
            fixAfterPut(red(grand));
            return;
        }
        
        // 叔父节点不是红色
        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 rotateLeft(Node<K, V> grand) {
        Node<K, V> parent = grand.right;
        Node<K, V> child = parent.left;
        grand.right = child;
        parent.left = grand;
        afterRotate(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;
        afterRotate(grand, parent, child);
    }
    
    private void afterRotate(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 { // grand 是根节点
            table[index(grand)] = parent;
        }
        // 更新child 的parent
        if (child != null) {
            child.parent = grand;
        }
        // 更新grand 的parent
        grand.parent = parent;
        
    }
    
    private Node<K, V> color(Node<K, V> node, boolean color) {
        if (node == null) return node;
        node.color = color;
        return node;
    }
    
    private Node<K, V> red(Node<K, V> node) {
        return color(node, RED);
    }
    
    private Node<K, V> black(Node<K, V> node) {
        return color(node, BLACK);
    }
    
    private boolean colorOf(Node<K, V> node) {
        return node == null ? BLACK : node.color;
    }
    
    private boolean isRed(Node<K, V> node) {
        return colorOf(node) == RED;
    }
    
    private boolean isBlack(Node<K, V> node) {
        return colorOf(node) == BLACK;
    }
    
    protected static class Node<K, V> {
        int hash;
        K key;
        V value;
        boolean color = RED;
        Node<K, V> left;
        Node<K, V> right;
        Node<K, V> parent;
        
        public Node(K key, V value, Node<K, V> parent) {
            this.key = key;
            int hash = key == null ? 0 : key.hashCode();
            this.hash = hash ^ (hash >>> 16);
            this.value = value;
            this.parent = parent;
        }
        
        public boolean hasTwoChildren() {
            return left != null && right != null;
        }
        
        public boolean isLeftChild() {
            return parent != null && this == parent.left;
        }
        
        public boolean isRightChild() {
            return parent != null && this == parent.right;
        }
        
        public Node<K, V> sibling() {
            if (isLeftChild()) return parent.right;
            if (isRightChild()) return parent.left;
            return null;
        }
        
        @Override
        public String toString() {
            return "Node_" + key + "_" + value;
        }
    }
    
}

HashMap 升级为LinkedHashMap

在HashMap 的基础上维护元素的添加顺序, 使得遍历结果遵从添加顺序

删除度为2 的节点31, 调整顺序, 更换node 的前驱后继节点的连接位置

删除数据调整顺序
public class LinkedHashMap<K, V> extends HashMap<K, V> {
    
    private LinkedNode<K, V> first;
    private LinkedNode<K, V> last;
    
    @Override
    public void clear() {
        super.clear();
        first = null;
        last = null;
    }

    @Override
    public boolean containsValue(V value) {
        LinkedNode<K, V> node = first;
        while (node != null) {
            if (Objects.equals(value, node.value)) return true;
            node = node.next;
        }
        return false;
    }

    @Override
    public void traversal(Visitor<K, V> visitor) {
        if (visitor == null) return;
        LinkedNode<K, V> node = first;
        while (node != null) {
            if (visitor.visit(node.key, node.value)) return;
            node = node.next;
        }
    }
    
    
    @Override
    protected void afterRemove(Node<K, V> willNode, Node<K, V> removeNode) {
        LinkedNode<K, V> node1 = (LinkedNode<K, V>) willNode;
        LinkedNode<K, V> node2 = (LinkedNode<K, V>) removeNode;
        if (node1 != node2) {
            // 交换两者在链表中的位置
            // 交换prev
            LinkedNode<K, V> tmp = node1.prev;
            node1.prev = node2.prev;
            node2.prev = tmp;
            if (node1.prev == null) {
                first = node1;
            } else {
                node1.prev.next = node1;
            }
            if (node2.prev == null) {
                first = node2;
            } else {
                node2.prev.next = node2;
            }
            
            // 交换next
            tmp = node1.next;
            node1.next = node2.next;
            node2.next = tmp;
            if (node1.next == null) {
                last = node1;
            } else {
                node1.next.prev = node1;
            }
            if (node2.next == null) {
                last = node2;
            } else {
                node2.next.prev = node2;
            }
            
        }
        
        LinkedNode<K, V> prev = node2.prev;
        LinkedNode<K, V> next = node2.next;
        
        if (prev == null) {
            first = next;
        } else {
            prev.next = next;
        }
        
        if (next == null) {
            last = prev;
        } else {
            next.prev = prev;
        }
        
    }
    
    
    @Override
    protected Node<K, V> createNode(K key, V value, Node<K, V> parent) {
        
        LinkedNode<K, V> node = new LinkedNode(key, value, parent);
        
        if (first == null) {
            first = last = node;
        } else {
            last.next = node;
            node.prev = last;
            last = node;
        }
        
        return node;
    }
    
    private static class LinkedNode<K, V> extends Node<K, V> {
        LinkedNode<K, V> prev;
        LinkedNode<K, V> next;
        
        public LinkedNode(K key, V value, Node<K, V> parent) {
            super(key, value, parent);
            
        }
    }
}

相关文章

  • 哈希表

    映射(Map) 和 集合(Set) 哈希表(HashTable)、哈希函数(Hash Function)、哈希碰撞...

  • 映射和哈希表

    Map Map 在有些编程语言中也叫字典, dictionary, Python, OC, Swift 每一个Ke...

  • Tourist with Data Structure Thir

    探索哈希表 概念 哈希集合:哈希集合是集合数据结构的实现之一,用于存储非重复值。哈希映射 :哈希映射是映射数据结构...

  • 算法通关 - 哈希表

    哈希表(HashMap&HashSet) 哈希表是一种建立映射关系的数据结构,保存key和value。它的主要实现...

  • Python数据结构-哈希表(Hash Table)

    一、哈希表 哈希表(Hash Table):通过键 key 和一个映射函数 Hash(key) 计算出对应的值 v...

  • 哈希表

    1.哈希表原理 哈希表的基本思想是:用哈希函数 f: key -> address将关键字映射到该记录在表中的位置...

  • 15_哈希表(Hash Table)

    哈希表初识 哈希表,也叫做散列表 它是如何实现高效处理数据的?假设我们利用哈希表来实现映射,存储三对数据:put(...

  • 哈希表、映射、集合

    哈希表基本概念 维基百科比较清楚,可以参考。我觉得可能最好跟着打字一遍,比读一遍能理解更多,往往太多字,很容易放弃...

  • Hash

    哈希表就是 依据关键字可以根据一定的算法(哈希函数)映射到表中的特定位置 的思想建立的表。因此哈希表最大的特点就是...

  • 算法训练营第二周总结(精辟要点)

    一、概述 这周主要学习了哈希表、映射、集合、树、二叉树、二叉搜索树、泛型递归、树的递归 二、哈希表 哈希表: 也叫...

网友评论

      本文标题:映射和哈希表

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