• Java HashSet和ArrayList的查找Contains()时间复杂度


    今天在刷leetCode时,碰到了一个题是这样的。

    给定一个整数数组,判断是否存在重复元素。

    如果任何值在数组中出现至少两次,函数返回 true。如果数组中每个元素都不相同,则返回 false。

    看到这个题的第一时间,就想到了利用集合ArrayList来存储,并且判断。

    代码:

    class Solution {
        public boolean containsDuplicate(int[] nums) {
                Set<Integer> list=new HashSet<>();
            for (int i=0;i<nums.length;i++){
                if(list.contains(nums[i])){
                    return true;
                }else {
                    list.add(nums[i]);
                }
            }
            return false;
        }
    }

    然而,当数据量足够大的时候,会提示超出时间限制

    后来又用了指针遍历数组,时间复杂度勉强通过,也不尽人意

    最后又用到了HashSet,时间复杂度已经很好了

        public static boolean containsDuplicate(int[] nums) {
            Set<Integer> set=new HashSet<>();
            for (int i=0;i<nums.length;i++){
                if(set.contains(nums[i])){
                    return true;
                }else {
                    set.add(nums[i]);
                }
            }
            return false;
        }

    这个时候。引发了我对这两者的思考,

    这道题不但在考你的基础算法实现,还涉及到算法效率优化问题。也就是必须要关注算法的时间复杂度。

    既然如此,就趁这个机会加深一下ArrayList与HashSet元素查找的时间复杂度区别,实际上就是底层的实现区别。

    ArrayList本质就是通过数组实现的,查找一个元素是否包含要用到遍历,时间复杂度是O(n) 

    而HashSetHashSet的查找是通过HashMap的KeySet来实现的,判断是否包含某个元素的实现,时间复杂度是O(1)

    ArrayList判断是否包含某个元素的源码实现:

     public boolean contains(Object o) {
            return indexOf(o) >= 0;
        }
    
    public int indexOf(Object o) {
            if (o == null) {
                for (int i = 0; i < size; i++)
                    if (elementData[i]==null)
                        return i;
            } else {
                for (int i = 0; i < size; i++) //从头遍历
                    if (o.equals(elementData[i]))
                        return i;
            }
            return -1;
        }

    HashSet判断是否包含某个元素的源码实现:

    public boolean contains(Object o) {
            return map.containsKey(o);
        }
    
     public boolean containsKey(Object key) {
            return getNode(hash(key), key) != null;
        }
    
       final Node<K,V> getNode(int hash, Object key) {
            Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
            if ((tab = table) != null && (n = tab.length) > 0 &&
                (first = tab[(n - 1) & hash]) != null) { //直接通过hash确定元素位置,不用从头遍历
                if (first.hash == hash && // always check first node
                    ((k = first.key) == key || (key != null && key.equals(k))))
                    return first;
                if ((e = first.next) != null) {
                    if (first instanceof TreeNode)
                        return ((TreeNode<K,V>)first).getTreeNode(hash, key);
                    do {
                        if (e.hash == hash &&
                            ((k = e.key) == key || (key != null && key.equals(k))))//部分情况下可能会继续遍历链表定位
                            return e;
                    } while ((e = e.next) != null);
                }
            }
            return null;
        }
  • 相关阅读:
    Java 学习 第二篇;面向对象 定义类的简单语法:
    Java 学习 第一篇
    Compiler Principles 语法分析
    未来智能机的发展趋势
    C语言IO操作总结
    python学习资料
    JavaScript 数组的创建
    JavaScript中的逗号运算符
    JavaScript本地对象 内置对象 宿主对象
    JavaScript的this用法
  • 原文地址:https://www.cnblogs.com/jiezai/p/11149932.html
Copyright © 2020-2023  润新知