哈希表(一)

来源:互联网 发布:淘宝第三方服务商 编辑:程序博客网 时间:2024/04/29 06:51

哈希表的应用

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

IP地址

IP地址表分析

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

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

伪代码分析:

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

i <---- 0j <---- 0C <---- nullEach 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 + 1while 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 + 1while 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 + 1while 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 Truereturn 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        returnL.append(o)

Find(o)

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

Remove(o)

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

哈希表
定义

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

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

set:

unorderedset C++ hashset java set python

map

unordered_map c++ hashmap java dict python

总结

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

0 0
原创粉丝点击