0
点赞
收藏
分享

微信扫一扫

【蓝桥杯】十天冲刺省赛,保送国赛基础知识常考点复习+必背Python代码模板 | Day04 | 数据结构基础之散列表(Hash)

MaxWen 2022-03-27 阅读 29

快接近蓝桥杯省赛了,现在是冲刺阶段,更多的是复习而不再是刷题了,刷过的题多看,多理解考点是什么,记住考过的内容,其实大部分内容都是同一个考点考来考去,只是变换了一个说法而已,所以冲刺阶段,和车神哥一起复习复习基础知识吧!!!加油~

冲刺省赛

比赛提要

数据结构基础——散列表(Hash)

主要目标是学会散列表(hash 算法)的原理与实现,学会灵活的运用,能够不依赖于模板根据题目独立写出各类散列表。数据结构 Hash 属于查找算法中的一部分,在比赛中通常也会占据一定的比例,相对较难也比较重要.

散列表(Hash)知识点:

  • Hash 的概念
  • 构造方法
  • 冲突处理

哈希表的定义

上面所提到的查找算法,简单来说,就是判断现有数据集合中是否有这个元素,或者是否有满足条件的元素。

其中的 Hash 算法则可以帮助我们判断是否有这个元素,虽然功能简单,但是其 O(1) 时间复杂度是具有高性能的。通过在记录的存储地址和它的关键码之间建立一个确定的对应关系。这样,不经过比较,一次读取就能得到所查元素的查找方法。相比普通的查找算法来说,仅仅在比较的环节,就会大大减少查找或映射所需要的时间。

我们采用散列技术将记录存储在一块连续的存储空间中,这块连续的存储空间即称为散列表。下面用一张图给大家展示一下散列表的实现过程:
在这里插入图片描述
如果还是不太明白的话,我们可以理解为数学函数,Y=F(X),X 为自变量也就是这里的 Key, F( ) 对应图中的 H( ),也就是一个映射关系,Y 因变量也就是对应的值的存放位置

散列表实战

下面我们就来学习一下关于散列表的使用方式,下面我们用一个题目来引入。

弗里的语言

输入、输出如下面示例所示:

  • 输入
1 行,输入 N,代表共计创造了多少个单词
第 2 行至第 N+1 行,输入 N 个单词

格式如下:  

  fjsdfgdfsg
  fdfsgsdfg
  bcvxbxfyres 
  • 输出
    例1:
输入:

6
1fagas 
dsafa32j
lkiuopybncv
hfgdjytr
cncxfg
sdhrest

输出:

NO

例2:

输入:

5
sdfggfds
fgsdhsdf
dsfhsdhr
sdfhdfh
sdfggfds


输出:

sdfggfds

解题思路

第一步

  • 需要创建一个散列表和一个公共溢出区
散列表
公共溢出区

第二步

  • 需要定义插入散列表函数
  1. 按照散列表的映射方式设计即可
  2. 需要传入一个参数来表示放什么数据
in(Name)
{
    1. 无冲突
    2. 冲突处理
}

第三步

  • 定义查询函数
isAt()
{
    1. 如果散列表查询成功返回 True
    2. 不为成功返回 False
}

第四步

  • 定义散列表映射函数,此处我们采用除留余数法即可
int out(string s)
{

    处理字符串 s 生成对应的 Key 值

}

第五步

  • 编写主函数代码
    输入 N

    循环 N 次://
    
        输入 word; 
        
        先查询,有相同的单词有就设置 flag 为 1,ans = word
        没有的话,就执行插入操作
    
    根据 flag 决定输出什么。

完整Python代码(必备模板)

总结给大家一个小窍门,在解题过程中可以使用:

  • Python 中是有 Hash 函数的,在这里我们直接使用它进行解题。

h = 999983
Value = ['']*h
UpValue = ['']*h
UpValueCount = 0

def isAt (s):
    n = int (hash(s)+h)%h
  #  print(n)

    if Value[n] == '':
        return False
    elif Value[n]==s:
        return True
    else:
        for i in range(0,UpValueCount):
            if UpValue[i] == s:
                return True

        return  False

def ins (s):
    global UpValueCount
    n =int (hash(s)+h)%h
    if Value[n] == '':
        Value[n]=s
        return True

    elif Value[n]==s:
        return False
    else:
        for i in range(0,UpValueCount):
            if UpValue[i] == s:
                return False

        UpValue[UpValueCount] = s

        UpValueCount=UpValueCount+1

        return  True
if __name__=='__main__':


    N=int (input())
    ans = 'NO'
    flag = False
    while N>0:
        N-=1
        word=input()
      #  print(word)
        if(not(flag)) :
            if(isAt(word)):
                flag=True
                ans=word
            else:
                ins(word)
    print(ans)

在这里插入图片描述

散列表的缺陷

散列表并不是适用于所有的需求场景,那么哪些情况下不适合使用呢?

  1. 散列技术一般不适合在允许多个记录有同样关键码的情况下使用。

    因为这种情况下,通常会有冲突存在,将会降低查找效率,体现不出散列表查找效率高的优点。

    并且如果一定要在这个情况下使用的话,还需要想办法消除冲突,这将花费大量时间,那么就失去了 O(1) 时间复杂度的优势,所以在存在大量的冲突情况下,我们就要弃用散列表。

  2. 散列方法也不适用于范围查找,比如以下两个情况。

  • 查找最大值或者最小值

    因为散列表的值是类似函数的,映射函数一个变量只能对应一个值,不知道其他值,也不能查找最大值、最小值,RMQ(区间最值问题)可以采用 ST 算法、树状数组和线段树解决。

  • 也不可能找到在某一范围内的记录

    比如查找小于 N 的数有多少个,是不能实现的,原因也是映射函数一个变量只能对应一个值,不知道其他值。

散列技术的关键问题

在使用散列表的时候,我们有两个关键的技术问题需要解决:

  1. 散列函数的设计,如何设计一个简单、均匀、存储利用率高的散列函数?
  2. 冲突的处理,如何采取合适的处理冲突方法来解决冲突。

如何设计实现散列函数

  1. 在构建散列函数时,我们需要秉持两个原则:
  • 简单

    • 散列函数不应该有很大的计算量,否则会降低查找效率。
  • 均匀:

    • 函数值要尽量均匀散布在地址空间,这样才能保证存储空间的有效利用并减少冲突。

散列函数实现三种方法

1. 直接定址法

散列函数是关键码(Key)的映射的线性函数,形如:
H ( k e y ) = a ∗ k e y + b H(key)=a∗key+b H(key)=akey+b
来看一个小案例:

H ( k e y ) = 1 11 ∗ k e y + 0 H ( k e y ) = 111 ​ ∗ k e y + 0 H(key) = \frac{1}{11} * key + 0H(key)= 11 1 ​ ∗key+0 H(key)=111key+0H(key)=111key+0
如图:
在这里插入图片描述
缺点:

  • 我们是看到了这个集合,然后想到他们都是 11 的倍数才想到这 Hash 函数。我们在平常的使用中一般不会提前知道 Key 值集合,所以使用较少。

适用范围:

  • 事先知道关键码,关键码集合不大且较为连续而不离散。

2. 除留余数法

H ( k e y ) = k e y m o d p H(key)=key mod p H(key)=keymodp
来个小例子:

这种方法是最常用的方法,这个方法的关键在于如何选取 P,使得利用率较高并且冲突率较低,一般情况下,我们会选取最接近表长且小于等于表长的最大素数。

缺点:

  • P 选取不当,会导致冲突率上升。

适用范围:

  • 除留余数法是一种最简单、也是最常用的构造散列函数的方法,并且不要求事先知道关键码的分布。

3. 数字分析法

比如我将我的集合全部转化为 16 进制数,根据关键码在各个位上的分布情况,选取分布比较均匀的若干位组成散列地址。或者将 N 位 10 进制数,观察各各位的数字分布,选取分布均匀的散列地址。

举例:
在这里插入图片描述
首先我们考虑一位作为散列函数,发现都是很多冲突,选取两位时,百位和十位组合最适宜,分布均匀且没有冲突。

当然,我们说的是这一方法的一个具体实列,既然叫做数字分析法,那么只有对于不同数据的不同分析,才能写出更是适配的 H(x)。

另外还有两种平时使用极少的方法,分别是平方取中法和折叠法,就不再做过多的讲解。

写在最后

车神哥也是第一次参赛——研究生组。不知道难度如何,所以尽力准备就好啦!重要的不是结果,而是那段你孤勇奋战的日子,这样的日子,我相信每个人在人生中都很珍贵。或许是高考,或许是考研,或许是为了仅有的一次升职加薪机会等等。

最近疫情也严重起来了,希望大家保护好自己,安全第一,比赛第二。

有梦想,每个人都很了不起!

往期回顾

  1. 十天冲刺省赛,保送国赛基础知识常考点复习+必背Python代码模板 | Day01 | 数据结构基础之链表
  2. 十天冲刺省赛,保送国赛基础知识常考点复习+必背Python代码模板 | Day02 | 数据结构基础之队列
  3. 十天冲刺省赛,保送国赛基础知识常考点复习+必背Python代码模板 | Day03 | 数据结构基础之栈

官方刷题练习系统:http://lx.lanqiao.cn/


ღ( ´・ᴗ・` )

举报

相关推荐

0 条评论