【算法导论】笔记-第十章 散列表

第10章 散列表

  • 散列表是实现字典操作的一种有效数据结构。

10.1 直接寻址表

  • 直接寻址表:表示动态集合,记作 T [ 0.. m − 1 ] T[0..m-1] T[0..m1]。其中每个位置称作
  • 槽:槽k指向集合中一个关键字为k的元素。若没有关键字为k的元素,则 T ( k ) = N I L T(k)=NIL T(k)=NIL
  • 缺点:全域U很大,存储不现实。并且浪费空间。

10.2 散列表

  • 利用散列函数h,由关键字k计算出槽的位置。函数h将关键字的全域U映射到散列表 T [ 0.. m − 1 ] T[0..m-1] T[0..m1]的槽位上: h : U → { 0 , 1 , ⋅ ⋅ ⋅ , m − 1 } h:U\rightarrow \{0,1,···,m-1\} h:U{0,1,,m1} h ( k ) h(k) h(k)即为关键字k的散列表
  • python提供的散列表为字典
  • 冲突:两个关键字可能映射到同一个槽中。
    • 解决方法:
      • 使散列函数h尽可能的随机,以避免冲突或者使冲突的次数减少
      • 链接法
      • 开放寻址法
  • 通过链接法解决冲突:把散列到同一槽中所有元素都放在一个链表中。槽 j j j中有一个指针,它指向存储所有散列到 j j j的元素的链表的表头。

10.3 散列函数

  • 好的散列函数的特点:每个关键字都被等可能地散列到m个槽位中任何一个。

  • 构造好的散列函数:

    • 启发式方法:用除法进行散列和用乘法进行散列。
    • 全域散列
  • 将关键字转换为自然数:如果所给关键字不是自然数,则必须有一种方法来将它们解释为自然数。

  • 除法散列表:通过取k除以m的余数,将关键字k映射到m个槽中的某一个上

    • 散列函数: h ( k ) = k   m o d   m h(k)=k\ mod\ m h(k)=k mod m
  • 乘法散列表:

    • 步骤:
      • 用关键字k乘上常数A(0<A<1),并提取kA的小数部分。
      • 用m乘以这个值,再向下取整
    • 散列函数: h ( k ) = ⌊ m ( k A m o d    1 ) ⌋ h(k)=\lfloor m(kA\mod 1)\rfloor h(k)=m(kAmod1)
  • 全域散列法:从一组精心设计的函数中,随机地选择散列函数

10.4 开放寻址法

  • 开放寻址法:每个表项或包含动态集合的一个元素,或包括NIL。当查找某个元素时,要系统地检查所有的表项,直到找到所需的元素,或最终查明该元素不在表中,这一操作称为探查

  • 伪代码:

    • HASH-INSERT(T, k):插入

      i = 0
      repeat
      j = h(k, i)
      if T[j] == NIL
          T[j] = k
          return j
      else i = i + 1
      until i == m
      error "hash table overflow"
      
    • HASH-SEARCH(T, k):查找

      i = 0
      repeat
          j = h(k, j)
          if T[j] == k
              return j
          i = i + 1
      until T[j] == NIL or i == m
      return NIL
      
    • python代码:

      ## 除法散列表
      def h(k, m):
          return k % m
      
      ## 插入
      def hash_insert(T, k):
          m = len(T)
          i = 1
          while i < m:
              j = h(k, i)
              if T[j] == None:
                  T[j] = k
                  return j
              else:
                  i = i + 1
          return "hash table overflow"
      
      ## 搜索
      def hash_search(T, k):
          m = len(T)
          i = 1
          j = h(k, i)
          while i < m and T[j] != None:
              j = h(k, i)
              if T[j] == k:
                  return j
              else:
                  i = i + 1
          return None
      
      T = [None]*10 ## 初始化散列表
      for k in [79,69,98,72,14,50]:
      	print(hash_insert(T, k))
          
      for k in [79,98,14,900]:
      	print(hash_search(T, k))
      
      
  • 计算开放寻址法中的探查序列

    • 线性探查:给定一个散列函数 h ′ : U → { 0 , 1 , ⋅ ⋅ ⋅ , m − 1 } h':U\rightarrow \{0,1,···,m-1\} h:U{0,1,,m1},称之为辅助散列函数
      • 散列函数: h ( k , i ) = ( h ′ ( k ) + i )   m o d   m ,   i = 0 , 1 , ⋅ ⋅ ⋅ , m − 1 h(k,i)=(h'(k)+i)\ mod\ m,\ i=0,1,···,m-1 h(k,i)=(h(k)+i) mod m, i=0,1,,m1
      • 缺点:一次群集问题,即随着时间的推移,连续被占用的槽不断增加,平均查找时间也随着不断增加。
    • 二次探查:
      • 散列函数: h ( k , i ) = ( h ′ ( k ) + c 1 i + c 2 i 2 )   m o d   m ,   i = 0 , 1 , ⋅ ⋅ ⋅ , m − 1 h(k,i)=(h'(k)+c_1i+c_2i^2)\ mod\ m,\ i=0,1,···,m-1 h(k,i)=(h(k)+c1i+c2i2) mod m, i=0,1,,m1
      • 初始的探查位置是 T [ h ′ ( k ) ] T[h'(k)] T[h(k)]
      • 如果两个关键字的初始探查位置相同,那么它们的探查序列也是相同的,这一性质导致一种程度较轻的群集现象,称为二次群集。
    • 双重散列
      • 散列函数: h ( k , i ) = ( h ( k ) + i h 2 ( k ) )   m o d   m ,   i = 0 , 1 , ⋅ ⋅ ⋅ , m − 1 h(k,i)=(h(k)+ih_2(k))\ mod\ m,\ i=0,1,···,m-1 h(k,i)=(h(k)+ih2(k)) mod m, i=0,1,,m1
      • 初始的探查位置是 T [ h 1 ( k ) ] T[h_1(k)] T[h1(k)]
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

From Star.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值