5、散列表

5、散列表

    散列表——最有用的基本数据结构之一。

    散列表的内部机制:实现、冲突和散列函数。

  5.1 散列函数

    散列函数是这样的函数,即无论你给它神秘数据,它都还你一个数字。(散列函数“将输入映射到数字”)。

    散列函数必须满足:

    (1)它必须是一致的。

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

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

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

    散列函数知道数组由多大,只返回有效的索引。如果数组包含五个元素,散列函数就不会返回无效索引100。

    结合使用散列函数和数组创建了一种被称为散列表(hash table)的数据结构。数组和链表都被直接映射到内存,但散列表更复杂,它使用散列函数来确定元素的存储位置。

    散列表也被称为散列映射、映射、字典和关联数组。

    散列表的速度很快!

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

    Python 提供的散列表实现为字典,你可使用函数 dict 来创建散列表。

    散列表适合用于:

    (1)仿真映射关系;

    (2)防止重复;

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

 

    代码清单5-1 散列表    

# -*- coding:UTF-8 -*-
# 创建一个空的散列表
book = dict()
# 在其中添加一些商品的价格
# 一个苹果的价格为67美分
book["apple"] = 0.67
# 牛奶的价格为1.49美元
book["milk"] = 1.49
book["avocado"] = 1.49
print book
# result: {'avocado': 1.49, 'apple': 0.67, 'milk': 1.49}
# 鳄梨的价格
print book["avocado"]
# result: 1.49

    散列表由键和值组成。在前面的散列表 book 中,键为商品名,值为商品价格。散列表将键映射到值。

  5.2 将散列表用于查找

    手机都内置了方便的电话簿,其中每个姓名都有对应的电话号码。假设你要创建一个类似这样的电话簿,将姓名映射到电话号码。该电话簿提供如下功能:

    (1)添加联系人及其电话号码;

    (2)通过输入联系人来获取其电话号码。

    代码清单5-2 使用散列表用于查找电话号码 

# -*- coding:UTF-8 -*-
# 新建一个散列表
# phone_book = {} 与 phone_book = dict()等效
phone_book = dict()
# 在这个电话簿中添加一些联系人的电话号码
phone_book["jenny"] = 8675309
phone_book["emergency"] =911

# 查找Jenny的电话号码
print phone_book["jenny"]

    将网址映射到 IP 地址,这个过程被称为 DNS 解析(DNS resolution),散列表是提供这种功能的方式之一。

    5.3 防止重复

    如果你将已投票的姓名存储在列表中,这个函数的速度终将变得非常慢,因为它必须使用简单查找搜索整个列表。但这里将它们存储在散列表中,而散列表让你能够迅速知道来投票的人是否投过票。使用散列表来检查是否重复,速度非常快。

    代码清单5-3 使用散列表防止重复

# -*- coding:UTF-8 -*-

# 创建一个散列表,用于记录已投票的人
voted = {}


def check_voter(name):
    # 有人来投票时,检查他是否在散列表中
    if voted.get(name):
        print "kick them out!"
    else:
        voted[name] = True
        print "let them vote!"


check_voter("tom")
# result: let them vote!
check_voter("mike")
# result: let them vote!
check_voter("mike")
# result: kick them out!

 

    5.4 将散列表用作缓存

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

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

    代码清单5-4 请求Facebook的一个URL伪代码

cache = {}
if cache.get{url}:
    # 返回缓存的数据
    return cache[url]
else:
    data = get_data_from_server(url)
    # 先将数据保存到缓存中
    cache[url] = data
    return data

    仅当URL不在缓存中时,你才让服务器做些处理,并将处理生成的数据存储到缓存中,再返回它。这样,当下次有人请求该URL时,你就可以直接发送缓存中的数据,而不用再让服务器进行处理了。

     5.5 冲突

    处理冲突的方式很多,最简单的办法如下:如果两个键映射到了同一个位置,就在这个位置存储一个链表。

    最理想的情况是,散列函数将键均匀地映射到散列表的不同位置。

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

    5.6 性能

    散列表的性能:

    

    在平均情况下,散列表执行各种操作的时间都为被称为常量时间。它并不意味着马上,而是说不管散列表多大,所需的时间都相同。

     在使用散列表时,避开最糟糕情况至关重要。为此,需要避免冲突,而要避免冲突,需要有:

    (1)较低的填装因子;

    (2)良好的散列函数。

 

posted @ 2019-04-26 13:44  Lamfai  阅读(197)  评论(0编辑  收藏  举报