3.31《算法图解》笔记——Chapter 5 Hash Table

算法图解笔记——Chapter 5 Hash Table
Author: Seven Zou
Email: zoushiqi0404@gmail.com
Language: Python2.7


5 散列表

回顾:前面几节学到数组、链表和栈(栈不能用于查找),还学到了简单查找、二分查找查找类算法,其运行时间分别为 O ( n ) O(n) O(n) O ( l o g n ) O(logn) O(logn)
假定存在一种以名称为输入,对应数字结果为输出的场景,则针对简单查找有 O ( n ) O(n) O(n),二分查找有 O ( l o g n ) O(logn) O(logn)
而存在一种运行时间为 O ( 1 ) O(1) O(1)的查找数据结构,即引入以下概念"散列函数"。


5.1 散列函数

如上述场景,散列函数就是将输入映射到数字的一种函数,且需满足一定需求:

  • 它必须是一致的。e.g.假设输入apple输出为4,那么每次输入apple时,得到必须是4。
  • 它应不同输入映射到不同的数字。

设存在以下数组,且已知每个Value对应的Label。通过散列函数可在输入Label时,通过找到其对应的Index进而获取其对应Value。

Index01234
Valuevalue1value2value3value4value5

结合使用散列函数和数组创建为散列表(Hash Table)的数据结构,其包含额外逻辑的数据结构。数组和链表都直接映射到内存,而散列表
通过使用散列函数来确定元素的存储位置。
Remark: 散列表适用于模拟映射关系;散列表适用于防止重复。

# -*- coding: utf-8 -*-
book = dict()           # 散列表,在python中为字典,直接调用dict()

book["apple"] = 0.67    # 一个苹果的价格为67美分
book["milk"] = 1.49     # 牛奶的价格为1.49美元
book["avocado"] = 1.49

print book
print book["avocado"]   # 鳄梨的价格
Output: {'avocado': 1.49, 'apple': 0.67, 'milk': 1.49}
         1.49

5.2 Case
  • Case 1 电话簿
    1.添加联系人及电话号码;2.通过输入联系人就可获取对应号码。
# -*- coding: utf-8 -*-
phone_book = dict()     # 散列表
# phone_book = {}       # 与前面等效
phone_book["jenny"] = 8675309
phone_book["emergency"] = 911

print phone_book["jenny"]
Output: 8675309
  • Case 2 投票箱
    防止已投过票的人二次投票,需要检索人名进行确认。
# -*- coding: utf-8 -*-
def check_voter(name):
    if voted.get(name):         # 检查name在表中,返回let them vote!,否则返回kick them out!
        print "kick them out!"
    else:
        voted[name] = True
        print "let them vote!"
        

check_voter("tom")	# tom第一次投票
check_voter("mike")	# mike第一次投票
check_voter("mike")	# mike第二次投票会被拒绝
Output: let them vote!
			 let them vote!
			 kick them out!
  • Case 3 作为缓存
    缓存的工作原理在于:网站将数据记住,而不再重新计算。优点在于:1.用户能够更快地看到网站;2.运营商需要做的工作更少。而缓存是一种常用的加速方式,所以大型网站都使用缓存,而缓存的数据则存储在散列表中!一个网站页面不止包含主页,还包含诸多子页。因此,它需要将页面URL映射到页面数据。
# -*- coding: utf-8 -*-
cache = {}


def get_page(url):
    if cache.get(url):
        return cache[url]   # 返回缓存的数据
    else:
        data = get_data_from_server(url)
        cache[url] = data   # 先将数据保存到缓存中
        return data
5.3 冲突与性能

如果在根据Label通过Index寻找对应的Value时,发生了新旧值覆盖问题,那么散列表就发生了冲突。这种情况极有可能发生,主要源于散列函数不能将所以的Label均匀地映射到散列表的不同位置。但也存在Solution,可以在冲突的位置建立一个链表,这样就可以纵向检索,但如果链表过长,会导致速度极大下降。所以选择合适的散列函数是及其重要的,这里就不免提到性能。散列表所有的操作的运行时间都是 O ( n ) O(n) O(n)线性时间。所以为了避免最糟情况(冲突),较低的填装因子、良好的散列函数是必不可少的。

操作散列表(平均情况)散列表(最糟情况)数组链表
查找 O ( 1 ) O(1) O(1) O ( n ) O(n) O(n) O ( 1 ) O(1) O(1) O ( n ) O(n) O(n)
插入 O ( 1 ) O(1) O(1) O ( n ) O(n) O(n) O ( n ) O(n) O(n) O ( 1 ) O(1) O(1)
删除 O ( 1 ) O(1) O(1) O ( n ) O(n) O(n) O ( n ) O(n) O(n) O ( 1 ) O(1) O(1)

_Note:_填装因子计算公式如下,其度量的是散列表中有多少位置是空的。(因子超过0.7,就该调整散列表的长度)
填 装 因 子 = 散 列 表 包 含 的 元 素 数 / 位 置 总 数 填装因子=散列表包含的元素数/位置总数 =/


Reference

[美]Aditya Bhargava/袁国忠, 算法图解, 北京:人民邮电出版社, 2017.3.


附个人Github地址: https://github.com/shiqi0404/Algorithm_Diagram,其中包括笔记、Code还有书本pdf版。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值