哈希:它是如何在内部工作的?

这可能听起来像一个非常模糊的问题,但事实并非如此。 我已经通过维基上的哈希函数描述,但它不是很有帮助的理解。

我正在寻找像Hashing这样相当复杂的主题的简单答案。 这是我的问题:

  1. 哈希是什么意思? 它如何在内部工作?
  2. 它遵循什么algorithm?
  3. HashMapHashTableHashList什么HashList
  4. “恒定时间复杂度”是什么意思,为什么散列的不同实现给定时间操作?
  5. 最后,为什么在大多数面试问题中, HashLinkedList被问到,从testing受访者的知识来看,有没有什么特定的逻辑?

我知道我的问题清单很大,但是如果我能够清楚地回答这些问题,我真的很感激,因为我真的很想理解这个话题。

  1. 这里是关于哈希的一个很好的解释。 例如,要存储string“Rachel”,可以将散列函数应用于该string以获取内存位置。 myHashFunction(key: "Rachel" value: "Rachel") --> 10 。 该函数可能返回10为input“Rachel”,所以假设你有一个大小为100的数组,你在索引10存储“Rachel”。如果你想检索该元素,你只需调用GetmyHashFunction("Rachel") ,它将返回10 。注意在这个例子中,键是“Rachel”,值是“Rachel”,但是你可以使用另一个值作为例如出生date或者对象的那个键。 你的哈希函数可能会返回两个不同的input相同的内存位置,在这种情况下,如果你正在实现自己的哈希表,你将有一个碰撞,你必须照顾这可能使用链接列表或其他技术。

  2. 这里使用了一些常用的散列函数。 一个好的散列函数满足以下条件:每个密钥同样可能散列到n个内存槽中的任何一个,而不pipe其他密钥散列到哪里。 其中一种方法被称为划分方法。 我们把一个关键字k映射到n个时隙中的一个上,并把剩下的k除以n。 h(k) = k mod n 。 例如,如果你的数组大小是n = 100 ,你的密钥是一个整数k = 15那么h(k) = 10

  3. 哈希表是同步的,哈希表不是。 HashMap允许空值作为键,但Hashtable不允许。

  4. 散列表的目的是在添加和获取元素时具有O(c)恒定的时间复杂性。 在大小为N的链表中,如果你想得到最后一个元素,你必须遍历所有的列表,直到你得到它,所以复杂度是O(N)。 使用散列表,如果你想检索一个元素,你只需要传递键,散列函数将返回所需的元素。 如果散列函数被很好的实现,它将会在一个常量时间O(c)这意味着你不必遍历存储在散列表中的所有元素。 你会得到“即时”的元素。

  5. 程序员/开发人员计算机科学家需要了解数据结构和复杂性=)

  1. 散列意味着生成一个(希望)代表一个值的唯一编号。
  2. 不同types的值( IntegerString等)使用不同的algorithm来计算哈希码。
  3. HashMap和HashTable是地图 ; 他们是一个独特的密钥集合,每个密钥都与一个值相关联。
    Java没有HashList类。 哈希是一组唯一的值。
  4. 从哈希表中获取项目是恒定的表格大小。
    散列值的计算并不一定是恒定的。
    例如,计算一个string的散列涉及迭代string,而不是关于string大小的恒定时间。
  5. 这些是人们应该知道的事情。
  1. 散列正在将给定的实体(以java的forms – 一个对象)转换为一些数字(或序列)。 哈希函数是不可逆的 – 即你不能从哈希中获得原始对象。 它在内部是通过JVM获取内存地址来实现的(对于java.lang.Object

  2. JVM地址的东西是不重要的细节。 每个类都可以用自己的algorithm重写hashCode()方法。 Modren Java IDE允许生成好的hashCode方法。

  3. 哈希表和哈希表是相同的东西。 他们的键值对,其中密钥被哈希。 哈希列表和哈希集不存储值 – 只有键。

  4. 常数意味着无论散列表(或任何其他集合)中有多less条目,通过它的键find给定对象所需的操作数量是不变的。 即 – 1,或接近1

  5. 这是计算机科学的基本材料,大家应该都很熟悉。 我认为谷歌已经指定哈希表是计算机科学中最重要的数据结构。

我会尽量简单地解释哈希和它的目的。

首先,考虑一个简单的列表。 在这样的列表中,每个操作(插入,查找,删除)都会有O(n)的复杂性,这意味着你必须parsing整个列表(或平均一半)来执行这样的操作。

散列是一种非常简单而有效的加速方法:考虑将整个列表分成一系列小列表。 在一个这样的小列表中的项目将有一些共同的东西,这个东西可以从关键推论。 例如,通过列出名称,我们可以使用第一个字母作为将select在哪个小列表中查找的质量。 这样,通过按键的第一个字母对数据进行分区,我们获得了一个简单的哈希,这将能够将整个列表拆分成大约30个较小的列表,这样每个操作将花费O(n)/ 30次。

但是,我们可以注意到结果并不完美。 首先,只有30个,我们不能改变。 其次,一些字母比其他字母更频繁地使用,所以YZ的集合会比A集合小得多。 为了获得更好的结果,最好find一种方法来分割大小基本相同的项目。 我们怎么能解决这个问题? 这是你使用散列函数的地方。 这是一个function,可以创build任意数量的分区,每个分区的数量大致相同。 在我们的例子中,我们可以使用类似的东西

 int hash(const char* str){ int rez = 0; for (int i = 0; i < strlen(str); i++) rez = rez * 37 + str[i]; return rez % NUMBER_OF_PARTITIONS; }; 

这将确保相当均匀的分布和可configuration数量的组(也称为桶)。

考虑为给定值search数组的问题。 如果数组未被sorting,则search可能需要检查数组中的每个元素。 如果对数组进行sorting,则可以使用二进制search,因此可以将最坏情况的运行时复杂度降低到O(log n)。 如果事先知道该值在数组中的索引,我们可以更快地search。 假设我们有这个神奇函数,它会告诉我们给定值的索引。 有了这个神奇的function,我们的search减less到只有一个探针,给我们一个恒定的运行时间O(1)。 这样的函数被称为散列函数。 散列函数是一个函数,当给定一个键时,在表中生成一个地址。

哈希是什么意思,它在内部是如何工作的?

散列是一个string短的固定长度值或代表原始string的键的转换。 这不是索引。 哈希的核心是哈希表。 它包含一些项目。 哈希表包含数据项的键的索引,并使用此索引将数据放入数组中。

它遵循什么algorithm?

简单地说,大多数散列algorithm都是在逻辑“index = f(key,arrayLength)”上工作的

最后,为什么在大多数面试问题中,Hash和LinkedList被问到,从testing受访者的知识来看,有没有什么特定的逻辑?

它关于你在逻辑推理上有多好。 每个程序员都知道这是最重要的数据结构。