若是某应用要用到一个动态集合,其中每一个元素都是全域U={0,1….,m}中的一个关键字 为表示动态集合,使用数组。称为直接寻址表,记为T[m],其中每一个位置称为一个槽slot,对应于全域中的一个关键字。槽k指向集合中一个关键字为k的元素。若是该集合中没有关键字为k的元素,则T[k]=NIL; node
当关键字的全域U比较小时,直接寻址是一种简单而有效的方法。python
当域U很大时,须要消耗大量内存,很不实际;算法
若是域U很大而实际出现的key不多,则大量空间被浪费;数组
没法处理关键字不是数字的状况。安全
直接寻址表:key为k的元素放到k的位置上。服务器
改进方法:app
(1)构建大小为m的寻址表T;函数
(2)key为k的元素放到h(k)位置上;spa
(3)h(k)是一个函数,其将域U映射到表T[0,1,...,m-1]。对象
在直接寻址表上加了一个哈希函数就成了哈希表。
哈希表(Hash Table,又称为散列表),是一种线性表的存储结构。哈希表由一个直接寻址表和一个哈希函数组成。哈希函数h(k)将k做为自变量,返回元素的存储下标。
假设有一个长度为7的哈希表,哈希函数h(k)=k%7。元素集合{14,22,3,5}的存储方式以下图:
14%7=0,所以14存在index=0的位置;22%7=1,所以22存在index=1的位置;3%7=3,所以3存在index=3的位置;5%7=5,所以5存在index=5的位置。
因为哈希表的大小是有限的,而要存储的值的总数量是无限的,所以对于任何哈希函数,都会出现两个不一样元素映射到同一个位置上的状况 ,这种状况叫作哈希冲突。
好比h(k)=k%7, h(0)=h(7)=h(14)=...
开放寻址法:若是哈希函数返回的位置已经有值,则能够向后探查新的位置来存储这个值。
线性探查:若是位置i被占用,则探查i+1,i+2,......
二次探查:若是位置i被占用,则探查i+12,i-12,i+22,i-22,......
二度哈希:有n个哈希函数,当使用第一个哈希函数h1发送冲突时,则尝试使用h2,h3,......
哈希表每一个位置都链接一个链表,当冲突发生时,冲突的元素将被加到该位置链表的最后。
# -*- coding:utf-8 -*- __author__ = 'Qiushi Huang' class LinkList: """链表类""" class Node: """链表中的节点""" def __init__(self, item=None): self.item = item self.next = None class LinkListIterator: """迭代器类""" def __init__(self, node): self.node = node def __next__(self): if self.node: # 若是node不为空 cur_node = self.node self.node = cur_node.next # 更新node为下一个 return cur_node.item # 输出前一个node else: raise StopIteration def __iter__(self): return self def __init__(self, iterable=None): """ 构造函数 :param iterable: 传递的列表 """ self.head = None self.tail = None if iterable: self.extend(iterable) def append(self, obj): """ 插入节点 :param obj:要插入的对象 :return: """ s = LinkList.Node(obj) if not self.head: self.head = s self.tail = s else: self.tail.next = s self.tail = s def extend(self, iterable): for obj in iterable: self.append(obj) def find(self, obj): for n in self: if n == obj: return True else: return False def __iter__(self): """ 若是一个类想被用于for ... in循环,相似list或tuple那样,就必须实现一个__iter__()方法 该方法返回一个迭代对象,而后,Python的for循环就会不断调用该迭代对象的__next__()方法拿到循环的下一个值, 直到遇到StopIteration错误时退出循环。 :return: """ return self.LinkListIterator(self.head) def __repr__(self): # 转换为字符串 return "<<" + ", ".join(map(str, self)) + ">>" # 哈希表作成相似集合的结构 class HashTable: def __init__(self, size=101): self.size = size self.T = [LinkList() for i in range(self.size)] # 未传值时,LinkList()是一个空链表 def h(self, k): # 哈希函数 return k % self.size def insert(self, k): # 插入函数 i = self.h(k) if self.find(k): print("Duplicated Insert") else: self.T[i].append(k) def find(self, k): # 查找函数 i = self.h(k) return self.T[i].find(k) ht = HashTable() ht.insert(0) ht.insert(1) ht.insert(0) # Duplicated Insert ht.insert(3) ht.insert(102) # 102%101=1 print(",".join(map(str, ht.T))) print(ht.find(102)) """ <<0>>,<<1, 102>>,<<>>,<<3>>,<<>>,<<>>,<<>>,<<>>,<<>>,<<>>,<<>>,<<>>,<<>>,<<>>,<<>>,<<>>,<<>>,<<>>,<<>>, <<>>,<<>>,<<>>,<<>>,<<>>,<<>>,<<>>,<<>>,<<>>,<<>>,<<>>,<<>>,<<>>,<<>>,<<>>,<<>>,<<>>,<<>>,<<>>,<<>>,<<>>, <<>>,<<>>,<<>>,<<>>,<<>>,<<>>,<<>>,<<>>,<<>>,<<>>,<<>>,<<>>,<<>>,<<>>,<<>>,<<>>,<<>>,<<>>,<<>>,<<>>,<<>>, <<>>,<<>>,<<>>,<<>>,<<>>,<<>>,<<>>,<<>>,<<>>,<<>>,<<>>,<<>>,<<>>,<<>>,<<>>,<<>>,<<>>,<<>>,<<>>,<<>>,<<>>, <<>>,<<>>,<<>>,<<>>,<<>>,<<>>,<<>>,<<>>,<<>>,<<>>,<<>>,<<>>,<<>>,<<>>,<<>>,<<>>,<<>>,<<>>,<<>> True """
字典与集合都是经过哈希表来实现的,e.g. a={'name':'Alex', 'age':18, 'gender':'Man'}
MD5(Message-Digest Algorithm 5)曾经是密码学中经常使用的哈希函数,能够把任意长度的数据映射为128位的哈希值。
1)一样的消息,其MD5值一定相同;
2)能够快速计算出任意给定消息的MD5值;
3)除非暴力枚举全部可能的消息,不然不可能从哈希值反推出消息自己;
4)两条消息之间即便只有微小的差异,其对应的MD5值也应该是彻底不一样、彻底不相关的;
5)不能在有意义的时间内人工地构造两个不一样的消息,使其具备相同的MD5值。
算出文件的哈希值,若两个文件的哈希值相同,则可认为这两个文件是相同的。所以:
1)用户能够利用它来验证下载的文件是否完整。
2)云存储服务商能够利用它来判断用户要上传的文件是否已经存在于服务器上,从而实现秒传的功能,同时避免存储过多相同的文件副本。
历史上MD5和SHA-1曾经是使用最为普遍的cryptographic hash function,可是随着密码学的发展,这两个哈希函数的安全性相继受到了各类挑战。
所以如今安全性较重要的场合推荐使用SHA-2等新的更安全的哈希函数。
SHA-2包含了一系列的哈希函数:SHA-224,SHA-256,SHA-384,SHA-512,SHA-512/224,SHA-512/256,其对应的哈希值长度分别为224,256,384or512位。
SHA-2具备与MD5相似的性质(参见MD5算法的特征)。
例如,在比特币系统中,全部参与者须要共同解决以下问题:对于一个给定的字符串U,给定的目标哈希值H,须要计算出一个字符串V,使得U+V的哈希值与H的差小于一个给定值D。此时,只能经过暴力枚举V来进行猜想。首先计算出结果的人可得到必定奖金。而某人首先计算成功的几率与其拥有的计算量成正比,因此其得到的奖金的指望值与其拥有的计算量成正比。