Skip to content

哈希表工作原理与应用学习笔记 #28

Open
@boji123

Description

@boji123

近日遇到了青阳大神,和他聊起了微软研究院的面试。我没去试试还是有点后悔,不过机会总是留给有准备的人,建议大家平时多多学习算法吧,leetcode是个很好的平台。青阳谈到了哈希表,我觉得这十分有趣 / 我孤陋寡闻,因此特地学习了一下并写了这篇笔记。

哈希表即散列表(hash=散列),是一种提供了某种表映射关系的数据结构,哈希表算法则是一种以空间为代价换取时间的算法,在以前存储空间紧张的时候,哈希表算法难以进行,而随着现在内存空间越来越大,哈希表被越来越广泛地应用。特别地,哈希表算法尤其受到ACM(国际大学生程序设计竞赛)的欢迎,因为它具有极其优秀的效率,相比于队列O(n)的查找时间,他只需要O(1)就可以定位内存。

哈希表的原理。首先我们需要定义一种映射关系,例如从字符串到整形数据的映射(MD5也是一种映射),这种映射通常是稀疏的(并不是所有的字符串都是有意义的单词 / 频繁出现)因此,对于一对一的映射,映射后的整形也是稀疏的,为了提高空间利用率,我们对映射后的数据求余,把整形限制在一个区间内,如果这个区间足够大,那么可以认为不存在冲突。如果我们把整形数据作为内存数组的下标来寻址、访问内存空间,那么就实现了从字符串到内存地址的映射。假设我们维护的是一个结构类型,那么我们可以根据指针找回原字符串,实现双向的映射。

一些待解决的问题。由于哈希表做的是一种求余后的映射,所以必然会有许多地址从未被访问、有许多字符串具有相同的哈希值访问相同的地址,产生冲突。由于哈希表是一种以空间换时间的算法,所以空间浪费是可以牺牲的,而对于冲突的问题,我们可以在产生冲突后额外维护一个链表,在访问到内存后继续做一个小的链表查找。由于当内存大小足够大时,冲突是小概率事件,他不会对整个代码的运行效率有多少影响。

哈希表的应用。哈希表最优秀的地方在于其搜索时间,而缺点在于其实现复杂度,因此特别适用于需要巨量搜索的场合,例如微软面试中提到的一个题,假设电脑中所有文件都是文章,里面含有大量的英文单词,要求统计英文单词的出现频率(这里把无关的部分省略了)。那么我们就可以维护一个哈希表,然后每得到一个单词,就进行一次映射(哈希、求余),并把访问的地址里的数据加一。
(例:mapInt=hash(charArr)%10000;buff[mapInt]++)
最后我们再遍历哈希表,根据结构指针找回原来的字符串,然后再输出对应的频率即可(哈希映射后顺序会被打乱,如果需要按字典序输出,需要另外排序)。

设n为单词总量相对于队列O(n^2)、字典树O(nlogn)的复杂度,哈希表只有O(n)的复杂度。

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions