• 【算法】散列表


    散列表

    散列函数

    定义

    散列函数“将输入映射到数字”。即无论你给它什么数据,它都还你一个数字。

    散列函数必须满足一些要求

    它必须是一致的。例如,假设你输入apple时得到的是4,那么每次输入apple时,得到的都必须为4。

    它应将不同的输入映射到不同的数字。 例如, 如果一个散列函数不管输入是什么都返回1,它就不是好的散列函数。最理想的情况是,将不同的输入映射到不同的数字。

    散列函数准确地指出了存储位置,具体原因如下:

    l  散列函数总是将同样的输入映射到相同的索引。

    l  散列函数将不同的输入映射到不同的索引。

    l  散列函数知道数组有多大,只返回有效的索引。

    散列表

    使用散列函数和数组创建了一种被称为散列表(hashtable)的数据结构

    l  散列表是一种包含额外逻辑的数据结构。数组和链表都被直接映射到内存,但散列表更复杂,它使用散列函数来确定元素的存储位置

    l  散列表也使用数组来存储数据,因此其获取元素的速度与数组一样快

    python使用字典来实现散列表功能,可使用函数dict创建散列表

    应用

    将散列表用于查找

    示例:

    l  创建映射。

    l  查找。

    >>> phonebook={}
    >>> phonebook['li']=123456
    >>> phonebook['p']=987654
    >>> print(phonebook['p'])
    987654
    
    防止重复

    示例:检查是否存在某个元素

    voted={}
    def check_voter(name):
        if voted.get(name):              #使用函数get来返回是否存在。不存在返回None
            print('kick them out!')
        else:
            voted[name]=True
            print('let them vote')
     
    check_voter('tom')
    
    check_voter('jerry')
    
    check_voter('jerry')
    
    将散列表用作缓存

    缓存的工作原理:网站将数据记住,而不再重新计算。

    缓存优点

    l  用户能够更快地看到网页

    l  需要做的工作更少。

    缓存是一种常用的加速方式,所有大型网站都使用缓存,而缓存的数据则存储在散列表中!

    访问过程

    cache={}
    def get_page(url):
        if cache.get(url):                        #检查缓存中是否存储了该页面
            return cache[url]                      #存储了,即返回它
        else:
            data=get_data_from_server(url)            #没存储,从服务器调用
            cache[url]=data                           #将其存储到缓存中
            return data                               #返回该页面
    

      

    小结

    散列表适合用于:

    l  模拟映射关系;

    l  防止重复;

    l  缓存/记住数据,以免服务器再通过处理来生成它们

    冲突(collision)

    定义

    给两个键分配的位置相同。

    处理冲突的方式

    如果两个键映射到了同一个位置,就在这个位置存储一个链表。

    经验

    散列函数很重要。避免散列函数将所有的键都映射到一个位置,而最理想的情况是,散列函数将键均匀地映射到散列表的不同位置。

    如果散列表存储的链表很长,散列表的速度将急剧下降。然而, 如果使用的散列函数很好,这些链表就不会很长!

    散列函数很重要,好的散列函数很少导致冲突。

    性能

    在平均情况下,散列表执行各种操作的时间都为O(1) O(1)被称为常量时间

    散列表的性能

    操作

    平均情况

    最糟情况

    查找

    O(1)

    O(n)

    插入

    O(1)

    O(n)

    删除

    O(1)

    O(n)

    散列表同数组和链表比较

    操作

    平均情况

    最糟情况

    数组

    链表

    查找

    O(1)

    O(n)

    O(1)

    O(n)

    插入

    O(1)

    O(n)

    O(n)

    O(1)

    删除

    O(1)

    O(n)

    O(n)

    O(1)

    在平均情况下,散列表的查找(获取给定索引处的值)速度与数组一样快,而插入和删除速度与链表一样快,因此它兼具两者的优点!

    但在最糟情况下,散列表的各种操作的速度都很慢。

    在使用散列表时,避开最糟情况至关重要。需要有:

    较低的填装因子

    l  良好的散列函数。

    填装因子

    计算公式

    散列表的填装因子=散列表包含的元素数/位置总数

    调整长度(resizing)

    填装因子大于1意味着元素数量超过了数组的位置数。

    一旦填装因子开始增大,就需要在散列表中添加位置,这被称为调整长度(resizing)

    经验

    填装因子越低,发生冲突的可能性越小,散列表的性能越高。

    一个不错的经验规则是:一旦填装因子大于0.7,就调整散列表的长度

    良好的散列函数

    良好的散列函数让数组中的值呈均匀分布

    糟糕的散列函数让值扎堆,导致大量的冲突。

    小结

    l   可以结合散列函数和数组来创建散列表。

    l   冲突很糟糕,应使用可以最大限度减少冲突的散列函数

    l   散列表的查找、插入和删除速度都非常快。

    l   散列表适合用于模拟映射关系

    l   一旦填装因子超过0.7,就该调整散列表的长度

    l   散列表可用于缓存数据(例如,在Web服务器上)。

    l   散列表非常适合用于防止重复

  • 相关阅读:
    SOJ 3531_Number Pyramids
    TYVJ P1047 乘积最大 Label:dp
    TYVJ P1067 合唱队形 Label:上升子序列?
    TYVJ P1093 验证数独 Label:none
    TYVJ P1088 treat Label:鞭笞人的DP
    TYVJ P1008 传球游戏
    表达式系列问题
    数字三角形系列 系列问题
    TYVJ P1024 外星人的密码数字
    TYVJ P1056 能量项链 Label:环状区间DP
  • 原文地址:https://www.cnblogs.com/lilip/p/9528833.html
Copyright © 2020-2023  润新知