哈希表一个通过哈希函数来计算数据存储位置的数据结构,通常支持如下操作:

  1. insert(key,value): 插入键值对(key,value)
  2. get(key):如果存在键为key的键值对则返回其value,否则返回空值
  3. delete(key): 删除键为key的键值对

当关键字的全域U比较小时,直接寻址是一种简单而有效的方法

直接寻址表

python中哈希位置冲突 python中哈希表用法_哈希冲突

U代表是key

key可能出现在这个所有的集合里面,key存的是身份证号,所有身份存在一个集合里面,这个集合就是u,考虑哪些存在里面

T就是列表

#直接寻址技术缺点:

  1. T的大小是根据U的,所有键出现的组合,可能我的键是个无穷的 --当域U很大时,需要消耗大量内存,很不实际
  2. 可能U从0到100万都出现,可能字典长度都是10,开个100万的内存,只有10个值,其他都是空,浪费内存–如果域U很大而实际出现的key很少,则大量空间被浪费
  3. 无法处理关键字不是数字的情况 --key不是数字而是字符串,有问题

直接寻址表加一个哈希,就是哈希

  • 直接寻址表: key为k的元素放到k位置上
  • 改进直接寻址表: 哈希(Hshing)
  1. 构建大小为m的寻址表T
  2. key为k的元素放到h(k)位置上
  3. h(k)是一个函数,其将域U映射到表T[0,1,…,m-1]

哈希表
哈希表(Hash Table 又称为散列表),是一种线性表的存储结构。哈希表由一个直接寻址表和一个哈希函数组成。哈希函数h(k)将元素关键字k作为自变量,返回元素的存储下标

#取余数
假设有一个长度为7的哈希表,哈希函数h(k)=k%7.元素集合{14,22,3,5}的存储方式如下图

python中哈希位置冲突 python中哈希表用法_链表_02

哈希冲突

由于哈希表的大小是有限的,而要存储的值的总数量的无限的,因此对于任何哈希函数,都会出现两个不同元素映射到同一个位置上的情况,这种情况叫做哈希冲突

比如h(k)=k%7,h(0)=h(7)=h(14)=…

解决 哈希冲突 -----开放寻址法

查找也是要线性查找

开放寻址法: 如果哈希函数返回的位置已经有值,则可以向后探查新的位置来存储个值

  1. 线性探查:如果位置i被占用,则探查I+1,i+2……
  2. 二次探查:如果位置i被占用,则探查i+12,i-12,i+22,i-22……
  3. 二度哈希:有n个哈希函数,当使用第1个哈希函数好h1发生冲突时,则尝试使用h2,h3……

解决哈希冲突 --拉链发

拉链发: 哈希表每个位置都连接一个链表,当冲突发生时,冲突的元素将被加到该位置链表的最后

查找155一看值是11,遍历一个11的列表

python中哈希位置冲突 python中哈希表用法_哈希冲突_03

除法哈希表: 取模

h(k) = k % m --m是长度

乘法哈希法:

h(k) = floor(m*(A*key%1)) --m是大小 A是一个值 对1取模

全域哈希法: #对m和p取模

python中哈希位置冲突 python中哈希表用法_直接寻址_04

#! /usr/bin/env python
# -*- coding: utf-8 -*-
# Date: 2018/3/17
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
                #更新一下啊node成下一个
                self.node = cur_node.next
                #把传进来输出下
                return cur_node.item
            else:
                raise StopIteration

        def __iter__(self):
            return self

    def __init__(self, iterable=None):
        #头结点
        self.head = None
        #尾节点
        self.tail = None
        #如果传个列表进来,循环插入尾部插入了
        if iterable:
            self.extend(iterable)

    #模仿python链表来写的    插入的对象obj
    def append(self, obj):
        #创建节点 列表
        s = LinkList.Node(obj)
        #刚开开始是空,头尾都是一个
        if not self.head:
            self.head = s
            self.tail = s
        else: #如果不是空   尾插法
            self.tail.next = s  #先和尾巴接起来
            self.tail = s  #更新尾巴

    #模仿python链表来写的
    def extend(self, iterable):
        for obj in iterable:
            self.append(obj)

    #查找 链表foreach查询   self就是对象LinkList
    def find(self, obj):
        for n in self:
            #找到了
            if n == obj:
                return True
        else:
            return False

    #让链表迭代,支持foreach循环   __iter__方法可以直接封装一个迭代器
    def __iter__(self):
        #迭代器类
        return self.LinkListIterator(self.head)

    #转换成字符串 打印的时候    self是可迭代的  map是把每个可迭代对象的每一个元素都转换成字符串
    def __repr__(self):
        return "<<"+", ".join(map(str, self))+">>"

# lk = LinkList([1,2,3,4,5])
# for element in lk:
#     print(element)

# 哈希表做一个类似于集合的结构
class HashTable:   #哈希表构造函数
    def __init__(self, size=101):
        self.size = size
        #空链表 head 和 tail 都是None     为什么是空链表不懂可以看下设计模式的组合
        self.T = [LinkList() for i in range(self.size)]

    #只支持k是整数的
    def h(self, k):
        return k % self.size

    #插入
    def insert(self, k):
        #i是k的值  k要插入哪个位置上
        i = self.h(k)
        #找到
        if self.find(k):
            print("Duplicated Insert.")
        else:
            self.T[i].append(k)
            print("insert into")

    def find(self, k):
        i = self.h(k)
        #T[i]是个链表.写了一个find函数  调用的是LinkList的find方法
        return self.T[i].find(k)


ht = HashTable()
ht.insert(0)
ht.insert(5)
ht.insert(6)
ht.insert(505)
ht.insert(607)
print(",".join(map(str, ht.T)))
# print(ht.find(203))

python中哈希位置冲突 python中哈希表用法_链表_05