0
点赞
收藏
分享

微信扫一扫

【LeetCode 哈希表专项】设计哈希映射(706)

雷亚荣 2022-01-08 阅读 75

1. 题目

不使用任何内建的哈希表库设计一个哈希映射(HashMap)。

实现 MyHashMap 类:

  • MyHashMap() 用空映射初始化对象;
  • void put(int key, int value)HashMap 插入一个键值对 (key, value) 。如果 key 已经存在于映射中,则更新其对应的值 value
  • int get(int key) 返回特定的 key 所映射的 value ;如果映射中不包含 key 的映射,返回 − 1 -1 1
    void remove(key) 如果映射中存在 key 的映射,则移除 key 和它所对应的 value

1.1 示例

输入:
["MyHashMap", "put", "put", "get", "get", "put", "get", "remove", "get"]
[[], [1, 1], [2, 2], [1], [3], [2, 1], [2], [2], [2]]
输出:
[null, null, null, 1, -1, null, 1, null, -1]

解释:
MyHashMap myHashMap = new MyHashMap();
myHashMap.put(1, 1); // myHashMap 现在为 [[1,1]]
myHashMap.put(2, 2); // myHashMap 现在为 [[1,1], [2,2]]
myHashMap.get(1);    // 返回 1 ,myHashMap 现在为 [[1,1], [2,2]]
myHashMap.get(3);    // 返回 -1(未找到),myHashMap 现在为 [[1,1], [2,2]]
myHashMap.put(2, 1); // myHashMap 现在为 [[1,1], [2,1]](更新已有的值)
myHashMap.get(2);    // 返回 1 ,myHashMap 现在为 [[1,1], [2,1]]
myHashMap.remove(2); // 删除键为 2 的数据,myHashMap 现在为 [[1,1]]
myHashMap.get(2);    // 返回 -1(未找到),myHashMap 现在为 [[1,1]]

1.2 说明

  • 来源: 力扣(LeetCode)
  • 链接: https://leetcode-cn.com/problems/design-hashmap

1.3 限制

  • 0 <= key, value <= 106
  • 最多调用 1 0 4 10^4 104putgetremove 方法

2. 解法一(分离链接法)

2.1 分析

详细分析请参考:【数据结构Python描述】仿照Python解释器使用哈希表手动实现一个字典

下面首先使用列表作为每个 bucket 发生哈希碰撞时所使用的二级容器。

2.2 解答

from random import randrange


class MyHashMap:

    class _Item:
        __slots__ = 'key', 'value'

        def __init__(self, key, value):
            self.key = key
            self.value = value

    def __init__(self, cap=6500, p=109345121):
        """创建一个空的映射"""
        self._table = cap * [[]]
        self._n = 0
        self._prime = p  # MAD压缩函数中大于哈希表容量的大质数
        self._scale = 1 + randrange(p-1)  # MAD压缩函数中的缩放系数a
        self._shift = randrange(p)  # MAD压缩函数中的偏移系数b

    def _hash_function(self, key):
        """哈希函数"""
        return (self._scale * hash(key) + self._shift) % self._prime % len(self._table)

    def __len__(self):
        return self._n

    def get(self, key):
        j = self._hash_function(key)
        bucket = self._table[j]
        for item in bucket:
            if item.key == key:
                return item.value
        return -1

    def put(self, key, value):
        j = self._hash_function(key)
        size = len(self._table[j])
        for item in self._table[j]:  # 遍历查询映射中是否存在键key
            if key == item.key:
                item.value = value
                return
        self._table[j].append(self._Item(key, value))  # 映射中不存在键key
        if len(self._table[j]) > size:  # k为新的键
            self._n += 1
        if self._n > len(self._table) // 2:  # 确保负载系数小于0.5
            self._resize(2 * len(self._table) - 1)  # 通常2 * n - 1为质数

    def remove(self, key):
        j = self._hash_function(key)
        bucket = self._table[j]
        for item in bucket:  # 遍历查询映射中是否存在键key
            if key == item.key:
                bucket.remove(item)
                self._n -= 1
                return

    def _resize(self, cap):
        """将哈希表容量调整为cap"""
        old = list(self)  # 通过迭代获得已有的所有键值对
        self._table = cap * [[]]
        self._n = 0
        for key, value in old:
            self.put(key, value)

    def __iter__(self):
        for bucket in self._table:
            if bucket is not None:
                for item in bucket:
                    yield item.key, item.value

下面使用链表作为每个 bucket 发生哈希碰撞时所使用的二级容器。

from random import randrange


class _ListNode:
    def __init__(self, key=0, value=0, next=None):
        self.key = key
        self.value = value
        self.next = next


class _LinkedList:
    def __init__(self):
        self.head = None
        self.tail = None
        self.n = 0

    def append(self, key: int, value: int):
        node = _ListNode(key, value)
        if self.head is None:
            self.head = node
            self.tail = node
        else:
            self.tail.next = node
            self.tail = self.tail.next
        self.n += 1

    def remove(self, key: int):
        dummy = _ListNode()
        dummy.next = self.head
        predecessor, cursor = dummy, dummy.next
        while cursor:
            if cursor.key == key:
                predecessor.next = cursor.next
                self.n -= 1
                self.head = dummy.next
                return
            predecessor, cursor = cursor, cursor.next
        raise KeyError('Key does not exist!')

    def __len__(self):
        return self.n


class MyHashMap:

    def __init__(self, cap=11, p=109345121):
        """创建一个空的映射"""
        self._table = [_LinkedList() for _ in range(cap)]
        self._n = 0
        self._prime = p  # MAD压缩函数中大于哈希表容量的大质数
        self._scale = 1 + randrange(p - 1)  # MAD压缩函数中的缩放系数a
        self._shift = randrange(p)  # MAD压缩函数中的偏移系数b

    def _hash_function(self, key):
        """哈希函数"""
        return (self._scale * hash(key) + self._shift) % self._prime % len(self._table)

    def __len__(self):
        return self._n

    def get(self, key):
        j = self._hash_function(key)
        bucket = self._table[j]
        cursor = bucket.head
        while cursor:
            if cursor.key == key:
                return cursor.value
            cursor = cursor.next
        return -1

    def put(self, key, value):
        j = self._hash_function(key)
        size = len(self._table[j])
        bucket = self._table[j]
        cursor = bucket.head
        while cursor:
            if cursor.key == key:
                cursor.value = value
                return
            cursor = cursor.next
        self._table[j].append(key, value)  # 映射中不存在键key
        if len(self._table[j]) > size:  # k为新的键
            self._n += 1
        if self._n > len(self._table) // 2:  # 确保负载系数小于0.5
            self._resize(2 * len(self._table) - 1)  # 通常2 * n - 1为质数

    def remove(self, key):
        j = self._hash_function(key)
        bucket = self._table[j]
        try:
            bucket.remove(key)
            self._n -= 1
            return
        except KeyError:
            return

    def _resize(self, cap):
        """将哈希表容量调整为cap"""
        old = list(self)  # 通过迭代获得已有的所有键值对
        self._table = [_LinkedList() for _ in range(cap)]
        self._n = 0
        for key, value in old:
            self.put(key, value)

    def __iter__(self):
        for bucket in self._table:
            cursor = bucket.head
            while cursor:
                yield cursor.key, cursor.value
                cursor = cursor.next


def main():
    hash_map = MyHashMap()
    # hash_map.put(1, 1)
    # hash_map.put(2, 2)
    # print(hash_map.get(1))
    # print(hash_map.get(3))
    # hash_map.put(2, 1)
    # print(hash_map.get(2))
    # hash_map.remove(2)
    # print(hash_map.get(2))

    operations = ["MyHashMap", "put", "put", "put", "put", "remove", "put", "put", "put", "put", "put", "put", "get",
                  "put", "get", "put", "remove", "get", "put", "put", "put", "put", "get", "remove", "get", "remove",
                  "get", "put", "put", "put", "put", "put", "put", "remove", "remove", "put", "put", "remove", "get",
                  "put", "put", "put", "remove", "put", "get", "put", "get", "put", "remove", "put", "put", "get",
                  "remove", "put", "put", "remove", "put", "put", "put", "put", "put", "remove", "put", "remove", "put",
                  "put", "put", "put", "remove", "put", "get", "remove", "put", "put", "put", "put", "put", "put",
                  "put", "put", "remove", "remove", "put", "put", "put", "get", "put", "put", "put", "remove", "remove",
                  "put", "put", "remove", "remove", "put", "put", "put", "remove", "remove", "put", "put", "put", "put",
                  "put", "put", "remove", "get", "put", "get", "get", "put", "put", "remove", "put", "get", "put",
                  "get", "put", "put", "put", "put", "put", "remove", "put", "remove", "put", "put", "put", "get",
                  "put", "put", "put", "put", "put", "put", "put", "get", "put", "put", "put", "put", "put", "remove",
                  "remove", "remove", "put", "put", "put", "put", "put", "get", "put", "put", "put", "get", "get",
                  "remove", "put", "put", "put", "put", "remove", "get", "get", "put", "put", "get", "remove", "get",
                  "put", "put", "put", "remove", "put", "get", "put", "get", "put", "put", "put", "remove", "put",
                  "remove", "put", "put", "put", "put", "remove", "put", "put", "put", "put", "remove", "put", "put",
                  "put", "put", "put", "put", "put", "remove", "remove", "remove", "put", "put", "put", "get", "put",
                  "put", "put", "put", "remove", "get", "put", "get", "put", "put", "remove", "put", "put", "put",
                  "put", "put", "put", "put", "remove", "remove", "remove", "put", "remove", "get", "put", "get", "put",
                  "get", "put", "put", "put", "put", "put", "get", "remove", "put", "put", "put", "put", "remove",
                  "put", "put", "get", "put", "put", "put", "remove", "put", "put", "put", "put", "remove", "put",
                  "remove", "remove", "put", "put", "put", "remove", "put", "put", "put", "put", "put", "get", "put",
                  "get", "put", "get", "get", "put", "put", "remove", "put", "remove", "put", "put", "put", "put",
                  "remove", "get", "get", "remove", "put", "put", "put", "remove", "get", "get", "put", "put", "put",
                  "put", "put", "remove", "get", "put", "put", "put", "get", "put", "put", "remove", "put", "put",
                  "put", "get", "get", "put", "put", "put", "remove", "put", "get", "put", "put", "remove", "put",
                  "remove", "remove", "put", "put", "put", "put", "get", "put", "put", "put", "remove", "remove", "put",
                  "get", "put", "put", "put", "put", "put", "remove", "put", "put", "put", "get", "remove", "put",
                  "remove", "put", "put", "put", "remove", "put", "put", "put", "remove", "put", "remove", "put", "get",
                  "put", "put", "remove", "put", "put", "put", "put", "put", "put", "put", "put", "put", "put", "put",
                  "put", "put", "get", "put", "get", "get", "put", "get", "remove", "remove", "put", "put", "put",
                  "put", "put", "put", "put", "remove", "get", "put", "put", "put", "put", "put", "put", "put",
                  "remove", "put", "put", "put", "get", "get", "get", "put", "get", "put", "put", "get", "put", "put",
                  "put", "remove", "put", "put", "put", "put", "put", "remove", "put", "put", "put", "put", "get",
                  "put", "put", "put", "put", "remove", "put", "put", "remove", "put", "put", "remove", "get", "put",
                  "put", "get", "get", "get", "put", "put", "put", "get", "remove", "put", "get", "remove", "put",
                  "get", "put", "get", "get", "put", "remove", "put", "put", "put", "put", "remove", "put", "put",
                  "put", "put", "get", "put", "put", "get", "get", "put", "put", "get", "get", "put", "put", "put",
                  "get", "put", "put", "remove", "remove", "put", "put", "put", "remove", "remove", "put", "put", "put",
                  "put", "put", "put", "put", "put", "put", "put", "put", "put", "get", "put", "put", "put", "get",
                  "put", "put", "remove", "put", "put", "put", "put", "get", "get", "put", "put", "remove", "put",
                  "put", "put", "put", "put", "get", "put", "get", "get", "put", "put", "put", "put", "put", "put",
                  "get", "put", "remove", "put", "put", "put", "put", "put", "put", "put", "put", "put", "put", "put",
                  "remove", "put", "get", "put", "put", "get", "put", "get", "put", "put", "put", "put", "get",
                  "remove", "put", "remove", "get", "remove", "put", "put", "put", "put", "put", "get", "put", "put",
                  "put", "put", "get", "put", "put", "put", "remove", "put", "remove", "put", "put", "put", "put",
                  "get", "remove", "remove", "put", "put", "put", "put", "put", "put", "put", "put", "put", "remove",
                  "put", "put", "put", "put", "get", "put", "put", "remove", "put", "remove", "get", "put", "put",
                  "put", "get", "get", "remove", "put", "put", "remove", "put", "put", "put", "put", "put", "get",
                  "put", "put", "remove", "get", "get", "put", "remove", "put", "put", "get", "get", "put", "get",
                  "put", "remove", "put", "put", "put", "remove", "put", "put", "put", "put", "put", "put", "put",
                  "put", "put", "put", "put", "put", "remove", "put", "get", "put", "put", "put", "get", "get",
                  "remove", "put", "put", "remove", "get", "put", "get", "remove", "put", "put", "put", "put", "put",
                  "put", "put", "put", "put", "remove", "put", "put", "remove", "get", "get", "remove", "put", "remove",
                  "put", "put", "get", "put", "put", "put", "put", "get", "put", "get", "put", "remove", "put", "put",
                  "put", "put", "put", "put", "get", "get", "put", "put", "put", "put", "put", "get", "get", "put",
                  "remove", "remove", "put", "remove", "get", "get", "put", "put", "put", "remove", "remove", "get",
                  "get", "remove", "put", "put", "put", "get", "put", "put", "put", "put", "put", "put", "get",
                  "remove", "put", "put", "put", "put", "get", "put", "remove", "put", "get", "get", "get", "put",
                  "put", "put", "put", "put", "get", "get", "put", "put", "put", "put", "put", "put", "put", "put",
                  "put", "put", "put", "get", "put", "put", "get", "get", "put", "get", "put", "put", "put", "put",
                  "put", "put", "get", "put", "get", "put", "put", "put", "remove", "get", "get", "put", "put",
                  "remove", "put", "put", "put", "get", "put", "put", "get", "put", "put", "get", "put", "put", "put",
                  "remove", "put", "put", "remove", "put", "put", "remove", "put", "put", "put", "put", "get", "get",
                  "get", "put", "put", "put", "put", "get", "remove", "put", "put", "remove", "get", "get", "put",
                  "put", "get", "put", "get", "get", "put", "put", "put", "put", "get", "remove", "put", "put", "put",
                  "put", "get", "put", "put", "remove", "put", "put", "put", "put", "put", "put", "put", "get", "put",
                  "remove", "put", "put", "put", "put", "get", "put", "put", "put", "get", "put", "put", "put", "get",
                  "put", "put", "put", "put", "put", "get", "put", "put", "remove", "put", "put", "get", "put", "put",
                  "put", "put", "get", "put", "put", "get", "put", "get", "put", "put", "put", "put", "remove", "put",
                  "put", "put", "put", "put", "remove", "remove", "put", "put", "remove", "put", "put", "put", "put",
                  "put", "get", "remove", "get", "put", "get", "remove", "put", "get", "remove", "get", "put", "put",
                  "put", "put", "put", "put", "put", "put", "remove", "remove", "get", "put", "put", "remove", "put",
                  "put", "put", "put", "put", "put", "put", "get", "put", "put", "put", "put", "put", "put", "put",
                  "remove", "get", "remove", "remove", "put", "put", "get", "put", "put", "put", "put", "put", "remove",
                  "put", "put", "remove", "put", "put", "put", "put", "put", "put", "remove", "remove", "remove", "put",
                  "put", "put", "get", "put", "put", "put", "remove", "put", "put"]
    parameters = [[], [970, 538], [908, 29], [395, 865], [250, 847], [836], [233, 568], [657, 790], [595, 271],
                  [769, 219], [55, 112], [157, 493], [920], [632, 358], [669], [506, 228], [904], [473], [461, 40],
                  [748, 973], [446, 544], [766, 461], [395], [211], [415], [157], [252], [252, 22], [942, 681],
                  [600, 988], [424, 39], [685, 482], [561, 605], [632], [461], [916, 329], [739, 735], [769], [942],
                  [460, 226], [183, 411], [224, 524], [769], [508, 614], [632], [254, 825], [603], [115, 667], [460],
                  [940, 813], [50, 629], [519], [595], [39, 913], [742, 13], [765], [163, 627], [554, 130], [255, 945],
                  [22, 78], [912, 390], [632], [609, 410], [956], [515, 243], [975, 871], [520, 313], [682, 538], [563],
                  [119, 902], [916], [766], [293, 885], [657, 665], [518, 832], [897, 489], [340, 972], [790, 24],
                  [637, 445], [544, 498], [115], [600], [269, 209], [734, 3], [243, 108], [233], [679, 632], [640, 55],
                  [288, 301], [682], [871], [922, 755], [624, 787], [776], [293], [564, 902], [32, 743], [934, 278],
                  [250], [389], [620, 711], [420, 623], [346, 959], [829, 832], [776, 894], [465, 769], [508], [358],
                  [126, 481], [255], [50], [477, 991], [973, 337], [32], [823, 283], [21], [733, 431], [583],
                  [735, 407], [873, 702], [578, 256], [813, 221], [669, 432], [790], [941, 945], [645], [560, 775],
                  [823, 772], [458, 220], [243], [947, 136], [168, 560], [946, 19], [172, 608], [624, 260], [876, 516],
                  [76, 334], [704], [615, 737], [110, 453], [189, 678], [746, 201], [497, 330], [632], [993], [497],
                  [915, 545], [329, 558], [662, 773], [208, 135], [797, 614], [640], [108, 809], [262, 401], [560, 965],
                  [494], [222], [922], [237, 439], [688, 796], [974, 331], [367, 470], [339], [115], [790], [658, 873],
                  [513, 520], [2], [76], [110], [199, 511], [674, 853], [546, 849], [430], [394, 41], [189], [443, 983],
                  [270], [353, 44], [959, 995], [275, 798], [910], [522, 939], [176], [520, 292], [631, 538],
                  [216, 551], [197, 623], [55], [452, 416], [805, 462], [884, 223], [484, 454], [126], [59, 954],
                  [454, 575], [500, 940], [999, 520], [211, 106], [190, 915], [995, 462], [519], [368], [734],
                  [549, 845], [47, 885], [609, 356], [765], [163, 520], [382, 774], [190, 545], [146, 166], [748],
                  [282], [357, 531], [113], [89, 489], [994, 5], [967], [972, 826], [220, 91], [296, 808], [826, 7],
                  [401, 910], [126, 802], [534, 781], [293], [282], [515], [29, 473], [390], [32], [265, 801], [658],
                  [29, 648], [678], [40, 493], [9, 809], [701, 244], [345, 542], [386, 296], [632], [89], [575, 758],
                  [168, 575], [286, 619], [210, 335], [546], [77, 534], [380, 885], [414], [590, 272], [924, 933],
                  [662, 1], [544], [349, 402], [244, 274], [907, 303], [638, 453], [339], [255, 802], [973], [130],
                  [266, 695], [486, 444], [557, 522], [518], [392, 750], [538, 174], [407, 337], [455, 912], [755, 278],
                  [242], [947, 647], [367], [657, 280], [286], [255], [307, 453], [382, 216], [609], [69, 582], [340],
                  [36, 792], [4, 288], [518, 878], [376, 516], [873], [211], [417], [908], [695, 90], [646, 513],
                  [114, 675], [924], [321], [486], [300, 87], [68, 223], [924, 665], [680, 323], [410, 97], [538],
                  [685], [884, 617], [496, 967], [155, 853], [367], [694, 765], [607, 567], [748], [565, 855],
                  [151, 633], [300, 838], [76], [163], [796, 591], [516, 514], [95, 921], [353], [445, 40], [32],
                  [163, 846], [697, 892], [637], [870, 672], [560], [410], [909, 414], [319, 973], [259, 144],
                  [423, 142], [165], [984, 652], [121, 133], [117, 13], [340], [390], [237, 305], [520], [712, 752],
                  [380, 7], [661, 937], [249, 9], [937, 291], [168], [501, 89], [989, 540], [736, 722], [739], [250],
                  [741, 270], [920], [421, 848], [917, 716], [788, 294], [520], [642, 352], [250, 775], [710, 268],
                  [349], [801, 427], [921], [905, 183], [69], [163, 429], [274, 863], [108], [410, 289], [578, 258],
                  [613, 113], [992, 527], [155, 194], [557, 696], [190, 519], [630, 59], [234, 795], [740, 324],
                  [270, 492], [107, 63], [705, 446], [205], [390, 676], [374], [557], [321, 754], [710], [121], [488],
                  [156, 679], [132, 238], [793, 469], [966, 912], [612, 691], [860, 749], [245, 736], [829], [237],
                  [858, 449], [655, 591], [913, 760], [977, 737], [13, 954], [189, 252], [215, 933], [168], [682, 867],
                  [802, 747], [710, 342], [994], [257], [288], [972, 139], [255], [637, 821], [383, 858], [685],
                  [146, 53], [464, 179], [98, 642], [911], [524, 259], [196, 641], [644, 843], [271, 723], [706, 285],
                  [205], [256, 884], [291, 722], [750, 142], [205, 50], [259], [816, 839], [123, 741], [913, 349],
                  [894, 577], [914], [129, 267], [242, 60], [288], [189, 30], [507, 805], [277], [227], [332, 850],
                  [716, 309], [493], [401], [669], [682, 330], [880, 925], [4, 287], [858], [177], [351, 217], [621],
                  [193], [611, 128], [183], [789, 817], [655], [658], [501, 965], [412], [334, 651], [345, 411],
                  [73, 9], [162, 791], [644], [330, 608], [998, 312], [96, 234], [906, 419], [221], [179, 324],
                  [507, 978], [632], [134], [155, 691], [92, 442], [353], [861], [714, 403], [824, 261], [814, 164],
                  [118], [931, 816], [39, 844], [790], [894], [980, 797], [673, 80], [109, 321], [293], [256],
                  [625, 277], [272, 650], [890, 666], [282, 399], [50, 95], [635, 169], [25, 749], [681, 759],
                  [126, 84], [229, 309], [892, 941], [505, 343], [496], [843, 820], [259, 362], [549, 86], [340],
                  [602, 374], [711, 73], [196], [519, 269], [785, 465], [747, 706], [449, 878], [407], [357],
                  [459, 357], [615, 903], [191], [86, 672], [467, 638], [406, 351], [665, 29], [416, 514], [739],
                  [563, 810], [190], [402], [705, 575], [91, 324], [993, 183], [902, 528], [136, 560], [866, 309],
                  [458], [13, 751], [999], [201, 651], [666, 246], [969, 696], [249, 255], [632, 413], [109, 432],
                  [866, 745], [203, 476], [72, 869], [379, 178], [955, 758], [367], [864, 228], [13], [101, 120],
                  [679, 497], [814], [216, 235], [401], [588, 717], [914, 836], [648, 29], [253, 926], [316], [69],
                  [978, 205], [963], [701], [941], [758, 489], [506, 806], [597, 467], [618, 861], [863, 454], [190],
                  [490, 703], [866, 151], [735, 303], [217, 552], [460], [850, 691], [554, 450], [1, 997], [424],
                  [603, 68], [319], [883, 877], [489, 109], [819, 400], [957, 400], [467], [601], [481], [832, 895],
                  [979, 52], [137, 90], [394, 540], [198, 303], [562, 223], [680, 851], [911, 399], [730, 427], [563],
                  [983, 9], [559, 525], [422, 86], [121, 720], [531], [151, 902], [468, 501], [39], [932, 29], [650],
                  [598], [131, 715], [752, 133], [119, 831], [789], [606], [941], [735, 652], [107, 448], [98],
                  [436, 737], [728, 947], [533, 835], [971, 356], [776, 984], [880], [939, 191], [119, 844], [168],
                  [733], [39], [739, 760], [940], [265, 768], [523, 176], [969], [269], [464, 948], [456], [183, 358],
                  [559], [211, 986], [696, 242], [427, 90], [785], [868, 968], [976, 307], [667, 619], [798, 831],
                  [297, 468], [802, 53], [221, 956], [319, 908], [791, 313], [56, 158], [494, 398], [387, 909], [298],
                  [180, 573], [839], [663, 316], [460, 628], [674, 243], [254], [922], [197], [814, 245], [560, 195],
                  [392], [788], [439, 556], [698], [746], [138, 331], [379, 700], [984, 296], [937, 471], [968, 888],
                  [220, 87], [706, 368], [430, 702], [466, 349], [441], [334, 642], [324, 670], [635], [406], [734],
                  [611], [954, 130], [666], [601, 106], [951, 372], [382], [64, 691], [481, 581], [91, 807], [113, 529],
                  [307], [129, 955], [383], [926, 412], [644], [86, 862], [879, 202], [661, 959], [849, 806],
                  [583, 501], [331, 825], [922], [391], [68, 504], [149, 731], [463, 64], [572, 623], [165, 760], [268],
                  [448], [819, 282], [586], [45], [353, 316], [589], [655], [249], [382, 367], [963, 184], [625, 538],
                  [932], [996], [935], [612], [788], [252, 397], [728, 935], [765, 924], [976], [784, 464], [806, 326],
                  [625, 866], [512, 83], [569, 768], [473, 714], [401], [683], [603, 271], [762, 849], [449, 150],
                  [242, 372], [285], [31, 474], [716], [653, 344], [673], [588], [813], [672, 111], [120, 299],
                  [353, 497], [297, 510], [619, 677], [390], [50], [185, 215], [809, 322], [749, 218], [868, 920],
                  [955, 207], [530, 64], [95, 906], [940, 961], [505, 893], [339, 428], [364, 122], [426], [646, 775],
                  [455, 542], [423], [807], [142, 73], [392], [174, 439], [575, 427], [321, 697], [9, 998], [32, 272],
                  [351, 496], [834], [481, 71], [760], [737, 574], [812, 451], [626, 196], [102], [648], [617],
                  [166, 152], [701, 720], [750], [625, 425], [335, 404], [820, 328], [240], [226, 815], [508, 265],
                  [785], [863, 881], [298, 263], [325], [12, 960], [439, 28], [502, 331], [114], [117, 930], [922, 65],
                  [456], [686, 211], [668, 478], [601], [839, 336], [737, 282], [756, 191], [581, 547], [89], [167],
                  [695], [272, 244], [449, 570], [688, 413], [142, 769], [382], [522], [794, 632], [714, 983], [163],
                  [737], [310], [771, 745], [254, 395], [551], [765, 622], [263], [751], [986, 924], [76, 585],
                  [548, 618], [589, 427], [785], [866], [541, 789], [58, 900], [816, 495], [324, 395], [626],
                  [648, 807], [280, 557], [92], [994, 295], [852, 148], [700, 192], [626, 551], [628, 937], [442, 892],
                  [268, 263], [536], [593, 528], [567], [164, 733], [997, 800], [583, 941], [967, 894], [737],
                  [825, 103], [528, 774], [485, 373], [611], [935, 118], [190, 262], [494, 836], [917], [997, 448],
                  [269, 659], [941, 894], [913, 554], [63, 246], [167], [235, 456], [136, 501], [309], [212, 503],
                  [796, 931], [337], [129, 759], [302, 276], [360, 779], [809, 918], [55], [888, 433], [535, 160], [97],
                  [769, 627], [762], [449, 596], [703, 27], [133, 407], [509, 67], [266], [697, 778], [903, 748],
                  [430, 909], [464, 626], [452, 493], [891], [293], [20, 945], [686, 88], [329], [207, 827], [202, 641],
                  [709, 186], [303, 451], [385, 541], [409], [201], [766], [395, 956], [971], [704], [250, 34], [354],
                  [766], [789], [106, 974], [652, 301], [654, 875], [109, 363], [481, 935], [361, 686], [989, 931],
                  [488, 673], [979], [806], [450], [509, 414], [528, 835], [673], [328, 121], [303, 406], [45, 174],
                  [410, 906], [42, 356], [675, 953], [882, 222], [978], [252, 396], [347, 94], [367, 754], [578, 278],
                  [107, 628], [73, 583], [166, 100], [162], [593], [791], [740], [697, 977], [368, 152], [971],
                  [918, 197], [152, 444], [995, 814], [869, 900], [335, 818], [250], [951, 948], [605, 293], [959],
                  [873, 243], [236, 239], [577, 636], [244, 441], [884, 904], [942, 993], [708], [581], [82],
                  [293, 859], [622, 486], [878, 920], [220], [493, 182], [445, 582], [392, 227], [523], [800, 270],
                  [404, 803]]

    for operation, parameter in zip(operations, parameters):
        if operation == "MyHashMap":
            continue
        elif operation == "put":
            hash_map.put(parameter[0], parameter[1])
        elif operation == "get":
            print(hash_map.get(parameter[0]))
        else:
            hash_map.remove(parameter[0])


if __name__ == '__main__':
    main()

3. 解法二(线性查找法)

3.1 分析

详细分析请参考:【数据结构Python描述】仿照Python解释器使用哈希表手动实现一个字典。

3.2 解答

from random import randrange


class MyHashMap:

    class _Item:
        __slots__ = 'key', 'value'

        def __init__(self, key, value):
            self.key = key
            self.value = value

    _AVAIL = object()  # 哨兵标识,用于标识被键值对被删除的哈希表单元

    def __init__(self, cap=11, p=109345121):
        """创建一个空的映射"""
        self._table = [None for _ in range(cap)]
        self._n = 0
        self._prime = p  # MAD压缩函数中大于哈希表容量的大质数
        self._scale = 1 + randrange(p - 1)  # MAD压缩函数中的缩放系数a
        self._shift = randrange(p)  # MAD压缩函数中的偏移系数b

    def _is_available(self, j):
        """当哈希表索引为j的单元处为空或键值对被删除,则返回True"""
        return self._table[j] is None or self._table[j] is MyHashMap._AVAIL

    def _find_slot(self, j, key):
        """查找索引为j的哈希表单元处是否有键k
        该方法的返回值为一个元组,且返回的情况如下:
        - 当在索引为j的哈希表单元处找到键k,则返回(True, fisrt_avail);
        - 当未在哈希表任何单元处找到键k,则返回(False, j)。
        """
        first_avail = None
        while True:
            if self._is_available(j):
                if first_avail is None:
                    first_avail = j
                if self._table[j] is None:
                    return False, first_avail
            elif key == self._table[j].key:
                return True, j
            j = (j + 1) % len(self._table)

    def _hash_function(self, key):
        """哈希函数"""
        return (self._scale * hash(key) + self._shift) % self._prime % len(self._table)

    def __len__(self):
        return self._n

    def get(self, key):
        j = self._hash_function(key)

        found, s = self._find_slot(j, key)
        if not found:
            return -1
        return self._table[s].value

    def put(self, key, value):
        j = self._hash_function(key)
        found, s = self._find_slot(j, key)
        if not found:
            self._table[s] = self._Item(key, value)
            self._n += 1
        else:
            self._table[s].value = value
        if self._n > len(self._table) // 2:  # 确保负载系数小于0.5
            self._resize(2 * len(self._table) - 1)  # 通常2 * n - 1为质数

    def remove(self, key):
        j = self._hash_function(key)
        found, s = self._find_slot(j, key)
        if not found:
            return
        self._table[s] = MyHashMap._AVAIL
        self._n -= 1

    def _resize(self, cap):
        """将哈希表容量调整为cap"""
        old = list(self)  # 通过迭代获得已有的所有键值对
        self._table = [None for _ in range(cap)]
        self._n = 0
        for key, value in old:
            self.put(key, value)

    def __iter__(self):
        for j in range(len(self._table)):
            bucket = self._table[j]
            if not self._is_available(j):
                yield bucket.key, bucket.value


def main():
    hash_map = MyHashMap()
    hash_map.remove(14)
    print(hash_map.get(4))  # -1
    hash_map.put(7, 3)
    hash_map.put(11, 1)
    hash_map.put(12, 1)
    print(hash_map.get(7))  # 3
    hash_map.put(1, 19)
    hash_map.put(0, 3)
    hash_map.put(1, 8)
    hash_map.put(2, 6)


if __name__ == '__main__':
    main()

举报

相关推荐

0 条评论