在哈希表中创建字符串哈希值的时间复杂度

时间:2015-07-21 21:10:33

标签: java c++ string hashtable

通常说在哈希表中插入和查找字符串是O(1)。但是如何制作字符串的哈希键呢?为什么它不是O(L),字符串的长度? 对我来说很明显,为什么整数是O(1),而不是字符串。

请注意,我理解为什么一般来说,插入哈希表是O(1),但我对将哈希插入表之前的步骤感到困惑;制作哈希值阶段。

在java中的hashTable与C ++中的unordered_map之间如何生成字符串的哈希键之间是否存在任何差异?

4 个答案:

答案 0 :(得分:9)

在哈希表中插入等等是O(1),因为它在表中的元素数量中是常量。

" O(1)"在这种情况下,不会声称你可以多快地计算哈希值。如果这种努力以某种方式增长,那就是它的方式。但是,我发现一个体面的(即#34;适合这个应用程序")哈希函数的复杂性不太可能比#34; size"中的线性更差。 (即我们的字符串示例中的长度)被散列的对象。

答案 1 :(得分:3)

  

通常说在哈希表中插入和查找字符串是O(1)。但是如何制作字符串的哈希键呢?为什么它不是O(L),字符串的长度?对我来说很明显,为什么整数是O(1),而不是字符串。

通常引用的O(1)表示时间不随容器中元素的数量增长。正如你所说,从字符串生成哈希值的时间本身可能不是字符串中的O(1) - 尽管对于某些实现它是:例如Microsoft的C ++ {{1有}:

std::hash<std::string>

size_t _Val = 2166136261U; size_t _First = 0; size_t _Last = _Keyval.size(); size_t _Stride = 1 + _Last / 10; if (_Stride < _Last) _Last -= _Stride; for(; _First < _Last; _First += _Stride) _Val = 16777619U * _Val ^ (size_t)_Keyval[_First]; return (_Val); 是字符串长度的十分之一,因此远离的固定字符数将合并到哈希值中。这样的散列函数是字符串的长度为O(1)

GCC的C ++标准库采用了不同的方法:至少在v4.7.2中,它通过_Stride支持类调用_Hash_impl非成员函数_Hash_bytes,包含每个字节的Murmur哈希。因此,GCC的static是字符串长度中的O(N)

  • GCC对碰撞最小化的更高优先级在使用hash<std::string>std::unordered_set的素数桶方面也很明显,MS的实现不会这样做 - 至少在VS2013 / VC12之前;简而言之,MS的方法对于不易发生冲突的键,重量更轻/更快,但是更早,更显着地降级。
  

在java中的hashTable和C ++中的unordered_map之间如何生成字符串的哈希键之间有什么区别?

C ++标准没有指定如何对字符串进行散列 - 它留给了各个编译器实现。因此,不同的编译器会产生不同的妥协 - 甚至是同一编译器的不同版本。

文档DavidPérezCabrera的答案链接解释了Java中的std::unordered_map函数:

  

返回此字符串的哈希码。 String对象的哈希码计算为

hashCode
  

使用 s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1] 算术,其中int是字符串的s[i] th 字符,i是字符串的长度,并且n表示取幂。 (空字符串的哈希值为零。)

这显然是字符串长度的O(N)。

答案 2 :(得分:2)

根据Java的实现,Hashtable使用key(String或Integer)的hashCode方法。 Hashtable String.hashCode Integer.hashCode

根据http://en.cppreference.com/w/cpp/utility/hash,C ++使用std::hash<std::string>std::hash<int>,实现位于功能文件中(/ path / to / c ++ ... /include/c++/4.8/functional)

答案 3 :(得分:0)

哈希函数的复杂度永远不会为O(1)。如果字符串的长度为n,则复杂度肯定为O(n)。但是,如果您计算给定数组中的所有哈希,则无需第二次计算,并且始终可以通过比较预先计算的哈希来比较O(1)时间中的两个字符串。