int hash (const string &key, int tableSize) {
int hashVal = 0;
for (int i = 0; i < key.length(); i++)
hashVal = 37*hashVal + key[i];
hashVal %= tableSize;
if (hashVal < 0) /* in case overflows occurs */
hashVal += tableSize;
return hashVal;
};
为什么我们控制hashVal是否小于零?这怎么可能?
答案 0 :(得分:2)
您可以在变量hashVal中获得溢出。这(有时)导致负值。例如,尝试在C ++程序中打印3 * 1000 * 1000 * 1000的值:
std::cout << 3 * 1000 * 1000 * 1000;
在我的计算机上,使用我的编译器,打印-1294967296。
结果3000000000是二进制的10110010110100000101111000000000,但由于此特定平台上的整数为32位,我们使用二进制补码方法表示负数,此位模式表示负数。 / p>
标准将整数溢出定义为未定义的行为,因此实际上可能发生任何事情,但这是典型的效果。
答案 1 :(得分:2)
如果字符串足够长,则代码为:
for (int i = 0; i < key.length(); i++)
hashVal = 37*hashVal + key[i];
可能会导致hashVal
的值超过int
的最大值(通常类似于2 31 - 1)并变为负值。这称为integer overflow。
C ++标准does not specify负操作数的%
运算符的值是正数还是负数;因此,根据您的编译器和CPU架构(以及可能的编译时开关),-47 % 37
之类的表达式可以评估为-10
或27
。因此,你引用的代码通过在结果中添加模数来防止前者的可能性。
顺便说一句,避免此问题的一种更简单的方法是将hashVal
定义为无符号。
答案 2 :(得分:0)
如果密钥足够长,hashVal
值可能会变为负数。您可以尝试使用不同长度的字符串(例如“1”,“11”,“111”,“1111”等)来查看hashVal
将变为负数的位置(大约5-7个字符就足够了)。
然后你尝试得到负数的模数,这也是负数。但你不能指向负数组索引(看起来,这个函数计算要存储的字符串的位置),所以你把它作为数组索引是正面的和适当的。
答案 3 :(得分:0)
hashVal
在for
循环中变得越来越大,它很容易变得比最大的signed int
值大,后者取决于平台。
如果hashVal
循环后for
为负数,则%=
运算符后仍可能为负数,这也是平台相关的(在某些情况下,它总是返回非负值,而它可能会返回否则,你需要检查hashVal
之后是否为否定。
答案 4 :(得分:0)
尝试按以下方式调用哈希函数
hash("HelloHello",100);
然后逐步执行程序或在哈希函数中打印一条消息,看看哈希值是否低于0。
例如,在for
循环中你可以放
if(hashVal < 0)
{
cout<<"OVERFLOW HAS HAPPENED\n";
break;
}
你会看到hashVal低于0。