哈希表的空间复杂度是多少?

时间:2011-06-30 04:33:38

标签: hashmap hashtable hash

具有32位密钥的哈希表的大小和单独存储的值的32位指针是什么?

它是2 ^ 32个插槽*(4个字节(键)+ 4个字节(指向值的指针)) = 4 * 10 ^ 9 *(4 + 4)= 32GB?

我试图理解哈希表的空间复杂性。

4 个答案:

答案 0 :(得分:11)

我认为你问的是错误的问题。数据结构的空间复杂性表明它占据的空间相对于它所拥有的元素数量。例如,空间复杂度O(1)意味着无论您放入多少元素,数据结构都会消耗恒定的空间。 O(n)意味着空间消耗随着其中元素的数量线性增长。

散列表的空间复杂度通常为O(n)

所以回答你的问题:这取决于它当前存储的元素数量,以及现实世界中实际实现的元素数量。

哈希表的内存消耗的下限是:(要存储的值的数量)*(SizeOf a Value)。因此,如果您想在哈希表中存储100万个值,每个值占用4个字节,那么它将消耗至少400万个字节(大约4MB)。通常,现实世界的实现会为基础设施使用更多的内存,但又一次:这在很大程度上取决于实际的实现,并且没有办法确定但是要测量它。

答案 1 :(得分:8)

散列表与散列函数值和插槽不匹配。以比散列函数范围小得多的参考向量的大小为模计算散列函数。因为这个值是固定的,所以在空间复杂度计算中不予考虑。

因此,每个合理哈希表的空间复杂度为 O(n)。

总的来说,这很好。虽然密钥空间可能很大,但存储的值的数量通常很容易预测。当然,功能上可接受的数据结构开销的内存量通常是显而易见的。

这就是哈希表无处不在的原因。它们通常为给定任务提供最佳数据结构,混合严格限制的内存开销,优于log 2 n 时间复杂度。我喜欢二叉树,但它们通常不会打哈希表。

答案 2 :(得分:1)

让我们假设我们有一个天真的哈希表,其中桶的数量等于元素大小的两倍。即O(2n)元素的数量是O(n)。

当元素数量超过可用存储桶数量的一半时,您需要创建一个新的存储桶数组,将其大小加倍,并将所有元素重新分配到新数据桶阵列中的新位置。

386  public V put(K key, V value) {
387      if (key == null)
388          return putForNullKey(value);
389      int hash = hash(key.hashCode());
390      int i = indexFor(hash, table.length);
391      for (Entry<K,V> e = table[i]; e != null; e = e.next) {
392          Object k;
393          if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
394              V oldValue = e.value;
395              e.value = value;
396              e.recordAccess(this);
397              return oldValue;
398          }
399      }
401      modCount++;
402      addEntry(hash, key, value, i);
403      return null;
404  }

768  void addEntry(int hash, K key, V value, int bucketIndex) {
769      Entry<K,V> e = table[bucketIndex];
770      table[bucketIndex] = new Entry<K,V>(hash, key, value, e);
771      if (size++ >= threshold)
772          resize(2 * table.length);
773  }

471  void resize(int newCapacity) {
472      Entry[] oldTable = table;
473      int oldCapacity = oldTable.length;
474      if (oldCapacity == MAXIMUM_CAPACITY) {
475          threshold = Integer.MAX_VALUE;
476          return;
477      }
479      Entry[] newTable = new Entry[newCapacity];
480      transfer(newTable);
481      table = newTable;
482      threshold = (int)(newCapacity * loadFactor);
483  }

488  void transfer(Entry[] newTable) {
489      Entry[] src = table;
490      int newCapacity = newTable.length;
491      for (int j = 0; j < src.length; j++) {
492          Entry<K,V> e = src[j];
493          if (e != null) {
494              src[j] = null;
495              do {
496                  Entry<K,V> next = e.next;
497                  int i = indexFor(e.hash, newCapacity);
498                  e.next = newTable[i];
499                  newTable[i] = e;
500                  e = next;
501              } while (e != null);
502          }
503      }
504  }

参考文献:

HashMap.put
http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/util/HashMap.java#HashMap.put%28java.lang.Object%2Cjava.lang.Object%29

答案 3 :(得分:0)

这个问题还没有完美的答案。我不确定占用的空间。 根据我对这个问题的理解。大小是动态的,并随输入的大小而变化。

那就是我们从一个随机数,哈希表大小开始,与哈希函数值相比,这个数字非常少。然后我们插入输入。现在,随着冲突的开始,我们动态地将哈希表大小加倍。 我认为这就是O(n)复杂性的原因。如果我错了,请纠正我。