2015腾讯校园招聘研发笔试题:在java中,哪些数据结构可以常量的时间复杂度O(1)添加元素?

原题贴出自:2015腾讯校园招聘技术类研发笔试题(西安、成都、武汉站)

喝水不忘挖井人,在次感谢IT面论坛的辛勤劳作!

原题描述:

在java中,哪些数据结构可以常量的时间复杂度O(1)添加元素?

HashMap、ArrayList、TreeMap和LinkedList

我给的选择是:HashMap和Linkedlist。

不一定正确,欢迎批评指正!!!共同学习,共同进步……

—————————————————————————分割线————————————————————

首当其冲,讲解HashMap

HashMap原理简介:

HashMap实现的是java.util.Map接口,提供了Map所有可用的方法实现,允许key或者value的值为null,不同于HashTable,HashMap是非线程安全实现,且实现是不保证元素有序排列,也不保证原有元素的顺序随着时间推移保持不变。

这种实现提供了基本操作(get和put),常量时间操作的性能,假定哈希函数将正确之间的桶中的元素。遍历集合的视图,需要的时间与HashMap实例的“容量”(桶的数量),加上它的大小(键 - 值映射关系数)。因此,这是非常重要的是不要设定初始容量太高(或将加载因子设置得太低),如果迭代性能很重要。

这个问题实现是牵扯到的是HashMap的put方法,下面介绍下put方法的实现,先来段源码:

transient Entry[] table = (Entry[]) EMPTY_TABLE;

内部容器实现是基于Entry(俗称的桶)

static class Entry implements Map.Entry {
        final K key;
        V value;
        Entry next;
        int hash;
********省略实现代码***********
}

Entry内部属性有next,可以看出桶的实现又是采用的链表形式.

public V put(K key, V value) {
        if (table == EMPTY_TABLE) {
            inflateTable(threshold);
        }
        if (key == null)
            return putForNullKey(value);
        int hash = hash(key);
        int i = indexFor(hash, table.length);
        for (Entry e = table[i]; e != null; e = e.next) {
            Object k;
            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
                V oldValue = e.value;
                e.value = value;
                e.recordAccess(this);
                return oldValue;
            }
        }

        modCount++;
        addEntry(hash, key, value, i);
        return null;
}

for循环那块代码实现的就是:根据key值计算出hash后,看看是否存在于HashMap的table(元素真实的容器)。由于Hash算法实现是采用的链地址法(看如下分析就知道为什么的),所以得循环遍历桶元素的链表。这个部分不是关心的重点,重点是下面的

addEntry(hash, key, value, i)方法。

void addEntry(int hash, K key, V value, int bucketIndex) {
        if ((size >= threshold) && (null != table[bucketIndex])) {
            resize(2 * table.length);
            hash = (null != key) ? hash(key) : 0;
            bucketIndex = indexFor(hash, table.length);
        }

        createEntry(hash, key, value, bucketIndex);
    }

第一个if语句是用来判断是否需要扩容,问题关注的是下面的createEntry(hash, key, value, bucketIndex)方法,继续接着分析。

void createEntry(int hash, K key, V value, int bucketIndex) {
        Entry e = table[bucketIndex];
        table[bucketIndex] = new Entry<>(hash, key, value, e);
        size++;
    }

注意啦,问题的核心在这里。为什么之前说是基于链地址法的hash算法。先定位到桶元素,然后更具要插入元素的hash,key和value来新建Entry元素,直接采用的是头插法,把元素添加到桶里。既然是头插入方式,那么可以推测出来插入一个元素的时间复杂度为O(1),也就能够保证常量时间添加,故HashMap也是O(1)时间完成元素添加的。

LinkedList见名知意,实现是基于链表的,因为链表添加动作时间复杂度是O(1),故LinkedList也是正确选项。

ArrayList:内部实现是基于数组,数组元素不能保证O(1)时间内任意添加一个元素。且当ArrayList需要扩容的时候,性能消耗是更加大的,先得重新开辟空间,然后再进行元素的添加。

TreeMap:内部实现是基于红黑树,呵呵~这个也就不是我们想要的答案咯。至于红黑树是什么东西,google下就差不多了,这里不深究。插入和删除操作都设计到树结构的调整。


共同学习,共同进步!



你可能感兴趣的:(java开发,数据结构算法)