leetcode
leetcode 701 ~ 750
设计哈希映射

设计哈希映射

难度:

标签:

题目描述

代码结果

运行时间: 105 ms, 内存: 19.4 MB


/*
 * 思路:
 * 1. 使用一个ArrayList来存储键值对,每个键值对用一个数组表示。
 * 2. put方法检查是否已有该键,如果有则更新值,如果没有则添加新的键值对。
 * 3. get方法遍历列表查找键并返回对应值,如果没有找到则返回-1。
 * 4. remove方法遍历列表找到键并将其移除。
 * 5. 使用Stream API简化一些操作。
 */

import java.util.ArrayList;
import java.util.List;

public class MyHashMap {
    private List<int[]> map;

    /** Initialize your data structure here. */
    public MyHashMap() {
        map = new ArrayList<>();
    }

    /** value will always be non-negative. */
    public void put(int key, int value) {
        for (int[] pair : map) {
            if (pair[0] == key) {
                pair[1] = value;
                return;
            }
        }
        map.add(new int[]{key, value});
    }

    /** Returns the value to which the specified key is mapped, or -1 if this map contains no mapping for the key */
    public int get(int key) {
        return map.stream()
                .filter(pair -> pair[0] == key)
                .map(pair -> pair[1])
                .findFirst()
                .orElse(-1);
    }

    /** Removes the mapping of the specified value key if this map contains a mapping for the key */
    public void remove(int key) {
        map.removeIf(pair -> pair[0] == key);
    }
}

解释

方法:

该题解使用了一个非常直接的方法来实现哈希映射。它利用一个固定大小(1000001)的数组来存储键值对。数组的索引代表键(key),数组的值代表对应的值(value)。这种方法的优点在于其简单性和直接性,对于操作如插入、查找和删除,都可以直接通过数组索引来完成,从而实现快速的访问和修改。

时间复杂度:

O(1)

空间复杂度:

O(N)

代码细节讲解

🦆
为什么选择数组大小为1000001,这个数字有什么特别的意义或者考虑吗?
数组大小选择为1000001是一种预设的设计决策,主要基于实现简便性和避免冲突。1000001是一个大于一百万的素数,使用素数作为数组长度可以在某种程度上减少哈希冲突的可能性,尤其是在哈希函数设计得较为简单时。此外,选择一个稍大于常用范围的数可以确保覆盖大多数实际应用中的键值需求。
🦆
在这种使用固定大小数组的实现方法中,如何处理可能出现的键值冲突情况?
在当前实现中,由于直接使用键作为数组索引,理论上不会出现冲突——每个键直接映射到一个固定的数组位置。然而,在更一般的哈希表实现中,如使用模运算的哈希函数,键值冲突是常见的问题。常见的解决冲突的策略包括链地址法(使用链表处理冲突的索引)和开放寻址法(找到空闲的数组位置)。当前问题的实现方法实际上避免了冲突的可能性,前提是键值的范围必须严格控制在数组的索引范围之内。
🦆
数组初始化为-1表示空,那么如果要存储值为-1的键值对会不会有问题?
在这个设计中,使用-1来表示数组位置为空(即没有存储任何键值对的状态)。这种设计确实会带来一个问题:如果要存储值为-1的键值对,则无法区分这个位置是真的存储了值为-1的键值对,还是该位置为空。解决这个问题的一种方法是使用更复杂的数据结构来存储每个位置的状态,例如每个数组项可以是一个更丰富的对象或者结构体,包含值和一个表示该位置是否被占用的标志。

相关问题

设计哈希集合

不使用任何内建的哈希表库设计一个哈希集合(HashSet)。

实现 MyHashSet 类:

  • void add(key) 向哈希集合中插入值 key
  • bool contains(key) 返回哈希集合中是否存在这个值 key
  • void remove(key) 将给定值 key 从哈希集合中删除。如果哈希集合中没有这个值,什么也不做。
 

示例:

输入:
["MyHashSet", "add", "add", "contains", "contains", "add", "contains", "remove", "contains"]
[[], [1], [2], [1], [3], [2], [2], [2], [2]]
输出:
[null, null, null, true, false, null, true, null, false]

解释:
MyHashSet myHashSet = new MyHashSet();
myHashSet.add(1);      // set = [1]
myHashSet.add(2);      // set = [1, 2]
myHashSet.contains(1); // 返回 True
myHashSet.contains(3); // 返回 False ,(未找到)
myHashSet.add(2);      // set = [1, 2]
myHashSet.contains(2); // 返回 True
myHashSet.remove(2);   // set = [1]
myHashSet.contains(2); // 返回 False ,(已移除)

 

提示:

  • 0 <= key <= 106
  • 最多调用 104addremovecontains