【188】Java8利用AVL树实现Map

news2025/1/15 16:30:38

AVL树又被叫做平衡二叉搜索树、平衡二叉树。AVL是其发明者的首字母缩写。

这篇文章中,AVLTreeMap 类集成了 java.util.Map 接口,并利用 AVL 树结构实现了 Map 接口的所有方法。本文还给出了测试代码。

为什么要发明AVL树?

当我按照从小到大或者从大到小的顺序向二叉查找树插入节点,二叉查找树就会退化成一个链表。这是二叉查找树的最差情况。搜索、插入、删除的最差效率都是 O(N)。这样就失去了用二叉查找树优化查找方法的意义。

就算不是最坏情况,也会出现非常不平衡的树,造成查找效率大于 O(logN) 小于 O(N) 。注意这里 logN 是以2为底N的对数。

AVL树的时间复杂度

因为含有数学公式,就使用图片了。
在这里插入图片描述

在这里插入图片描述

代码实现

AVLTreeMap.java


package zhangchao.avl;

import java.util.*;

/**
 * 利用AVL树,也就是平衡二叉树来实现map
 * @author zhangchao
 * @param <K> 键
 * @param <V> 值
 */
public class AVLTreeMap<K,V> implements Map<K, V>{
    // 根节点
    private Node<K, V> root = null;

    private Comparator<K> comparator;

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

    @Override
    public int size() {
        if (null == root) {
            return 0;
        }
        int size = 0;
        // 当前层的节点列表
        List<Node<K, V>> currentLevel = new ArrayList<>();
        currentLevel.add(root);
        while (!currentLevel.isEmpty()) {
            size += currentLevel.size();
            // 下一层的节点列表
            List<Node<K, V>> nextLevel = new ArrayList<>();
            for (Node<K, V> tmpNode : currentLevel) {
                if (null != tmpNode.leftChild) {
                    nextLevel.add(tmpNode.leftChild);
                }
                if (null != tmpNode.rightChild) {
                    nextLevel.add(tmpNode.rightChild);
                }
            }
            currentLevel.clear();
            currentLevel.addAll(nextLevel);
        }
        return size;
    }

    @Override
    public boolean isEmpty() {
        return (null == root);
    }

    @Override
    public boolean containsKey(Object keyObj) {
        if (null == root) {
            return false;
        }
        K key = (K)keyObj;
        Node<K, V> current = this.root;
        while(null != current) {
            int compareResult = this.comparator.compare(key, current.key);
            if (compareResult < 0) {
                current = current.leftChild;
            } else if (compareResult > 0) {
                current = current.rightChild;
            } else {
                return true;
            }
        }
        return false;
    }

    @Override
    public boolean containsValue(Object value) {
        if (null == this.root) {
            return false;
        }
        List<Node<K, V>> nodeList = this.nodeList();
        for (Node<K, V> node : nodeList) {
            if (null == value && null == node.value) {
                return true;
            }
            if (null != value && value.equals(node.value)) {
                return true;
            }
        }
        return false;
    }

    @Override
    public V get(Object keyObj) {
        if (null == this.root) {
            return null;
        }
        K key = (K)keyObj;
        Node<K, V> current = this.root;
        while(null != current) {
            int compareResult = this.comparator.compare(key, current.key);
            if (compareResult < 0) {
                current = current.leftChild;
            } else if (compareResult > 0) {
                current = current.rightChild;
            } else {
                return current.value;
            }
        }
        return null;
    }

    /**
     * 右旋操作
     * @param parent 爷爷节点
     * @param y 父级节点
     * @param x 子级节点
     */
    private void rotateRight(Node<K, V> parent, Node<K, V> y, Node<K, V> x) {
        y.leftChild = x.rightChild;
        x.rightChild = y;
        if (null == parent) {
            this.root = x;
        } else {
            // 判断原来的y是parent的左子节点还是右子节点。
            if (null != parent.leftChild && 0 == this.comparator.compare(parent.leftChild.key, y.key)) {
                parent.leftChild = x;
            } else if (null != parent.rightChild && 0 == this.comparator.compare(parent.rightChild.key, y.key)) {
                parent.rightChild = x;
            }
        }
        y.height = this.calHeight(y);
        x.height = this.calHeight(x);
        if (null != parent) {
            parent.height = this.calHeight(parent);
        }
    }

    /**
     * 左旋操作
     * @param parent 爷爷节点
     * @param y 父级节点
     * @param x 子级节点
     */
    private void rotateLeft(Node<K, V> parent, Node<K, V> y, Node<K, V> x) {
        y.rightChild = x.leftChild;
        x.leftChild = y;
        if (null == parent) {
            this.root = x;
        } else {
            // 判断原来的y是parent的左子节点还是右子节点。
            if (null != parent.leftChild && 0 == this.comparator.compare(parent.leftChild.key, y.key)) {
                parent.leftChild = x;
            } else if (null != parent.rightChild && 0 == this.comparator.compare(parent.rightChild.key, y.key)) {
                parent.rightChild = x;
            }
        }
        y.height = this.calHeight(y);
        x.height = this.calHeight(x);
        if (null != parent) {
            parent.height = this.calHeight(parent);
        }
    }

    @Override
    public V put(K key, V value) {
        if (null == this.root) {
            this.root = new Node<>();
            this.root.key = key;
            this.root.value = value;
            this.root.height = 1;
            return null;
        }
        // 如果key是全新的,保存所有的父亲节点。
        List<Node<K, V>> linkList = new ArrayList<>();
        // 如果key是全新的,这个变量是新节点的父亲节点。
        Node<K, V> parent = null;
        Node<K, V> current = root;
        int compareResult = 0;
        while (null != current) {
            compareResult = this.comparator.compare(key, current.key);
            if (compareResult < 0) {
                parent = current;
                linkList.add(parent);
                current = current.leftChild;
            } else if (compareResult > 0) {
                parent = current;
                linkList.add(parent);
                current = current.rightChild;
            } else {
                // 有相等的key,直接设置值就可以了。
                V oldValue = current.value;
                current.value = value;
                return oldValue;
            }
        }
        Node<K, V> newItem = new Node<K, V>();
        newItem.key = key;
        newItem.value = value;
        newItem.height = 1;
        if (compareResult < 0) {
            parent.leftChild = newItem;
        } else if (compareResult > 0) {
            parent.rightChild = newItem;
        }
        // 更新祖先节点的高度
        final int size = linkList.size();
        for (int i = size - 1; i >= 0; i--) {
            Node<K, V> item = linkList.get(i);
            item.height = calHeight(item);
        }

        linkList.add(newItem);
        int parentSize = linkList.size();
        for (int i = parentSize - 1; i >= 0; i--) {
            // 当前节点
            Node<K, V> z = linkList.get(i);
            // z的父节点,如果z是根节点,那么就是null。
            Node<K, V> z_parent = null;
            if (i > 0) {
                z_parent = linkList.get(i - 1);
            }
            int leftHeight = this.calHeight(z.leftChild);
            int rightHeight = this.calHeight(z.rightChild);
            int balance = leftHeight - rightHeight;
            if (balance > 1) { // LL 或 LR
                Node<K, V> y = z.leftChild;
                Node<K, V> x = linkList.get(i + 2);
                boolean isLL = (null != y.leftChild  && this.comparator.compare(y.leftChild.key, x.key) == 0);
                boolean isLR = (null != y.rightChild && this.comparator.compare(y.rightChild.key, x.key) == 0);
                if (isLL) { // LL 右旋
                    this.rotateRight(z_parent, z, y);
                }
                else if (isLR) {    // LR
                    // y和x之间左旋
                    this.rotateLeft(z, y, x);
                    // z和x之间右旋
                    this.rotateRight(z_parent, z, x);
                }
                break; // 停止for循环
            } else if (balance < -1) { // RR 或 RL
                Node<K, V> y = z.rightChild;
                Node<K, V> x = linkList.get(i + 2);
                boolean isRR = (null != y.rightChild && this.comparator.compare(y.rightChild.key, x.key) == 0);
                boolean isRL = (null != y.leftChild && this.comparator.compare(y.leftChild.key, x.key) == 0);
                if (isRR) {
                    this.rotateLeft(z_parent, z, y);
                } else if (isRL) {
                    // y和x之间右旋
                    this.rotateRight(z, y, x);
                    // z和x之间左旋
                    this.rotateLeft(z_parent, z, x);
                }
                break; // 停止for循环
            }
        }
        // 更新祖先节点高度
        for (int i = parentSize - 1; i >= 0; i--) {
            Node<K, V> item = linkList.get(i);
            item.height = calHeight(item);
        }
        return null;
    }

    private List<Node<K,V>> getNodeAndParent(K key, List<Node<K, V>> parents) {
        if (null == this.root) {
            return null;
        }
        Node<K, V> parent = null;
        Node<K, V> current = this.root;
        while(null != current) {
            int compareResult = this.comparator.compare(key, current.key);
            if (compareResult < 0) {
                parent = current;
                if (null != parents) {
                    parents.add(parent);
                }
                current = current.leftChild;
            } else if (compareResult > 0) {
                parent = current;
                if (null != parents) {
                    parents.add(parent);
                }
                current = current.rightChild;
            } else {
                List<Node<K, V>> result = new ArrayList<>();
                result.add(current);
                result.add(parent);
                return result;
            }
        }
        return null;
    }


    private K deleteAsBST(Node<K, V> node, Node<K, V> parent) {
        K endKey = null;
        // 叶子节点
        if (null == node.leftChild && null == node.rightChild) {
            if (node == parent.leftChild) {
                parent.leftChild = null;
            } else {
                parent.rightChild = null;
            }
            return parent.key;
        }
        // 左子节点为空,只有右子节点
        else if (null == node.leftChild && null != node.rightChild) {
            if (node == this.root) {
                this.root = node.rightChild;
            } else if (node == parent.leftChild) {
                parent.leftChild = node.rightChild;
            } else if (node == parent.rightChild) {
                parent.rightChild = node.rightChild;
            }
            endKey = node.rightChild.key;
            node.rightChild = null;
            return endKey;
        }

        // else 包含两种情况:
        // 1.左子节点不为空,右子为空
        // 2.左子节点不为空,右子不为空

        // 要删除的节点的左子树中,找出最大节点。
        Node<K, V> current = node.leftChild;
        Node<K, V> currentParent = node;
        while (null != current.rightChild) {
            currentParent = current;
            current = current.rightChild;
        }
        // 把current从原位置删除
        if (current == currentParent.leftChild) {
            currentParent.leftChild = current.leftChild;
        } else if (current == currentParent.rightChild) {
            currentParent.rightChild = current.leftChild;
        }
        // 让current取代node的位置
        if (node == this.root) {
            this.root = current;
        } else if (node == parent.leftChild) {
            parent.leftChild = current;
        } else {
            parent.rightChild = current;
        }
        current.leftChild = node.leftChild;
        current.rightChild = node.rightChild;

        node.leftChild = null;
        node.rightChild = null;

        if (null == current.leftChild) {
            return current.key;
        } else {
            Node<K, V> p1 = current.leftChild;
            while (null != p1.rightChild) {
                p1 = p1.rightChild;
            }
            return p1.key;
        }

    }

    @Override
    public V remove(Object keyObj) {
        // 空map,不执行删除操作。
        if (null == this.root) {
            return null;
        }
        K key = (K)keyObj;
        // 只有根节点的情况
        if (null == this.root.leftChild && null == this.root.rightChild) {
            if (this.comparator.compare(key ,this.root.key) == 0) {
                V v = this.root.value;
                this.root = null;
                return v;
            } else {
                return null;
            }
        }
        // 不包含key就返回null
        List<Node<K, V>> nodeAndParent = this.getNodeAndParent(key, new ArrayList<>());
        // map中没有对应的key,不执行删除操作。
        if (null == nodeAndParent || nodeAndParent.isEmpty()) {
            return null;
        }
        Node<K, V> node = nodeAndParent.get(0); // 要删除的节点
        V result = node.value;
        Node<K, V> parent = nodeAndParent.get(1); // 要删除的节点的父亲节点

        // 按照二叉搜索树(BST)的方式删除节点。返回结束节点的键。
        K endKey = this.deleteAsBST(node, parent);
        // 包含所有可能改动过高度的节点的列表。
        // 顺序是从根节点向下。
        // 替换了已删除节点位置的节点称为替换节点。
        // pathList的内容有以下三种情况:
        // 1. 叶子节点,pathList包含根节点到父节点。
        // 2. 没有左子节点,只有右子节点,pathList包含根节点到替换节点。
        // 3. 有左子节点,pathList包含根节点到替换节点,再加上替换节点到替换节点左子树最大节点。
        List<Node<K, V>> pathList = new ArrayList<>();
        List<Node<K,V>> endKeyResult = this.getNodeAndParent(endKey, pathList);
        pathList.add(endKeyResult.get(0));


        // 因为可能加入了节点,所以要重新计算 parents 的长度
        int size = pathList.size();
        for (int i = size - 1; i >= 0; i--) {
            Node<K, V> z_parent = i > 0 ? pathList.get(i - 1) : null;
            Node<K, V> z = pathList.get(i);

            // 更新高度
            z.height = this.calHeight(z);
            if (null != z_parent) {
                z_parent.height = this.calHeight(z_parent);
            }

            int leftHeight = calHeight(z.leftChild);
            int rightHeight = calHeight(z.rightChild);
            int balance = leftHeight - rightHeight;
            if (balance > 1) {
                Node<K, V> y = z.leftChild;
                Node<K, V> x = null;
                int y_leftHeight = calHeight(y.leftChild);
                int y_rightHeight = calHeight(y.rightChild);
                if (y_leftHeight >= y_rightHeight) {
                    // LL
                    x = y.leftChild;
                    // z和y之间右旋
                    this.rotateRight(z_parent, z, y);
                } else {
                    // LR
                    x = y.rightChild;
                    // y和x之间左旋
                    this.rotateLeft(z, y, x);
                    // z和x之间右旋
                    this.rotateRight(z_parent, z, x);
                }
            } else if (balance < -1) {
                Node<K, V> y = z.rightChild;
                Node<K, V> x = null;
                int y_leftHeight = calHeight(y.leftChild);
                int y_rightHeight = calHeight(y.rightChild);
                if (y_leftHeight >= y_rightHeight) {
                    // RL
                    x = y.leftChild;
                    // y和x之间右旋
                    this.rotateRight(z, y, x);
                    // z和x之间左旋
                    this.rotateLeft(z_parent, z, x);
                } else {
                    // RR
                    x = y.rightChild;
                    // z和y之间左旋
                    this.rotateLeft(z_parent, z, y);
                }
            }
        }
        return result;
    }
    // end public V remove(Object keyObj)


    @Override
    public void putAll(Map<? extends K, ? extends V> m) {
        if (null == m) {
            return;
        }
        Set<? extends K> keySet = m.keySet();
        for (K key : keySet) {
            this.put(key, m.get(key));
        }
    }

    @Override
    public void clear() {
        this.root = null;
    }

    private List<Node<K, V>> nodeList() {
        if (null == this.root) {
            return new ArrayList<Node<K, V>>();
        }
        List<Node<K, V>> result = new ArrayList<>();
        Stack<Node<K, V>> stack = new Stack<>();
        Node<K, V> current = this.root;

        while(null != current || !stack.isEmpty()) {
            while (null != current) {
                stack.push(current);
                current = current.leftChild;
            }
            current = stack.pop();
            // 放入结果列表中
            result.add(current);
            current = current.rightChild;
        }
        return result;
    }

    @Override
    public Set<K> keySet() {
        List<Node<K, V>> nodeList = nodeList();
        Set<K> set = new TreeSet<>(this.comparator);
        for (Node<K, V> node : nodeList) {
            set.add(node.key);
        }
        return set;
    }

    @Override
    public Collection<V> values() {
        List<Node<K, V>> nodeList = nodeList();
        List<V> result = new ArrayList<>();
        for (Node<K,V> node : nodeList) {
            result.add(node.value);
        }
        return result;
    }

    @Override
    public Set<Entry<K, V>> entrySet() {
        List<Node<K, V>> nodeList = this.nodeList();
        Set<Entry<K, V>> set = new TreeSet<Entry<K, V>>((o1, o2) -> {
            Node<K, V> n1 = (Node<K, V>) o1;
            Node<K, V> n2 = (Node<K, V>) o2;
            return comparator.compare(n1.key, n2.key);
        });
        for (Node<K,V> node : nodeList) {
            set.add(node);
        }
        return set;
    }

    private int calHeightForCheck(Node<K,V> node) {
        if (null == node) {
            return 0;
        }
        int height = 0;
        List<Node<K,V>> currentLevel = new ArrayList<>();
        currentLevel.add(node);
        while (!currentLevel.isEmpty()) {
            height ++;
            List<Node<K,V>> nextLevel = new ArrayList<>();
            for (Node<K,V> tmpNode : currentLevel) {
                if (null != tmpNode.leftChild) {
                    nextLevel.add(tmpNode.leftChild);
                }
                if (null != tmpNode.rightChild) {
                    nextLevel.add(tmpNode.rightChild);
                }
            }
            currentLevel = nextLevel;
        }
        return height;
    }

    private void showTree(Node node, Node parent, int level, String prefix) {
        if (null == node) {
            return;
        }
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < level; i++) {
            sb.append("  ");
        }
        sb.append(prefix);
        sb.append(node.key).append("         ");
        if (parent != null) {
            sb.append(parent.key);
        }
        int balance = calHeightForCheck(node.leftChild) - calHeightForCheck(node.rightChild);
        sb.append("    ").append(balance);
        System.out.println(sb);
        level++;
        showTree(node.leftChild, node, level, "left : ");
        showTree(node.rightChild, node, level, "right: ");
    }

    /**
     * 打印树形结构。
     */
    public void showTree() {
        if (null == root) {
            System.out.println("null");
        }
        showTree(root, null, 0, "root: ");
    }

    private void checkTree(Node node, Node parent, int level) {
        if (null == node) {
            return;
        }
        int balance = calHeightForCheck(node.leftChild) - calHeightForCheck(node.rightChild);
        if (balance < -1 || balance > 1) {
            throw new RuntimeException("balance < -1 || balance > 1");
        }
        level++;
        checkTree(node.leftChild, node, level);
        checkTree(node.rightChild, node, level);
    }

    /**
     * 检查树是不是符合AVL树的要求
     */
    public void checkTree() {
        if (null == root) {
            return;
        }
        checkTree(root, null, 0);
    }

    /**
     * 以node为根节点,计算树的高度
     * @param node 根节点
     * @return 树的高度
     */
    private int calHeight(Node<K,V> node) {
        if (null == node) {
            return 0;
        }
        int leftHeight = (null == node.leftChild) ? 0 : node.leftChild.height;
        int rightHeight = (null == node.rightChild) ? 0 : node.rightChild.height;
        return Math.max(leftHeight, rightHeight) + 1;
    }

    class Node<K,V> implements Entry<K,V> {
        K key = null;
        V value = null;
        int height;
        Node<K, V> leftChild;
        Node<K, V> rightChild;


        @Override
        public K getKey() {
            return key;
        }

        @Override
        public V getValue() {
            return value;
        }

        @Override
        public V setValue(V tmpValue) {
            V oldValue = value;
            value = tmpValue;
            return oldValue;
        }

        public int getHeight() {
            return height;
        }
    }
}


测试代码 TestAVLTreeMap.java
这里面有和二叉查找树Map的对比。
二叉查找树Map的实现文章:https://blog.csdn.net/zhangchao19890805/article/details/128609922?spm=1001.2014.3001.5502


package zhangchao.avl.test;

import zhangchao.avl.AVLTreeMap;

import zhangchao.bst.BstTreeMap;

import java.util.*;

public class TestAVLTreeMap {
    public static void main(String[] args) {
        t7();
    }

    public static void t7() {
        int a[] = {20, 10, 21, 22, 5, 15, 1};
        Comparator<Integer> comparator = (o1, o2) ->{
            if (null == o1 && null == o2) {
                return 0;
            }
            if (null == o1 && null != o2) {
                return -1;
            }
            if (null != o1 && null == o2) {
                return 1;
            }
            return o1 - o2;
        };
        AVLTreeMap<Integer, String> avlTreeMap = new AVLTreeMap<>(comparator );
        for (int key : a) {
            avlTreeMap.put(key, "__" + key);
        }
        avlTreeMap.showTree();
        avlTreeMap.remove(20);
        System.out.println("\n");
        avlTreeMap.showTree();
        avlTreeMap.checkTree();
    }

    public static void t6() {
        Comparator<Integer> comparator = (o1, o2) ->{
            if (null == o1 && null == o2) {
                return 0;
            }
            if (null == o1 && null != o2) {
                return -1;
            }
            if (null != o1 && null == o2) {
                return 1;
            }
            return o1 - o2;
        };
        AVLTreeMap<Integer, String> avlTreeMap = new AVLTreeMap<>(comparator );
        BstTreeMap<Integer, String> bstTreeMap = new BstTreeMap<>(comparator);

        long t1;
        long t2;

        // 比对插入
        System.out.println("insert");
        Random r = new Random();
        final int MAX = 100000;
        List<Integer> list = new ArrayList<>();
        for (int i = 0; i < MAX; i++) {
            int key = r.nextInt(MAX);
            list.add(i);
        }
        t1 = System.currentTimeMillis();
        for (int key : list) {
            avlTreeMap.put(key, "__" + key);
        }
        t2 = System.currentTimeMillis();
        System.out.println("AVL:" + (t2 - t1));
        t1 = System.currentTimeMillis();
        for (int key : list) {
            bstTreeMap.put(key, "__" + key);
        }
        t2 = System.currentTimeMillis();
        System.out.println("BST:" + (t2 - t1));

        // 比对查询
        System.out.println("\nsearch");
        t1 = System.currentTimeMillis();
        for (int i = 0; i < MAX; i++) {
            avlTreeMap.get(i);
        }
        t2 = System.currentTimeMillis();
        System.out.println("AVL:" + (t2 - t1));

        t1 = System.currentTimeMillis();
        for (int i = 0; i < MAX; i++) {
            bstTreeMap.get(i);
        }
        t2 = System.currentTimeMillis();
        System.out.println("BST:" + (t2 - t1));
//        avlTreeMap.showTree();

//      比对删除
        System.out.println("\nremove");
        t1 = System.currentTimeMillis();
        Collections.shuffle(list);
        for (int key : list) {
            avlTreeMap.remove(key);
        }
        t2 = System.currentTimeMillis();
        System.out.println("AVL:" + (t2 - t1));

        t1 = System.currentTimeMillis();
        for (int key : list) {
            bstTreeMap.remove(key);
        }
        t2 = System.currentTimeMillis();
        System.out.println("BST:" + (t2 - t1));

        avlTreeMap.checkTree();
    }



    public static void t3() {
        Map<Integer, String> map = new AVLTreeMap<>( (o1, o2) ->{
            if (null == o1 && null == o2) {
                return 0;
            }
            if (null == o1 && null != o2) {
                return -1;
            }
            if (null != o1 && null == o2) {
                return 1;
            }
            return o1 - o2;
        });
        int[] arr = new int[]{20,10,21,5,15,22,13,16};
        for (int i : arr) {
            map.put(i, "__" + String.valueOf(i));
        }
        AVLTreeMap avlTreeMap = (AVLTreeMap) map;
        avlTreeMap.showTree();
        avlTreeMap.remove(10);
        System.out.println("\n");
        avlTreeMap.showTree();
        avlTreeMap.checkTree();
    }

    public static void t2() {
        Map<Integer, String> map = new AVLTreeMap<>( (o1, o2) ->{
            if (null == o1 && null == o2) {
                return 0;
            }
            if (null == o1 && null != o2) {
                return -1;
            }
            if (null != o1 && null == o2) {
                return 1;
            }
            return o1 - o2;
        });
        int[] arr = new int[]{8,3,6,1,2,98,2,6,150,170,160,7,52,28,75,14,
                40,86,10,21,46,25};
        for (int i : arr) {
            map.put(i, "__" + String.valueOf(i));
        }
        AVLTreeMap avlTreeMap = (AVLTreeMap) map;
        avlTreeMap.showTree();
        avlTreeMap.remove(7);
        System.out.println("\n\n\n");
        avlTreeMap.showTree();
        avlTreeMap.checkTree();
    }

    public static void t1() {
        Map<Integer, String> map = new AVLTreeMap<>( (o1, o2) ->{
            if (null == o1 && null == o2) {
                return 0;
            }
            if (null == o1 && null != o2) {
                return -1;
            }
            if (null != o1 && null == o2) {
                return 1;
            }
            return o1 - o2;
        });
        int[] arr = new int[]{8,3,6,1,2,98,2,6,150,170,160,7,52,28,75,14,
                40,86,10,21,46,25};

        for (int i : arr) {
            map.put(i, "__" + String.valueOf(i));
        }

        AVLTreeMap avlTreeMap = (AVLTreeMap) map;
        avlTreeMap.showTree();

        System.out.println(map.get(3));
        System.out.println(map.get(6));
        System.out.println(map.get(98));
        System.out.println(map.get(null));

        Set<Integer> set = avlTreeMap.keySet();
        for (Integer i : set) {
            System.out.println(i);
        }
        System.out.println();

        HashSet<Integer> hashSet = new HashSet<>();
        for (int i : arr) {
            hashSet.add(i);
        }
        for (int i : hashSet) {
            if (!set.contains(i)) {
                System.out.println(false);
            }
        }
        System.out.println(set.size() + "   " + hashSet.size());

        System.out.println("containsKey 3: " + avlTreeMap.containsKey(3));
        System.out.println("containsKey 4: " + avlTreeMap.containsKey(4));
        System.out.println("containsValue __3: " + avlTreeMap.containsValue("__3"));
        System.out.println("containsValue __4: " + avlTreeMap.containsValue("__4"));
        System.out.println();

        Set<Map.Entry<Integer, String>> entrySet = avlTreeMap.entrySet();
        for (Map.Entry<Integer, String> item : entrySet) {
            System.out.println(item.getKey() + ": " + item.getValue());
        }
        avlTreeMap.checkTree();
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/830138.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【雕爷学编程】MicroPython动手做(33)——物联网之天气预报3

天气&#xff08;自然现象&#xff09; 是指某一个地区距离地表较近的大气层在短时间内的具体状态。而天气现象则是指发生在大气中的各种自然现象&#xff0c;即某瞬时内大气中各种气象要素&#xff08;如气温、气压、湿度、风、云、雾、雨、闪、雪、霜、雷、雹、霾等&#xff…

将自己的网站免费发布到互联网上【无需公网IP】

将自己的网站免费发布到互联网上【无需公网IP】 文章目录 将自己的网站免费发布到互联网上【无需公网IP】将本地搭建的网站发布到互联网步骤 ↓1. 注册并安装cpolar客户端1.1 windows系统1.2 linux系统&#xff08;支持一键自动安装脚本&#xff09;2. 登录cpolar web UI管理界…

Gradio-YOLOv5-YOLOv7 搭建Web GUI

目录 0 相关资料&#xff1a;1 Gradio介绍2 环境搭建3 GradioYOLOv54 GradioYOLOv75 源码解释 0 相关资料&#xff1a; Gradio-YOLOv5-Det&#xff1a;https://gitee.com/CV_Lab/gradio_yolov5_det 【手把手带你实战YOLOv5-入门篇】YOLOv5 Gradio搭建Web GUI: https://www.bi…

一次某某云上的redis读超时排查经历

性能排查&#xff0c;服务监控方面的知识往往涉及量广且比较零散&#xff0c;如何较为系统化的分析和解决问题&#xff0c;建立其对性能排查&#xff0c;性能优化的思路&#xff0c;我将在这个系列里给出我的答案。 问题背景 最近一两天线上老是偶现的redis读超时报警&#xf…

ChatGPT在工作中的七种用途

1. 用 ChatGPT 替代谷歌搜索引擎 工作时&#xff0c;你一天会访问几次搜索引擎&#xff1f;有了 ChatGPT&#xff0c;使用搜索引擎的频率可能大大下降。 据报道&#xff0c;谷歌这样的搜索引擎巨头&#xff0c;实际上很担心用户最终会把自己的搜索工具换成 ChatGPT。该公司针对…

KiCad各层简述

KiCad各层简述 KiCAD在Pcbnew中总计提供了32个铜层供导线走线&#xff08;可覆铜&#xff09;&#xff0c;12个固定技术层&#xff08;按照正反面分为6对&#xff09;&#xff0c;2个独立技术层&#xff0c;4个辅助层。在KiCad里Pcbnew的层描述中&#xff0c;F.代表电路板上层&…

机器学习笔记之优化算法(八)简单认识Wolfe Condition的收敛性证明

机器学习笔记之优化算法——简单认识Wolfe Condition收敛性证明 引言回顾&#xff1a; Wolfe \text{Wolfe} Wolfe准则准备工作推导条件介绍推导结论介绍 关于 Wolfe \text{Wolfe} Wolfe准则收敛性证明的推导过程 引言 上一节介绍了非精确搜索方法—— Wolfe \text{Wolfe} Wolf…

Letter of Acceptance 过期后,如何入境办学签?

很少会有同学遇到LoA过期时间之后入境办学签的问题&#xff0c;所以网上也很少有相关攻略。鉴于此&#xff0c;在联系了IRCC、学院办公室、研究生院和学校移民办公室之后&#xff0c;得到了最终答复。省流&#xff1a;在学校开个入学证明&#xff08;Proof of Enrolment&#x…

【雕爷学编程】MicroPython动手做(28)——物联网之Yeelight

知识点&#xff1a;什么是掌控板&#xff1f; 掌控板是一块普及STEAM创客教育、人工智能教育、机器人编程教育的开源智能硬件。它集成ESP-32高性能双核芯片&#xff0c;支持WiFi和蓝牙双模通信&#xff0c;可作为物联网节点&#xff0c;实现物联网应用。同时掌控板上集成了OLED…

mybatis log插件

目前idea当中已经实施收费了 最近找了一个不收费的插件安装上重启一下就行了 点我下载提取码&#xff1a;sjc8

blender基础认识(选项开关、工具栏、视图等)

文章目录 引言一、大纲选项开关和保存启动文件1. 大纲选项1. 禁用选中2. 视图影藏3. 视图禁用4. 渲染禁用 2. 保存启动文件 二、工具栏和侧边栏1. 左侧工具栏2. 右侧工具栏 三、视图1. 视角2. 缩放3. 拖拽4. 摄像机视角5. 切换正交视图6. 局部视图7. 显示隐藏 四、添加删除物体…

在centos7.9安装tomcat8,并配置服务启动脚本,部署jpress应用

目录 一、简述静态网页和动态网页的区别 二、简述 Webl.0 和 Web2.0 的区别 三、 安装Tomcat8&#xff0c;配置服务启动脚本&#xff0c;部署jpress应用 3.1、Tomcat简介 3.2、安装Tomcat 3.2.1、配置环境 3.2.2、安装JDK 3.2.3、安装tomcat8 3.2.4、访问主页&#xff1…

go编译文件

1.编译go文件 go build [go文件]2.执行文件编译文件 ./demo [demo为go文件名称]

自然语言处理学习笔记(三)————HanLP安装与使用

目录 1.HanLP安装 2.HanLP使用 &#xff08;1&#xff09;预下载 &#xff08;2&#xff09;测试 &#xff08;3&#xff09;命令行 &#xff08;4&#xff09;测试样例 3.pyhanlp可视化 4. HanLP词性表 1.HanLP安装 HanLP的 Python接口由 pyhanlp包提供&#xff0c;其安装…

【深度学习】在 MNIST实现自动编码器实践教程

一、说明 自动编码器是一种无监督学习的神经网络模型&#xff0c;主要用于降维或特征提取。常见的自动编码器包括基本的单层自动编码器、深度自动编码器、卷积自动编码器和变分自动编码器等。 其中&#xff0c;基本的单层自动编码器由一个编码器和一个解码器组成&#xff0c;编…

OLED透明屏安装指南:准备工作、步骤和注意事项

随着科技的不断发展&#xff0c;OLED透明屏作为一种新型的显示技术&#xff0c;逐渐得到了广泛的应用。 OLED透明屏具有高透明度、高亮度和广视角等优势&#xff0c;可以实现透明显示效果&#xff0c;为商业展示、户外广告等领域提供了更广阔的空间。 然而&#xff0c;正确的…

Qt实现可伸缩的侧边工具栏(鼠标悬浮控制伸缩栏)

Qt实现可伸缩的侧边工具栏 一直在网上找&#xff0c;发现大多的实现方案都是用一个按钮&#xff0c;按下控制侧边栏的伸缩&#xff0c;但是我想要实现鼠标悬浮在侧边栏的时候就伸出&#xff0c;移开就收缩的功能&#xff0c;也没找到好的参考&#xff0c;所以决定自己实现一个…

Apache Kafka Learning

一、Kafka Kafka是由Apache软件基金会开发的一个开源流处理平台&#xff0c;由Scala和Java编写。Kafka是一种高吞吐量的分布式发布订阅消息系统&#xff0c;它可以收集并处理用户在网站中的所有动作流数据以及物联网设备的采样信息。 Apache Kafka是Apache软件基金会的开源的流…

Quartz使用文档,使用Quartz实现动态任务,Spring集成Quartz,Quartz集群部署,Quartz源码分析

文章目录 一、Quartz 基本介绍二、Quartz Java 编程1、文档2、引入依赖3、入门案例4、默认配置文件 三、Quartz 重要组件1、Quartz架构体系2、JobDetail3、Trigger&#xff08;1&#xff09;代码实例&#xff08;2&#xff09;SimpleTrigger&#xff08;3&#xff09;CalendarI…

低代码开发工具到底是给“谁”用的?

不同的工具&#xff0c;受众也不一样。 你不要认为“低代码开发工具”只有一种&#xff0c;实际上它分 3 种。 第一种&#xff1a;企业级低代码开发平台 这种通常是给专业开发人员使用的&#xff0c;但也没有限制得很死&#xff0c;只要你懂编程逻辑&#xff0c;能写sql语句&…