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

来源:互联网 发布:国家卫计委数据 编辑:程序博客网 时间:2024/05/19 13:24

原题贴出自: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<K,V>[] table = (Entry<K,V>[]) EMPTY_TABLE;

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

static class Entry<K,V> implements Map.Entry<K,V> {        final K key;        V value;        Entry<K,V> 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<K,V> 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<K,V> 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下就差不多了,这里不深究。插入和删除操作都设计到树结构的调整。


共同学习,共同进步!



0 0
原创粉丝点击