leetcode 706 设计哈希映射

leetcode 706. 设计哈希映射

实现一个 hashmap

题目描述:

不使用任何内建的哈希表库设计一个哈希映射(HashMap)。
实现 MyHashMap 类:
MyHashMap() 用空映射初始化对象
void put(int key, int value) 向 HashMap 插入一个键值对 (key, value) 。如果 key 已经存在于映射中,则更新其对应的值 value 。
int get(int key) 返回特定的 key 所映射的 value ;如果映射中不包含 key 的映射,返回 -1 。
void remove(key) 如果映射中存在 key 的映射,则移除 key 和它所对应的 value 。


class MyHashMap {

using value_type = std::pair<int, int>;
using node_pair = std::pair<node*, node*>;
using hasher = std::hash<int>;

struct node {
    value_type value;
    node* next;
    node(value_type val = value_type(), node* next = nullptr) : value(val), next(next) {}
};

public:
    MyHashMap() {
        size_ = 0;
        hash_function_ = hasher();
        bucket_array_.resize(default_bucket_, nullptr);
    }
    
    void put(int key, int value) {
        auto [prev, node_to_insert] = find_node(key);
        int index = hash_function_(key) % bucket_count();
        if (node_to_insert != nullptr) {
            node_to_insert->value.second = value;
            return;
        }
        auto temp = new node(value_type(key,value), bucket_array_[index]);
        bucket_array_[index] = temp;
        ++size_;
    }
    
    int get(int key) {
        auto [prev, node_found] = find_node(key);
        if (node_found == nullptr) {
            return -1;
        }
        return node_found->value.second;
    }
    
    void remove(int key) {
        auto [prev, node_to_erase] = find_node(key);
        if (node_to_erase == nullptr) {
            return;
        }
        int index = hash_function_(key) % bucket_count();
        (prev ? prev->next : bucket_array_[index]) = node_to_erase->next;
        size_--;
    }

    int bucket_count() const {
        return bucket_array_.size();
    }

    node_pair find_node(const int& key) const {
        int index = hash_function_(key) % bucket_count();
        node* curr = bucket_array_[index];
        node* prev = nullptr;
        while (curr != nullptr) {
            auto [found_key, found_mapped] = curr->value;
            if (found_key == key) {
                return {prev, curr};
            }
            prev = curr;
            curr = curr->next;
        }
        return {nullptr, nullptr};
    }

    void clear() {
        for (auto& curr : bucket_array_) {
            while (curr != nullptr) {
                auto will_del = curr;
                curr = curr->next;
                delete will_del;
            }
        }
        size_ = 0;
    }

    int size() const {
        return size_;
    }

    ~MyHashMap() {
        clear();
    }

    float load_factor() const {
        return static_cast<float>(size()) / bucket_count();
    }

private:
    std::vector<node*> bucket_array_;
    static const int default_bucket_ = 100;
    hasher hash_function_;
    int size_;
};

思路

Hash 表的结构

由代码可得到如下图所示的 hash 表的结构:

img

find_node 函数

在对 hash 表操作的函数中,首先要实现一个 find_node 函数,代码如下图所示:

img

这个函数的思路为:

  1. 通过 hash 函数计算 key 所在的 bucket
  2. 定义两个变量用于记录当前遍历到的节点 curr 和其之前的一个节点 prev
  3. 遍历这个链表,如果找到符合 key 的 node 就返回这个 curr 和 prev
  4. 不符合就继续迭代到下一个节点

put 函数

  1. 如果 find_node 不为空,就更新其 value

  2. 如果 find_node 为空,其流程如下所示:

img

rehash 函数

当前的哈希表的实现没有进行 rehash,当 load_factor 大于 0.75 时需要进行 rehash,避免碰撞。其实代码如下所示:

void HashMap<K, M, H>::rehash(size_t new_bucket_count) {
    if (new_bucket_count == 0) {
        throw std::out_of_range("HashMap<K, M, H>::rehash: new_bucket_count must be positive.");
    }

    std::vector<node*> new_buckets_array(new_bucket_count, nullptr);
    for (auto& curr : _buckets_array) {
        while (curr != nullptr) {
            const auto& [key, mapped] = curr->value;
            size_t index = _hash_function(key) % new_bucket_count;
            // curr 存储的是当前桶的第一个元素的地址
            auto temp = curr;                     // temp 为将要更换桶位置的节点地址
            curr = temp->next;                    // curr 为 temp 的下一个节点地址
            temp->next = new_buckets_array[index];// temp 节点的下一个节点是新桶的第一个节点的地址
            new_buckets_array[index] = temp;      // 将 temp 做为新桶的第一个节点
        }
    }
    _buckets_array = std::move(new_buckets_array);
}

流程图如下所示:

img

insert 函数

  1. 寻找 key 对应的 node
  2. 如果没有,就创建一个新的 node 并返回其迭代器
  3. 如果有就直接返回迭代器

img

[] 运算符

  1. 调用 insert 插入一个 {key, {}} 的键值对

img

Reference

posted on 2023-10-19 11:02  LambdaQ  阅读(22)  评论(0编辑  收藏  举报