算法(C语言)-hash查询-学习笔记07

本文详细探讨了哈希表的实现,包括直接寻址表、散列表(链接法解决冲突)、散列函数(除法、乘法和全域散列法)以及开放寻址法。通过分析不同方法的优缺点,强调了散列函数选择的重要性。文章还介绍了完全散列的概念,确保在静态关键字集合下实现O(1)的访问。最后,给出了简单的C语言实现示例。
摘要由CSDN通过智能技术生成

正好以前上算法课的时候有做过hash的笔记,直接把笔记搬运过来了,可能和具体实现有点不一样,但道理是一样的。当时上课的书是算法导论
所有代码实现:Tian-hy/c_ds

1. 直接寻址表(Direct-address Tables)

直接寻址表,记为 T [ 0... m − 1 ] T[0...m-1] T[0...m1], 其中每个位置称为槽(Slot),对应全域U的一个关键字(Key),key指向satellite data,若没有key为k的satellite data,则 T [ k ] = N I L T[k]=NIL T[k]=NIL


O ( 1 ) O(1) O(1)

DIRECT-ADDRESS-SEARCH(T, k)
return T[k]

O ( 1 ) O(1) O(1)

DIRECT-ADDRESS-INSERT(T, x)
T[x.key] = x

O ( 1 ) O(1) O(1)

DIRECT-ADDRESS-DELETE(T, x)
T[x.key] = NIL

优点 将对象直接存放在表的槽中,从而节约空间
只需要知道下标就可以找到元素,不必存储关键字。然而不存储关键字,就必须用某种方法确定槽是否为空。
缺点 易造成表的空间不够或空间被浪费

2. 散列表

  • 在直接寻址下,具有关键字k的元素被放到槽k中。在散列的方式下,该元素被放到 h ( k ) h(k) h(k)中;即利用散列函数(hash function)h,计算出关键字k的槽的位置。

h : U → { 0 , 1 , . . . , m − 1 } h:U \rightarrow \{0, 1, ..., m-1\} h:U{ 0,1,...,m1}

  • 读作:关键字k的元素被散列到槽 h ( k ) h(k) h(k)上,或 h ( k ) h(k) h(k)是关键字k的散列值
  • 尽管尽可能的使h随机,但是难免出现冲突。

优点:

  1. 散列表的存储需求只需要 θ ( ∣ K ∣ ) \theta (|K|) θ(K),且查找一个元素任然只需要 O ( 1 ) O(1) O(1)

缺点:

  1. O ( 1 ) O(1) O(1)是平均情况时间,对直接寻址表来说是最坏情况时间
  2. 会出现冲突(collision)

解决冲突1:链接法

1. 链接发的介绍
  • 在链接法中,把散列到同一槽中的所有元素都放在一个链表中。槽j中有一个指针,指向散列到j的元素的链表的表头;弱不存在这样的元素,则指向NIL

Insert: O ( 1 ) O(1) O(1),插入相较于其他操作要稍微快一点,因为在此假设插入的元素没有出现在表中;若要检查x是否在表中需要付出额外代价,需要执行一个search来查找。

CHAINED-HASH-INSERT(T,x)
insert x at the head of list T[h(x.key)]

Search: O ( n ) O(n) O(n),查找方法的最坏情况运行时间与表的长度成正比。

O ( 1 ) O(1) O(1),期望时间。( O ( 1 + α ) O(1+\alpha) O(1+α),而 α \alpha α O ( 1 ) O(1) O(1)

CHAINED-HASH-SEARCH(T,k)
search for an element with key k in list T[h(k)]

Delete:

双向链表: O ( 1 ) O(1) O(1),因为我们输入的是x,而x包含x.key,所以无需搜索就可以直接找到x的位置,叫x.prev的元素指向x.next的元素,即可完成删除操作。

单向链表: O ( n ) O(n) O(n),我们可以直接找到x.key的位置,但是因为是单向链表所以无法直接找到x.prev,也就是说需要遍历链表找到x.prev,再将前一个元素的key指向后一个元素的key,完成删除操作,渐进运行时间与search相同

CHAINED-HASH-DELETE(T,x)
delete x from the list T[h(x.key)]

2. 链接法散列的分析(查找一个给定关键字的时间)
  • 最坏情况下查找的时间为 θ ( n ) \theta(n) θ(n)

  • 散列的平均性能依赖于所选取的散列函数h

  • Assume:

    1. 放n个元素、具有m个槽位的散列表T,定义装载因子(load factor) α = n / m \alpha =n/m α=n/m

    2. 简单均匀散列(simple uniform hashing)
      对于 j = 0 , 1 , . . . , m − 1 j=0, 1, ..., m-1 j=0,1,...,m1,列表 T [ j ] T[j] T[j]的长度用 n j n_j nj来表示,于是有
      n = n 0 + n 1 + . . . + n m − 1 n=n_0+n_1+...+n_{m-1} n=n0+n1+...+nm1
      n j n_j nj的期望长度为 E [ n j ] = α = n / m E[n_j]=\alpha=n/m E[nj]=α=n/m

    3. 定义:
      查找不成功,表中没有一个元素的关键字为k
      查找成功,成功找到关键字为k的元素


定理11.1: 在简单均匀散列的假设下,对于用链接法解决冲突的散列表,一次不成功searchavg time θ ( 1 + α ) \theta(1+\alpha) θ(1+α)

定理11.2: 在简单均匀散列的假设下,对于用链接法解决冲突的散列表,一次成功查询的avg time为 θ ( 1 + α ) \theta(1+\alpha) θ(1+α)

结论: 若散列表中槽数与表中的元素成正比,全部字典操作平均情况下都可以在 O ( 1 ) O(1) O(1)的时间内完成

3. 散列函数

介绍散列的三种具体方法:两种启发式方法(乘法除法进行散列)与一种利用随机技术来提供可证明的良好性能(全域散列,universal hashing)

  • 一种好的方法导出的散列值,在某种程度上应独立与数据可能存在的任何模式,甚至很接近的关键字要被散列到截然不同的散列值上。

  • 关键字转换为自然数

    将字符串转换为ASCII码,随后以128为基数来表示。

    举例: p t → ( p = 112 , t = 116 ) → p t = ( 112 ∗ 128 ) + 116 = 14452 pt\rightarrow (p=112, t=116) \rightarrow pt=(112*128)+116=14452 pt(p=1

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值