哈希表(一)

哈希表的应用

编程语言
文件系统
密码验证
存储优化

IP地址

IP地址表分析

分析登陆日志,迅速获得登陆服务器的ip情况:昨晚有没有登陆服务器?登陆的次数?一个小时之前有多少人登陆了服务器?

#1个小时之内的登陆量可能有数百万次
#一个问题接着一个问题处理会很慢
#计数:一个小时之前每个ip登陆服务器的次数
#C是存储ip地址到计数器映射的特殊数据结构

伪代码分析:

log——每行登陆日志(time,IP)组成的数组
C——IP地址到计数器的映射
i——第一行没被处理的日志
j——在一个小时之前的第一行日志

i <---- 0
j <---- 0
C <---- null
Each Second
    UpdateAccesslist(log,i,j,C)

UpdateAccesslist(log, i, j, C)

while log[i].time <= Now()
    C[log[i].IP] <---- C[log[i].IP] + 1
    i <---- i + 1
while log[j].time <= Now() - 3600
     C[log[j].IP] <---- C[log[j].IP] - 1
     j <---- j + 1

AccessedLastHour(IP,C)

return C[IP] > 0
映射C的实现
直接寻址

C的实现需要一个数据结构
IP(v4)一共有2^32个不同的地址
把IP地址转换成32-bit的整数
创建一个大小为2^32的整数数组A
使用A[int(IP)]作为C[IP]

把IP转换成整数
int(IP)

return IP[1] × 2^24 + IP[2] × 2^16 + IP[3] × 2^8 IP[4]

UpdateAccessList(log, i, j, A)

while log[i].time < Now()
    A[int(log[i].IP)] <---- A[int(log[i].IP)] + 1
    i <---- i + 1
while log[j].time < Now() - 3600
    A[int(log[j].IP)] <---- A[int(log[j].IP)] + 1
    j <---- j + 1

AcceseedLastHour(IP)

return A[int(IP)]
方法特点

UpdateAccessList()处理每行日志的时间复杂度为O(1)
AccessedLastHour()的时间复杂度为O(1)
但是即使只有少量的IP地址,仍需要2^32的存储空间
IPv6:需要2^128的存储空间,这不现实

基于链表的映射

直接寻址需要大量的存储空间
只存储活跃的IP地址
使用链表存储
对于每个IP地址,只存储最后出现的那一次
保持IP访问的顺序

UpdateAccessList(log, i, j, L)

while log[i].time <= Now()
    if L.Find(log[i],IP):
        L.Erase(log[i].IP)
    L.append(log[i].IP)
    i <---- i + 1
while log[j].time < Now() - 3600:
    if L.top() == log[j].IP:
        L.pop()
    j <---- j + 1

AccessesLastHour(IP, L)

return L.Find(IP)

方法特点:

n是活跃IP地址的数目
内存的使用量是O(n)
L.Append, L.Top, L.pop的时间复杂度为O(1)
L.Find, L.Erase的时间复杂度为O(n)
UpdateAccessList的时间复杂度为O(n)(每行)
AccessLastHour的时间复杂度为O(n)

哈希函数

对IP地址进行编码

使用较小的整数对IP地址进行编码
目前存在的活跃不同的IP具有不同的编码

哈希函数的定义

对于对象S的任意集合和任意整数m>0,函数h:S——>{0,1,…,m-1},函数h称为哈希函数。m为哈希函数的基数

希望哈希函数具有的性质:

函数h的运算速度很快
不同的对象对应不同的哈希值
内存使用量为O(m)的直接寻址
基数m尽可能小
如果对象的数目大于基数m时,不可能存在所有不同的哈希值

冲突的定义

当h(O1)=h(O2)且O1 != O2时,就是冲突了

链接

存储对象到其他对象的映射
应用
文件名 ——> 文件在磁盘上的位置
学号 ——> 学生姓名
联系人——> 联系电话号码

定义

具有HashKey(o),Get(o),Set(o)方法的,从S映射到V的数据结构。其中O属于S,v属于V

h:S —> {0,1,2,…,m-1}

伪代码实现
A <—— 有映射对(o,v)组成的m条链组成的数组
HashKey(o)

L <--- A[h(o)]
for (o',v') in L:
    if o' == o
        return True
return False

Get(o)

L <--- A[h(0)]
for (o', v') in L:
    if o' == o:
         return v'
return n/a

Set(o,v)

L <--- A[h(o)]
for (o', v') in L:
    if o' == o:
         v' = v
         return 
L.append(o,v)

方法分析:
定理

设c是数组A中的最长链的长度,则HashKey, Get, Set函数的时间复杂度为O(c + 1)
设n是映射中不同键o的数目,m是基数,那么链接需要的存储空间为O(n+m)

哈希表

集合的定义

具有Add(O), Remove(o),Find(o)方法的数据结构就是集合

集合的实现
A是对象O组成的m条链组成的数组
Add(o)

L <--- A[h(o)]
for o' in L:
    if o' == o
        return
L.append(o)

Find(o)

L <--- A[h(o)]
for o' in L:
    if o' == o:
       return true
return False

Remove(o)

if not Find(o):
    return
L <--- A[h(o)]
L.Erase(o)

哈希表
定义

使用哈希函数实现的集合或者映射叫哈希表

哈希表在编程语言中的使用:

set:

unorderedsetC++
hashsetjava
setpython

map

unordered_mapc++
hashmapjava
dictpython

总结

链接是实现哈希表的一种手段
需要存储容量为O(n+m)
操作的时间复杂度为:O(1)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值