首页 > 编程语言 >基于Python3的数据结构与算法 - 17 哈希表

基于Python3的数据结构与算法 - 17 哈希表

时间:2024-03-21 15:29:17浏览次数:28  
标签:__ key 17 self 寻址 Python3 哈希 def

一、哈希表

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

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

1. 直接寻址法

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

U: 关键字可能出现的所有的集合。
T:根据U建立相关的T。

然后关键字在相应的T中找值。

直接寻址法的缺点:

  • 当域U很大的时候,需要消耗大量的内存,很不实际。
  • 如果域U很大而实际出现的key很少,则大量空间被浪费。
  • 无法处理关键字不是数字的情况。

2. 哈希

直接寻址表 + 哈希函数 = 哈希

直接寻址表:key为k的元素放在k位置上

改进直接寻址表:哈希(Hashing)

  • 构建大小为m的寻址表T
  • key为k的元素放在h(k)位置上
  • h (k) 是一个函数,其将域U映射到表T[0, 1,....,m-1]

3. 哈希表

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

示例:

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

3.1 哈希冲突

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

例如:h (k) = k % 7, h (0) = h(7) = h(14)

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

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

  • 线性探查:如果位置i被占用,则探查i+1, i+2 ......(查找也是按照线性查找)
  • 二次探查:如果位置i被占用,则探查i+{_{1}}^{2},i - {_{1}}^{2}, i+{_{2}}^{2}, i-{_{2}}^{2}......
  • 二度哈希:有n个哈希函数,当时也第1个哈希函数h1发生冲突时,则尝试使用h2,h3。

3.3 解决哈希冲突 -- 拉链法(重点)

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

查找的复杂度不好计算(可能不是平均分配)

4. 常见的哈希函数

  • 除法哈希法:h (k) = k % m
  • 乘法哈希法:h (k) = floor(m * (A * key % 1))   --- floor表示向下取整,m为哈希表大小,A是一个介于0~1之间的常数。
  • 全域哈希法:ha,b(k) = ((a*key + b) % p) %  m  a, b = 1,2,......p-1

5. 自定义哈希表

# 创建一个可以迭代的链表
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:
                cur_node = self.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)

    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

    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):  # 迭代器
        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)]  # 直接寻址法中的T

    def h(self, k):  # 哈希表中的哈希函数
        return k % self.size

    def insert(self, k):  # 插入函数
        i = self.h(k)
        if self.find(k):
            print("Duplicacted Insert")
        else:
            self.T[i].append(k)

    def find(self, k):
        i = self.h(k)
        return self.T[i].find(k)


# 添加迭代器后,创建的链表可以被迭代
lk = LinkList([1, 2, 3, 4, 5])
for element in lk:
    print(element)

print(lk)

# numbers = [1, 2, 3, 4, 5]
# str_numbers = map(str, numbers)
# print(str_numbers)
# print(list(str_numbers))

ht = HashTable()

ht.insert(0)
ht.insert(1)
ht.insert(2)
ht.insert(4)
ht.insert(507)

print(",".join(map(str, ht.T)))
print(ht.find(3))

6. 哈希表的应用

6.1集合和字典

字典和集合都是通过哈希表来实现的。

  • a = {'name':'Alex', 'age':18, 'gender':'Man'}
  • 使用哈希表存储字典,通过哈希函数将字典的键映射为下标。假如h('name') = 3, h('age') = 1 , h('gender') = 4, 则哈希表存储为[None, 18, None, 'Alex', 'Man'] 
  • 如果发生哈希冲突,则通过拉链发或开发寻址法解决。
  • 相同的查找元素,集合和字典肯定比列表要快(因为是按照哈希表的查找)

6.2 md5算法

MD5(Message-Digest Algorithm 5)曾经是密码学中常用的哈希函数,可以把任意长度的数据映射为128的哈希值,其曾经包括如下特征:

  1. 同样的消息,其MD5值必定相同;
  2. 可以快速计算出任意给定消息的MD5值;
  3. 除非暴力的美剧所有可能的消息,否则不可能从哈希值反推消息本身;
  4. 两条消息之间即使只要微小的差别,其对应的MD5值也应该完全不同、完全不相关;
  5. 不能在有意义的时间内人工的构造两个不同的消息, 使其具有相同的MD5值。

标签:__,key,17,self,寻址,Python3,哈希,def
From: https://blog.csdn.net/weixin_47702917/article/details/136696241

相关文章

  • kernel BUG at arch/x86/kernel/apic/vector.c:174!
    问题兆芯设备适配ngrayos系统(debian系统4.20.1内核)时,在网口up时系统崩溃。版本如下:现象:经过排查,原因是因为兆芯设备启动参数加了noapic(不加系统无法正常刻录和启动),网口up时中断向量不够分配。APIC(AdvancedProgrammableInterruptController)是一种硬件设备,用于处......
  • 洛谷-P2178 学习笔记
    题面[NOI2015]品酒大会题目描述一年一度的“幻影阁夏日品酒大会”隆重开幕了。大会包含品尝和趣味挑战两个环节,分别向优胜者颁发“首席品酒家”和“首席猎手”两个奖项,吸引了众多品酒师参加。在大会的晚餐上,调酒师Rainbow调制了\(n\)杯鸡尾酒。这\(n\)杯鸡尾酒排成一......
  • 机试真题重点题目-2017
    A:连续字母#include<iostream>#include<string.h>#include<algorithm>usingnamespacestd;constintN=110;intn;charstr[N];intmain(){cin>>n;while(n--){scanf("%s",str+1);......
  • 洛谷题单指南-集合-P5266 【深基17.例6】学籍管理
    原题链接:https://www.luogu.com.cn/problem/P5266题意解读:本题考察map的应用。解题思路:直接使用map即可解题。100分代码:#include<bits/stdc++.h>usingnamespacestd;map<string,int>h;stringname;intn,op,score;intmain(){cin>>n;while(n--)......
  • 洛谷题单指南-集合-P5250 【深基17.例5】木材仓库
    原题链接:https://www.luogu.com.cn/problem/P5250题意解读:根据题目要求,需要一种数据结构,支持去重、排序、logN的查找,set是最合适的。解题思路:先回顾一下set的关键操作:设set<int>s;1、添加:s.insert(x)2、查询个数:s.count(x)3、查找第一个>=x的元素,返回迭代器:set<int>::iter......
  • CF817F MEX Queries 题解
    题目链接:CF或者洛谷不是很难的题,但在这里提供一个动态开点线段树怎么卡空间卡过去的极致空间处理技巧全局\(mex\)问题,常见的做法就是维护权值树,然后找第一个没有权值的点,可以维护\(\min\),但本题存在第三个操作,所以不能再去传统地维护\(\min或者\max\)去辅助二分了。观......
  • [ARC174B] Bought Review 题解
    【题目描述】你开了一家店,有\(A_i\)个\(i\)星级评论,你可以花费\(P_i\)元买到一个\(i\)星评论,问使得这家店评论的星星平均值不小于\(3\),最少要花多少钱。\(1\lei\le5\)。【思路】首先读入,判断平均值是否小于\(3\),如果大于等于,直接输出\(0\)​然后根据\(3\t......
  • spring使用jdk17运行出现编码问题
    遇到一个比较奇怪的问题。这个问题别人也遇到过。https://blog.csdn.net/gao_chuan_g/article/details/115117712一、情况简介使用jdk17+springboot3.x+spring6.x写一个小应用A,其中有一部分代码是用于生成SM2加密后的字符串,这个字符串会再做一些处理,最终会显示在前端的页面。......
  • CF765F,CF1793F,JSOI2009:区间最接近的两数
    link:https://codeforces.com/contest/765/problem/F据说是典中典问题(出现三次了)题意:给一个序列\(a_1,\dots,a_n\),有\(m\)次询问,每次询问给\(l,r(1\leql<r\leqn)\)问\(\min_{l\leqs<t\leqr}|a_s-a_t|\)\(1\leqn,m\leq10^5,a_i\leq10^9\).思路这个做法还是很妙,想......
  • 面试题 17.12. BiNodec
    /***Definitionforabinarytreenode.*structTreeNode{*intval;*structTreeNode*left;*structTreeNode*right;*};*/structTreeNode*convertBiNode(structTreeNode*root){if(!root)returnNULL;if(!root->left......