哈希表

1. 哈希表的概念

  哈希表:也叫做散列表。根据关键字和值(Key-Value)直接进行访问的数据结构。它通过关键字 key 和一个映射函数 Hash(key) 计算出对应的值 value,然后把键值对映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做哈希函数(散列函数),用于存放记录的数组叫做 哈希表(散列表)。

 

2. 哈希函数

  将哈希表中元素的关键键值映射为元素存储位置的函数。如h(k) = k % 10,设置10个长度空间,模相同的放在同一个下标空间

  哈希函数的特征:

   1. 哈希函数应该易于计算,并且尽量使计算出来的索引值均匀分布,这能减少哈希冲突  

   2. 哈希函数计算得到的哈希值是一个固定长度的输出值

   3. 如果Hash(key1) 不等于 Hash(key2),那么 key1、key2 一定不相等

   4. 如果 Hash(key1) 等于 Hash(key2),那么 key1、key2 可能相等,也可能不相等(会发生哈希碰撞)

 

3. 哈希冲突

  1. 概念:不同的关键字通过同一个哈希函数可能得到同一哈希地址,即 key1 ≠ key2,而 Hash(key1) = Hash(key2),这种现象称为哈希冲突。

  2. 解决哈希冲突的方法

   1. 开发地址法:如果哈希函数返回的地方有值,则向后寻找新的空位置来存储该值

     1. 线性探查法:如果H(i) 被占用,则探查i+1,i+2.....

     2. 二次探查法:如果H(i) 被占用,则探查i+1²,i-1²,i+2²,i-2²......

     3. 二度哈希法:使用N个哈希函数,当使用第一个哈希函数发生冲突时,尝试使用其他的H2,H3等哈希函数

   2. 拉链法:将具有相同哈希地址的元素(或记录)存储在同一个线性链表中。

    

 

4. 实现哈希表的拉链插入法

# 链表
class LinkList:
    # 构建节点类
    class Node:
        def __init__(self, item=None):
            self.item = item
            self.next = None
    # 构建迭代器,因为容器类对象不可迭代
    class LinkListIterator:
        def __init__(self, node):
            self.node = node
        # 迭代器__iter__
        def __iter__(self):
            return self
        # 迭代器__next__
        def __next__(self):
            if self.node is not None:
                cur_node = self.node
                self.node = cur_node.next
                return cur_node.item
            else:
                raise StopIteration

    # 对象实例化
    def __init__(self, iterator=None):
        self.head = None
        self.tail = None
        # 判断调用类的时候如果参数是一个可迭代对象,则循环插入链表
        if iterator:
            self.extend(iterator)
    # 插入元素
    def append(self, val):
        node = LinkList.Node(val)
        # 判断链表是否为空,为空则head和tail都指向插入元素
        if not self.head:
            self.head = node
            self.tail = node
        # 不为空,则指针的next指向插入元素,并且更新指针尾插入元素
        else:
            self.tail.next = node
            self.tail = node
    # 插入可迭代列表函数 我自己不会想到这个,默认调用的时候不传参数
    def extend(self, iterator):
        for i in iterator:
            self.append(i)
    # 查找元素
    def find_LinkList(self, val):
        for n in self:
            if n == val:
                return True
            else:
                return False
    # 链对象可迭代化
    def __iter__(self, iterator=None):
        return self.LinkListIterator(self.head)
    # 输出字符串化,内置函数
    def __repr__(self):
        return "<"+",".join(map(str, self))+">"

# 类似于集合的结构
class HashTable:
    def __init__(self, size=100):
        self.size = size
        self.table = [LinkList() for i in range(self.size)]     # 建立空链表
    # 哈希函数,确定存储下标位置
    def hash_function(self, k):
        return k % self.size
    # 查找元素
    def find_HashTable(self, val):
        i = self.hash_function(val)         # i为哈希函数返回值,即指向哪一个链表
        return self.table[i].find_LinkList(val)      # 在指定i的链表中调用find_LinkList函数查找插入元素是否存在该链表中
    # 插入元素
    def insert(self, val):
        i = self.hash_function(val)     # 获取插入链表位置下标i
        if self.find_HashTable(val):    # 查找插入元素是否存在
            print("重复插入!")
        else:
            self.table[i].append(val)   # 不存在则调用append函数

 

5. 扩展小知识

  python中的集合和字典本质上就是哈希表,集合要求元素不能重复,字典要求键唯一不可变,是可以通过哈希函数计算出唯一地址的,键可以是数字、字符串和元组,但是元素内的元素要规定不能是列表,字典,集合等可变的对象。字典通过将键作为参数进行转换,得到唯一的地址,然后将值存放在该地址中,这是为什么当键相同的时候,值会被覆盖掉的原因

  Python 的哈希算法对相同的值计算得到的结果是一样的,也就是说 314 和 314.0 的值相同,他们被认为是相同的键(Key)

posted @ 2023-01-10 19:39  无敌小豆包  阅读(222)  评论(0编辑  收藏  举报