【188】Java8利用AVL树实现Map

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();
    }
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

移动端商品分类左右联动

代码&#xff1a; <template><view class"u-wrap"><view class"u-menu-wrap"><scroll-view scroll-y scroll-with-animation class"u-tab-view menu-scroll-view" :scroll-top"scrollTop":scroll-into-view&quo…

uniapp uni-combox 下拉提示无匹配项(完美解决--附加源码解决方案及思路)

问题描述 匆匆忙忙又到了周一啦&#xff0c;一大早就来了一个头疼的问题&#xff0c;把我难得团团转&#xff0c;呜呜呜~ 下面我用代码的方式展示出来&#xff0c;看下你的代码是否与我的不同。 解决方案 <uni-forms-item label"名称" name"drugName&quo…

销存管理系统ssm进销存仓库销售java jsp源代码mysql

本项目为前几天收费帮学妹做的一个项目&#xff0c;Java EE JSP项目&#xff0c;在工作环境中基本使用不到&#xff0c;但是很多学校把这个当作编程入门的项目来做&#xff0c;故分享出本项目供初学者参考。 一、项目描述 销存管理系统ssm 系统有1权限&#xff1a;管理员 二…

ClickHouse(六):Clickhouse数据类型-1

进入正文前&#xff0c;感谢宝子们订阅专题、点赞、评论、收藏&#xff01;关注IT贫道&#xff0c;获取高质量博客内容&#xff01; &#x1f3e1;个人主页&#xff1a;含各种IT体系技术&#xff0c;IT贫道_Apache Doris,Kerberos安全认证,大数据OLAP体系技术栈-CSDN博客 &…

flannel的三种常见模式分析

概述 大家接触flannel这种网络模式大多数可能都是从k8s中知道的&#xff0c;初始使用很少去深入了解它&#xff0c;毕竟使用它其实是很简单的。但是有时候会出现奇奇怪怪的网络问题&#xff0c;这个时候就需要我们更深入了解一下flannel这种网络模式。 Flannel是CoreOS开源的&…

学习C#编写上位机的基础知识和入门步骤:

00001. 掌握C#编程语言基础和.NET框架的使用。 00002. 学习WinForm窗体应用程序开发技术&#xff0c;包括控件的使用和事件驱动编程。 00003. 熟悉基本的数据结构和算法知识&#xff0c;如链表、栈、队列等。 00004. 理解串口通信协议和通信方法&#xff0c;用于与底层硬件设…

后端整理(集合框架、IO流、多线程)

1. 集合框架 Java集合类主要有两个根接口Collection和Map派生出来 Collection派生两个子接口 List List代表了有序可重复集合&#xff0c;可以直接根据元素的索引进行访问Set Set代表无序不可重复集合&#xff0c;只能根据元素本身进行访问 Map接口派生 Map代表的是存储key…

CS5265 USB-C to HDMI 4k@60Hz单转方案

CS5265AN是一款高性能Type-C/DP1.4至HDMI2.0b转换器芯片&#xff0c;集成了DP1.4兼容接收机和HDMI2.0b兼容发射机&#xff0c;还配备了CC控制器用于CC通信&#xff0c;实现DP Alt模式。DP接口包括4条主通道、辅助通道和HPD信号&#xff0c;接收器支持每通道最大5.4Gbps数据速率…

[自学记录05|百人计划]Early-Z和Z-Prepass

其实这篇我是不想写的&#xff0c;因为网上资料真的非常非常多很多人都写过&#xff0c;但是我后来想了想&#xff0c;做笔记不就是这样吗&#xff0c;所以就写吧~。前置知识&#xff1a;深度测试Z-Buffer[计算机图形学]可见性与遮挡,Z-Buffer(前瞻预习/复习回顾)__Yhisken的博…

小主机折腾记16

7月折腾了 1.2500s&#xff0c;2550k&#xff0c;e3 1225的性能测试 结果如下图 总结如下&#xff1a; a.2500s e3 1225 2390t 差别不大 b.1333频率相对1066频率内存提升12%左右 c.为什么少了2550k&#xff0c;因为装上去风扇尬转&#xff0c;没画面&#xff0c;我猜是因为…

2023年第四届“华数杯”数学建模思路 - 复盘:光照强度计算的优化模型

文章目录 0 赛题思路1 问题要求2 假设约定3 符号约定4 建立模型5 模型求解6 实现代码 0 赛题思路 &#xff08;赛题出来以后第一时间在CSDN分享&#xff09; https://blog.csdn.net/dc_sinor?typeblog 1 问题要求 现在已知一个教室长为15米&#xff0c;宽为12米&#xff0…

百度地图点标记加调用

先看效果 PHP代码 <?phpnamespace kds_addons\edata\controller;use think\addons\Controller; use think\Db;class Maps extends Controller {// 经纬度计算面积function calculate_area($points){$totalArea 0;$numPoints count($points);if ($numPoints > 2) {f…

第六章:SpringMVC上

第六章&#xff1a;SpringMVC上 6.1&#xff1a;SpringMVC简介 什么是MVC MVC是一种软件架构的思想&#xff0c;将软件按照模型、视图、控制器来划分。 M&#xff1a;Model&#xff0c;模型层&#xff0c;指工程中的JavaBean&#xff0c;作用是处理数据。 一类称为实体类Bean&…

JavaEE初阶之网络初识

一、网络发展史 1.1独立模式 独立模式:计算机之间相互独立; 1.2网络互连 随着时代的发展,越来越需要计算机之间互相通信,共享软件和数据,即以多个计算机协同工作来完成业务,就有了网络互连。网络互连:将多台计算机连接在一起,完成数据共享。 数据共享本质是网络数据…

时序预测 | Python实现NARX-DNN空气质量预测

时序预测 | Python实现NARX-DNN空气质量预测 目录 时序预测 | Python实现NARX-DNN空气质量预测效果一览基本介绍研究内容程序设计参考资料效果一览 基本介绍 时序预测 | Python实现NARX-DNN空气质量预测 研究内容 Python实现NARX-DNN空气质量预测,使用深度神经网络对比利时空气…

Hive数据仓库

数据仓库概念与起源发展由来 数仓概念 数据仓库&#xff08;英语&#xff1a;Data Warehouse&#xff0c;简称数仓、DW&#xff09;&#xff0c;是一个用于存储、分析、报告的数据系统。数据仓库的目的是构建面相分析的集成化数据环境&#xff0c;分析结果为企业提供决策支持…

使用docker部署一个jar项目

简介: 通过docker镜像, docker可以在服务器上运行包含项目所需运行环境的docker容器, 在线仓库里有很多各个软件公司官方发布的镜像, 或者第三方的镜像. 如果我们需要使用docker把我们的应用程序打包成镜像, 别的机器上只要安装了docker, 就可以直接运行镜像, 而不需要再安装应…

LabVIEW使用灰度和边缘检测进行视频滤波

LabVIEW使用灰度和边缘检测进行视频滤波 数字图像处理&#xff08;DIP&#xff09;是真实和连续世界的离散表示。除此之外&#xff0c;这种数字图像在通信、医学、遥感、地震学、工业自动化、机器人、航空航天和教育等领域变得非常重要。计算机技术越来越需要视频图像的数字图…

软件测试这个行业究竟能做到多少岁?35岁真的是一个坎?

前言 在国内&#xff0c;软件测试行业是近10多年来随着互联网的飞速发展逐步兴起来的。 随着行业的发展&#xff0c;测试市场的人才缺口也越来越大&#xff0c;能够提供的就业机会也就越来越多&#xff0c;所以很多人都意气风发地投身到测试行业之中&#xff0c;憧憬这自己在这…

数据结构 | 递归

目录 一、何谓递归 1.1 计算一列数之和 1.2 递归三原则 1.3 将整数转换成任意进制的字符串 二、栈帧&#xff1a;实现递归 三、递归可视化 四、谢尔平斯基三角形 五、复杂的递归问题 六、动态规划 一、何谓递归 递归是解决问题的一种办法&#xff0c;它将问题不断地分…