哈希表、集合、映射

151 阅读3分钟

哈希表的原理与实现

哈希表

原理

哈希表(Hash table)又称散列表,是一种可以通过“关键码”(key)直接进行访问的数据结构。

哈希表由两部分组成

一个数据结构,通常是链表、数组

Hash 函数,输入“关键码”( key) ,返回数据结构的索引

介绍

  1. 对外表现为可以通过关键码直接访问: hash_table[key] = value,实际上是在数据结构的hash(key)位置处存储了value: data_structure[hash(key)]=value
  2. 最简单的例子(count()函数),关键码是整数,定义 hash(key) = key,那这个哈希表其实就是一个数组了, key自己就是下标
  3. 当然,一般情况下,关键码key是一个比较复杂的信息,比如很大的数、字符串这时候key就不能直接作为数据结构的下标了
  4. 此时就需要设计一个Hash函数,把复杂信息映射到一个较小的值域内,作为索引

例子

一个简单的 hash_table["lies"]= 233的例子,以各字符ASCIl码相加mod 20为Hash 函数

image.png

哈希碰撞

原理

哈希碰撞(Collisions)指的是两个不同的key被计算出同样的 Hash结果

把复杂信息映射到小的值域,发生碰撞是不可避免的

好的Hash函数可以减少碰撞发生的几率,让数据尽可能地均衡分布

哈希碰撞+开散列

开散列是最常见的碰撞解决方案

image.png

  • Hash 函数依然用于计算数组下标

  • 数组的每个位置存储一个链表的表头指针(我们称它为表头数组)

  • 每个链表保存具有同样Hash值的数据

形象描述:“挂链”——表头数组每个位置“挂”着一个链表

工程应用

  • 电话号码簿
  • 用户信息表
  • 缓存( LRU Cache)
  • 键值对存储( Redis )

完整结构图

时间复杂度

  • 期望: 插入、查询、删除O(1)
    • 数据分布比较均衡时
  • 最坏: 插入、查询、删除O(n)
    • 数据全部被映射为相同的Hash值时

无序集合、映射的实现与应用

集合与映射

  • 集合( set)存储不重复的元素
    • 有序集合,遍历时按元素大小排列,一般用平衡二叉搜索树实现,O(logN)
    • 无序集合,一般用hash实现,O(1)
  • 映射( map)存储关键码(key)不重复的键值对( key-value pair)
    • 有序集合,遍历时按照key大小排列,一般用平衡二叉搜索树实现,O(logN)
    • 无序集合,一般用哈希表实现,O(1)

对于语言内置的类型( int, string),已经有默认的优秀的hash 函数,可以直接放进set/map里使用

Python code

list_a = list([1, 2, 3, 4])

集合

set_a = {'peiqi', 'qioazhi', 'suxi'}
set_b = set(list_a)

字典

map_a = {'peiqi': 100, 'qiaozhi': 50, 'suxi': 30}

Cache

缓存的两个要素:大小、替换策略

常见替换算法:

  • LRU - least recently used,最近最少使用(淘汰最旧数据)

  • LFU - least frequently used,最不经常使用(淘汰频次最少数据)

实战:实现一个LRU

LRU cache