散列函数&散列表

(《算法图解》)
一 散列函数

  • 无论你给它什么数据,它都还你一个数字。即散列函数“将输入映射到数字”。
    eg: “hello” —>2
    “Apple”—>4

  • 它必须是一致的,即输入“Apple”时得到的是4,那么每次输入“Apple”时得到的必须为4。

  • 将不同的输入映射到不同的数字。

二 散列表

在这里插入图片描述

1.将“apple”作为输入交给散列函数,输出为3(通过散列函数决定将苹果价格存放在数组中哪个位置),因此将苹果的价格(0.67元)存储到数组的索引3处。

2.将“milk”作为输入交给散列函数,输出为0,因此将牛奶的价格(1.49元)存储到数组的索引0处。

3.散列函数准确的指出了价格的存储位置。

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

5.大部分语言中都提供了散列表实现。python中提供的散列表实现为字典

book = dict()#创建散列表
book["apple"] = 0.67#一个苹果的价格为0.67元
book["milk"] = 1.49#一盒牛奶的价格为1.49元
book["avocado"] = 1.49
print(book)

输出如下:
在这里插入图片描述
让我们来查询鳄梨的价格:

print(book["avocado"])

输出如下:

在这里插入图片描述
散列表(字典)由组成。在上述的散列表中,键为商品名,值为商品价格。散列表将键映射到值。

三 应用案列
1 将散列表用于查找

phone_book = {}#与phone_book = dict()等效
phone_book["jenny"] = 55556
phone_book["marry"] = 85525
print(phone_book["jenny"])

输出:
在这里插入图片描述
散列表被被用于大海捞针式的查找,比如访问网站时,计算机必须将网址转化为ip地址
Google.com–>74.125.239.133

2 防止重复

假设你负责一个投票站,每人只能投一票,为了避免重复投票,你先询问这个人的名字,并在已投票名单中对比,如果投过了,就不让他投了。
为此,首先创建一个散列表,用于记录已投票的人:

voted = {}

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

value = voted.get("Tom")

如果Tom在散列表中,函数get返回True,否则返回None。
完整代码:

voted = {}
def check_voter(name):
    if voted.get(name):
        print("他已经投过了")
    else:
        voted[name] = True
        print("请让他投票")

check_voter("tom")
check_voter("jame")
check_voter("jame")

输出如下:
在这里插入图片描述
如果你将已投票者的姓名存储在列表中,这个函数的速度终将变得非常慢,因为它必须使用简单查找搜索整个列表。
但这里将它们存储在散列表中,让你迅速知道来投票的人是否投过票。

3 将散列表用作缓存
假如你有个沙雕舍友总问你问题,火星离地球多远?水星呢?月球呢?你每次都要打开浏览器搜索,再告诉他答案,这要花费个几分钟。但是你很快记住了月球距离地球238900公里,因此你就不用再去花几分钟搜索,可以直接告诉他答案。
这就是缓存的原理:网站将数据记住,而不再重新计算。
需要将页面url映射到页面数据:
facebook.com/about -->About页面的数据
facebook.com–>主页的数据
当你访问facebook的页面时,它首先检查散列表中是否存储了该页面。

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时,就可以直接发送缓存中的数据,而不用再让服务器进行处理了。

四 冲突
若你选择的散列函数为按照首字母映射,
apple和avocado就会冲突
最简单的方法: 如果两个键映射到了同一个位置,就在这个位置存储一个链表。
但是,假如销售的商品全部都是a开头,那么第一个位置就包含一个很长的链表,后面的散列表都是空的。这种情况散列表的情况急剧下降。
所以散列函数的选择很重要。

五 性能

平均状况 /最糟状况/ 数组/ 链表
查找O(1)/ O(n) /O(1)/O(n)
插入O(1)/ O(n)/ O(n) / O(1)
删除O(1)/ O(n)/ O(n) / O(1)

O(1)被称为常量时间,并不意味着马上,而是说不管散列表多大,所需的时间都相同。

在这里插入图片描述
意味着无论散列表包含一个元素还是10亿个元素,从中获取数据所需的时间都相同。
实际上,常量时间——从数组中获取一个元素所需的时间就是固定的:不管数组多大,从中获取一个元素所需的时间都是相同的。
平均状况下,散列表的查找(获取给定索引处的值)速度与数组一样快,而插入和删除速度与链表一样快。
最糟情况下,各种操作速度都很慢。
为此要避免冲突,需要:

  1. 较低的装填因子
  2. 良好的散列函数

1.装填因子(度量散列表中有多少位置是空的)
公式:散列表包含的元素数/位置总数
一旦装填因子大于0.7,就调整散列表长度
2.良好的散列函数
良好的散列函数让数组中的值成均匀分布
糟糕的散列函数让值扎堆,导致大量的冲突

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值