散列表(应用案例)

392 阅读6分钟

5.2 应用案例

散列表用途广泛,本节将介绍几个应用案例。

5.2.1 将散列表用于查找

手机都内置了方便的电话簿,其中每个姓名都有对应的电话号码。

1.png

假设你要创建一个类似这样的电话簿,将姓名映射到电话号码。该电话簿需要提供如下功能。

  • 添加联系人及其电话号码。

  • 通过输入联系人来获悉其电话号码。

这非常适合使用散列表来实现!在下述情况下,使用散列表是很不错的选择。

  • 创建映射。

  • 查找。

创建电话簿非常容易。首先,新建一个散列表。

phone_book = dict()

顺便说一句,Python提供了一种创建散列表的快捷方式——使用一对大括号。

phone_book = {} 与phone_book = dict()等效

下面在这个电话簿中添加一些联系人的电话号码。

phone_book["jenny"] = 8675309

phone_book["emergency"] = 911 这就成了!现在,假设你要查找Jenny的电话号码,为此只需向散列表传入相应的键。

print phone_book["jenny"]

8675309 Jenny的电话号码

如果要求你使用数组来创建电话簿,你将如何做呢?散列表让你能够轻松地模拟映射关系。

散列表被用于大海捞针式的查找。例如,你在访问像adit.io 这样的网站时,计算机必须将adit.io转换为IP地址。

无论你访问哪个网站,其网址都必须转换为IP地址。

3.png

这不是将网址映射到IP地址吗?好像非常适合使用散列表啰!这个过程被称为DNS解析(DNS resolution),散列表是提供这种功能的方式之一。

5.2.2 防止重复

假设你负责管理一个投票站。显然,每人只能投一票,但如何避免重复投票呢?有人来投票时,你询问他的全名,并将其与已投票者名单进行比对。

4.png

如果名字在名单中,就说明这个人投过票了,因此将他拒之门外!否则,就将他的姓名加入到名单中,并让他投票。现在假设有很多人来投过了票,因此名单非常长。

5.png

每次有人来投票时,你都得浏览这个长长的名单,以确定他是否投过票。但有一种更好的办法,那就是使用散列表!

为此,首先创建一个散列表,用于记录已投票的人。

voted = {}

有人来投票时,检查他是否在散列表中。

value = voted.get("tom")

如果“tom”在散列表中,函数get将返回它;否则返回None。你可使用这个函数检查来投票的人是否投过票!

6.png

代码如下。

voted = {}

def check_voter(name):

if voted.get(name):

print "kick them out!"

else:

voted[name] = True

print "let them vote!"

我们来测试几次。

check_voter("tom")

let them vote!

check_voter("mike")

let them vote!

check_voter("mike")

kick them out!

首先来投票的是Tom,上述代码打印let them vote!。接着Mike来投票,打印的也是letthem vote!。然后,Mike又来投票,于是打印的就是kick them out!。

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

5.2.3 将散列表用作缓存

来看最后一个应用案例:缓存。如果你在网站工作,可能听说过进行缓存是一种不错的做法。下面简要地介绍其中的原理。假设你访问网站facebook.com。

(1) 你向Facebook的服务器发出请求。

(2) 服务器做些处理,生成一个网页并将其发送给你。

(3) 你获得一个网页。

7.png

例如,Facebook的服务器可能搜集你朋友的最近活动,以便向你显示这些信息,这需要几秒钟的时间。作为用户的你,可能感觉这几秒钟很久,进而可能认为Facebook怎么这么慢!另一方面,Facebook的服务器必须为数以百万的用户提供服务,每个人的几秒钟累积起来就相当多了。为服务好所有用户,Facebook的服务器实际上在很努力地工作。有没有办法让Facebook的服务器少做些工作,从而提高Facebook网站的访问速度呢?

假设你有个侄女,总是没完没了地问你有关星球的问题。火星离地球多远?月球呢?木星呢?每次你都得在Google搜索,再告诉她答案。这需要几分钟。现在假设她老问你月球离地球多远,很快你就记住了月球离地球238 900英里。因此不必再去Google搜索,你就可以直接告诉她答案。这就是缓存的工作原理:网站将数据记住,而不再重新计算。

如果你登录了Facebook,你看到的所有内容都是为你定制的。你每次访问facebook.com,其服务器都需考虑你感兴趣的是什么内容。但如果你没有登录,看到的将是登录页面。每个人看到的登录页面都相同。Facebook被反复要求做同样的事情:“当我注销时,请向我显示主页。”有鉴于此,它不让服务器去生成主页,而是将主页存储起来,并在需要时将其直接发送给用户。

8.png

这就是缓存,具有如下两个优点。
  • 用户能够更快地看到网页,就像你记住了月球与地球之间的距离时一样。下次你侄女再问你时,你就不用再使用Google搜索,立刻就可以告诉她答案。

  • Facebook需要做的工作更少。

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

Facebook不仅缓存主页,还缓存About页面、Contact页面、Terms and Conditions页面等众多其他的页面。因此,它需要将页面URL映射到页面数据。

9.png

当你访问Facebook的页面时,它首先检查散列表中是否存储了该页面。

10.png

具体的代码如下。

cache = {}

def get_page(url):

if cache.get(url):

return cache[url]

else:

data = get_data_from_server(url)

cache[url] = data

return data

仅当URL不在缓存中时,你才让服务器做些处理,并将处理生成的数据存储到缓存中,再返

回它。这样,当下次有人请求该URL时,你就可以直接发送缓存中的数据,而不用再让服务器进

行处理了。

5.2.4 小结

这里总结一下,散列表适合用于:

  • 模拟映射关系;

  • 防止重复;

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