哈希封装unordered_map/unordered_set

前言
看这篇博客之前请先看:
C++ | 哈希表-CSDN博客

💓 个人主页:普通young man-CSDN博客

⏩ 文章专栏:C++_普通young man的博客-CSDN博客

⏩ 本人giee:   普通小青年 (pu-tong-young-man) - Gitee.com

      若有问题 评论区见📝

🎉欢迎大家点赞👍收藏⭐文章
————————————————


目录

模拟实现unordered_map和unordered_set

insert方法的实现

Hashtable类中的Insert方法

unordered_map和unordered_set中的insert方法

迭代器iterator的实现

HTIterator类

unordered_map和unordered_set中的迭代器

map支持[]运算符

unordered_map中的[]运算符

完整代码实现

        Hashtable.h

unordered_map

unordered_set


本篇博客需要讲解的比较少,主要是配合上文写的

模拟实现unordered_map和unordered_set

insert方法的实现

Hashtable类中的Insert方法

Hashtable类的Insert方法用于向哈希表中插入一个新元素。其实现步骤如下:

pair<Iterator,bool> Insert(const T& data) {
    // 利用仿函数比较
    Hash hash;
    KeyOfT koft;
    // 判断是否存在
    Iterator it = Find(koft(data));
    if (it != End()) {
        return { it, false };
    }

    // 扩容
    if (_tables.size() == _n) {
        // 创建一个哈希表
        vector<Node*> new_tables(__stl_next_prime(_tables.size() + 1));
        // 遍历旧哈希表
        for (int i = 0; i < _tables.size(); i++) {
            // cur指向每一个元素,方便遍历哈希桶
            Node* cur = _tables[i];
            // 遍历桶
            while (cur) {
                // 计算新表的哈希值
                size_t new_hashi = hash(koft(cur->_data)) % new_tables.size();
                // 存储下一个节点
                Node* next = cur->_next;
                // 插入节点
                cur->_next = new_tables[new_hashi];
                new_tables[new_hashi] = cur;
                // 将下一个节点给cur,持续插入
                cur = next;
            }
        }
        // 交换指针
        _tables.swap(new_tables);
    }

    // 计算哈希值
    size_t hashi = hash(koft(data)) % _tables.size();
    // 头插
    Node* newnode = new  Node(data);
    newnode->_next = _tables[hashi];
    _tables[hashi] = newnode;
    // 增加个数
    ++_n;

    return { Iterator(newnode, this), true };
}
  • 检查元素是否已存在:使用Find方法查找要插入的元素是否已经存在于哈希表中。如果存在,则直接返回该元素的迭代器和false
  • 扩容操作:当哈希表的负载因子(元素个数_n与哈希桶数量_tables.size()之比)达到 1 时,需要进行扩容。扩容步骤如下:
    • 创建一个新的更大的哈希表new_tables
    • 遍历旧哈希表中的每个元素,将其重新插入到新哈希表中。
    • 交换旧哈希表和新哈希表的指针。
  • 插入新元素:计算新元素的哈希值,使用头插法将新元素插入到对应的哈希桶中。
  • 返回结果:返回新插入元素的迭代器和true
unordered_mapunordered_set中的insert方法

unordered_mapunordered_set中的insert方法只是简单地调用了Hashtable类的Insert方法:

// unordered_map
pair<iterator, bool> insert(const pair<K,V>& key) {
    return  _ht.Insert(key);
}

// unordered_set
pair<iterator, bool> insert(const K& key) {
    return  _ht.Insert(key);
}

迭代器iterator的实现

HTIterator

HTIterator类是哈希表的迭代器类,用于遍历哈希表中的元素。其实现步骤如下:

// 模板参数说明:
// K 表示键的类型
// T 表示存储在哈希表中的数据类型
// Ref 表示对数据的引用类型(例如 T& 或 const T&)
// Ptr 表示指向数据的指针类型(例如 T* 或 const T*)
// Hash 是一个哈希函数对象类型,用于计算键的哈希值
// KeyOfT 是一个函数对象类型,用于从存储的数据中提取键
template<class K, class T, class Ref, class Ptr, class Hash, class KeyOfT>
struct HTIterator {
    // 定义哈希表类型,方便后续使用
    typedef Hashtable<K, T, Hash, KeyOfT>  HT;
    // 定义节点类型,方便后续使用
    typedef Hashnode<T> Node;
    // 定义迭代器自身类型,方便后续使用
    typedef HTIterator<K, T, Ref, Ptr, Hash, KeyOfT> Self;

    // 指向哈希表的指针,用于访问哈希表的成员(如哈希桶数组等)
    const HT*  _ht;
    // 指向当前节点的指针,用于操作当前节点的数据
    Node* _node;

    // 构造函数,用于初始化迭代器
    // 参数 node 是当前节点的指针
    // 参数 ht 是指向哈希表的指针
    HTIterator(Node* node, const HT* ht) :
        _node(node), 
        _ht(ht)
    {}

    // 重载解引用运算符 *,用于获取当前节点的数据的引用
    // 当使用 *it(it 是迭代器对象)时,返回当前节点存储的数据的引用
    Ref operator*() {
        return  _node->_data;
    }

    // 重载箭头运算符 ->,用于获取当前节点的数据的指针
    // 当使用 it->(it 是迭代器对象)时,返回当前节点存储的数据的指针
    Ptr operator->() {
        return &_node->_data;
    }

    // 重载不等运算符 !=,用于比较两个迭代器是否指向不同的节点
    // 参数 s 是另一个迭代器对象
    // 如果两个迭代器指向的节点不同,返回 true,否则返回 false
    bool operator!=(const Self&  s) {
        return _node != s._node;
    }

    // 重载前置自增运算符 ++,用于将迭代器移动到下一个元素
    // 返回移动后的迭代器自身
    Self operator++() {
        // 如果当前节点有下一个节点,将迭代器移动到下一个节点
        if (_node->_next) {
            _node = _node->_next;
        }
        else {
            // 计算当前节点数据的键的哈希值
            Hash hash;
            KeyOfT koft;
            size_t hashi = hash(koft(_node->_data)) % _ht->_tables.size();
            // 从下一个哈希桶位置开始查找
            hashi++; 
            // 遍历哈希桶,直到找到一个有数据的桶或者遍历完所有桶
            while (hashi < _ht->_tables.size()) {
                _node = _ht->_tables[hashi];
                if (_node)
                    break;
                else
                    hashi++;
            }
            // 如果遍历完所有桶都没有找到有数据的桶,将节点指针置为 nullptr
            if (hashi == _ht->_tables.size()) {
                _node = nullptr;
            }
        }
        return *this;
    }
};
  • 构造函数:初始化迭代器的节点指针_node和哈希表指针_ht
  • 解引用运算符*:返回当前节点的数据引用。
  • 箭头运算符->:返回当前节点的数据指针。
  • 不等运算符!=:比较两个迭代器是否指向不同的节点。
  • 前置自增运算符++:将迭代器移动到下一个元素。如果当前桶还有数据,则移动到当前桶的下一个节点;否则,从下一个桶开始查找,直到找到有数据的桶或遍历完所有桶。
unordered_mapunordered_set中的迭代器

unordered_mapunordered_set中的迭代器是通过Hashtable类的迭代器实现的:

// unordered_map
typedef typename hash_bucket::Hashtable<K, pair<const K,V>, Hash, MapKeyOfT>::Iterator iterator;
typedef typename hash_bucket::Hashtable<K, pair<const K, V>, Hash, MapKeyOfT >::ConstIterator const_iterator;

// unordered_set
typedef typename hash_bucket::Hashtable<K, const K,Hash,SetKeyOfT>::Iterator iterator;
typedef typename hash_bucket::Hashtable<K, const K,Hash ,SetKeyOfT >::ConstIterator const_iterator;

map支持[]运算符

unordered_map中的[]运算符

unordered_map中的[]运算符用于通过键访问或插入元素。其实现步骤如下:

V& operator[](const K& key) {
    pair<iterator, bool> ret = insert({ key, V() });
    return ret.first->second;
}
  • 入元素:调用insert方法插入一个键值对{ key, V() }。如果键已经存在,则insert方法会返回该元素的迭代器和false;否则,会插入一个新元素并返回该元素的迭代器和true
  • 返回值:返回插入元素的值的引用。

通过这种方式,[]运算符可以实现以下功能:

  • 如果键已经存在,则返回该键对应的值的引用。
  • 如果键不存在,则插入一个新的键值对,值为默认构造的V(),并返回该值的引用。

完整代码实现


        Hashtable.h
// 防止头文件被重复包含
#pragma once
#include<iostream>
#include<vector>
// 使用标准命名空间,方便使用标准库中的类和函数
using namespace std;

// 该函数用于获取大于等于给定值 n 的下一个质数
// 质数常用于哈希表的容量设置,可减少哈希冲突
inline unsigned long __stl_next_prime(unsigned long n)
{
    // 假设 long 至少为 32 位
    // 定义质数列表中质数的数量
    static const int __stl_num_primes = 28;
    // 预定义的质数列表,这些质数会作为哈希表扩容时的容量选择
    static const unsigned long __stl_prime_list[__stl_num_primes] = {
        53, 97, 193, 389, 769,
        1543, 3079, 6151, 12289, 24593,
        49157, 98317, 196613, 393241, 786433,
        1572869, 3145739, 6291469, 12582917, 25165843,
        50331653, 100663319, 201326611, 402653189, 805306457,
        1610612741, 3221225473, 4294967291
    };
    // first 指向质数列表的起始位置
    const unsigned long* first = __stl_prime_list;
    // last 指向质数列表的结束位置
    const unsigned long* last = __stl_prime_list + __stl_num_primes;
    // 使用 lower_bound 函数在质数列表中查找第一个不小于 n 的质数
    // lower_bound 是标准库函数,采用二分查找,效率较高
    const unsigned long* pos = lower_bound(first, last, n);
    // 如果没有找到比 n 大的质数,返回列表中最后一个质数;否则返回找到的质数
    return pos == last ? *(last - 1) : *pos;
}

// 仿函数模板,用于将键 k 转换为无符号整数类型,默认实现是直接转换
// 这个仿函数在计算哈希值时会用到
template<class k>
struct HashFun
{
    // 重载 () 运算符,将键转换为 size_t 类型
    size_t operator()(const k& key) {
        return (size_t)key;
    }
};

// 针对 string 类型的 HashFun 特化版本
// 使用 BKDR 哈希算法将字符串转换为哈希值,减少哈希冲突
template<>
struct HashFun<string>
{
    size_t operator()(const string& key) {
        size_t hash = 0;
        // 利用 BKDR 算法计算字符串的哈希值
        for (auto it : key)
        {
            hash += it;
            hash *= 131;
        }
        return hash;
    }
};

// 链地址法实现的哈希表命名空间
namespace hash_bucket {

    // 哈希表节点结构体,存储数据和指向下一个节点的指针
    template<class T>
    struct Hashnode
    {
        T _data;
        Hashnode<T>* _next;
        // 构造函数,初始化节点数据,指向下一个节点的指针置为 nullptr
        Hashnode(const T& data)
            :
            _data(data),
            _next(nullptr)
        {}
    };

    // 前置声明 Hashtable 类,以便在迭代器类中使用
    template<class K, class T, class Hash, class KeyOfT>
    class Hashtable;

    // 哈希表迭代器结构体,用于遍历哈希表中的元素
    template<class K, class T, class Ref, class Ptr, class Hash, class KeyOfT>
    struct HTIterator
    {
        // 定义哈希表类型,方便后续使用
        typedef Hashtable<K, T, Hash, KeyOfT>  HT;
        // 定义节点类型,方便后续使用
        typedef Hashnode<T> Node;
        // 定义迭代器自身类型,方便后续使用
        typedef HTIterator<K, T, Ref, Ptr, Hash, KeyOfT> Self;

        // 指向哈希表的指针,用于访问哈希表的成员(如哈希桶数组等)
        const HT*  _ht;
        // 指向当前节点的指针,用于操作当前节点的数据
        Node* _node;
        // 构造函数,初始化迭代器的节点指针和哈希表指针
        HTIterator(Node* node, const HT* ht) :
            _node(node), 
            _ht(ht)
        {}

        // 重载解引用运算符 *,用于获取当前节点的数据的引用
        Ref operator*() {
            return  _node->_data;
        }

        // 重载箭头运算符 ->,用于获取当前节点的数据的指针
        Ptr operator->() {
            return &_node->_data;
        }

        // 重载不等运算符 !=,用于比较两个迭代器是否指向不同的节点
        bool operator!=(const Self&  s) {
            return _node != s._node;
        }

        // 重载前置自增运算符 ++,用于将迭代器移动到下一个元素
        Self operator++() {
            // 如果当前节点有下一个节点,将迭代器移动到下一个节点
            if (_node->_next) {
                _node = _node->_next;
            }
            else {
                // 计算当前节点数据的键的哈希值
                Hash hash;
                KeyOfT koft;
                size_t hashi = hash(koft(_node->_data)) % _ht->_tables.size();
                // 从下一个哈希桶位置开始查找
                hashi++; 
                // 遍历哈希桶,直到找到一个有数据的桶或者遍历完所有桶
                while (hashi < _ht->_tables.size()) {
                    _node = _ht->_tables[hashi];
                    // 如果找到有数据的桶,跳出循环
                    if (_node)
                        break;
                    else
                        // 不断增加哈希桶位置,继续查找
                        hashi++;
                }
                // 如果遍历完所有桶都没有找到有数据的桶,将节点指针置为 nullptr
                if (hashi == _ht->_tables.size()) {
                    _node = nullptr;
                }
            }
            return *this;
        }
    };

    // 哈希表类,使用链地址法解决哈希冲突
    template<class K, class T, class Hash, class KeyOfT>
    class Hashtable
    {
        // 友元声明,允许迭代器类访问 Hashtable 的私有成员
        template<class K, class T, class Ref, class Ptr, class Hash, class KeyOfT>
        friend  struct HTIterator;

        // 定义节点类型,方便后续使用
        typedef Hashnode<T> Node;
    public:
        // 定义普通迭代器类型
        typedef HTIterator<K, T, T&, T*, Hash, KeyOfT> Iterator;
        // 定义常量迭代器类型
        typedef HTIterator<K, T, const T&, const T*, Hash, KeyOfT> ConstIterator;

        // 返回指向哈希表第一个元素的迭代器
        Iterator Begin() {
            // 如果哈希表中没有元素,直接返回 End 迭代器
            if (_n == 0) return End();
            // 遍历哈希桶数组,找到第一个有数据的桶
            for (size_t i = 0; i < _tables.size(); i++) {
                Node* cur = _tables[i];
                if (cur) {
                    return Iterator(cur, this);
                }
            }
            // 如果没有找到有数据的桶,返回 End 迭代器
            return End();
        }

        // 返回指向哈希表末尾的迭代器
        Iterator End() {
            return Iterator(nullptr, this);
        }

        // 常量版本的 Begin 方法,返回常量迭代器
        ConstIterator Begin() const {
            // 如果哈希表中没有元素,直接返回 End 迭代器
            if (_n == 0) return End();
            // 遍历哈希桶数组,找到第一个有数据的桶
            for (size_t i = 0; i < _tables.size(); i++) {
                Node* cur = _tables[i];
                if (cur) {
                    return ConstIterator(cur, this);
                }
            }
            // 如果没有找到有数据的桶,返回 End 迭代器
            return End();
        }

        // 常量版本的 End 方法,返回常量迭代器
        ConstIterator End() const {
            return ConstIterator(nullptr, this);
        }

        // 构造函数,初始化哈希表,使用下一个质数作为初始大小
        Hashtable() :
            _tables(__stl_next_prime(0)),
            _n(0)
        {};

        // 析构函数,释放哈希表中所有节点的内存
        ~Hashtable() {
            // 遍历哈希桶数组
            for (int i = 0; i < _tables.size(); i++)
            {
                Node* cur = _tables[i];
                // 遍历当前桶中的所有节点
                while (cur)
                {
                    // 存储下一个节点
                    Node* next = cur->_next;
                    // 释放当前节点的内存
                    delete cur;
                    // 将下一个节点赋值给 cur,继续释放
                    cur = next;
                }
                // 将当前桶的指针置为 nullptr
                _tables[i] = nullptr;
            }
            // 将元素个数置为 0
            _n = 0;
        }

        // 拷贝构造函数,复制另一个哈希表的内容
        Hashtable(const Hashtable& m1) :
            _n(m1._n),
            _tables(m1._tables.size())
        {
            // 循环遍历每一个桶
            for (int i = 0; i < m1._tables.size(); i++)
            {
                Node* cur = m1._tables[i];
                // 遍历当前桶中的所有节点
                while (cur)
                {
                    // 创建一个新节点,复制当前节点的数据
                    Node* newnode = new Node(cur->_data);
                    // 使用头插法将新节点插入到当前桶中
                    newnode->_next = _tables[i];
                    _tables[i] = newnode;
                    // 移动到下一个节点
                    cur = cur->_next;
                }
            }
        }

        // 赋值运算符重载,使用拷贝交换技术实现赋值操作
        Hashtable& operator=(Hashtable tmp)
        {
            // 交换哈希桶数组
            _tables.swap(tmp._tables);
            // 交换元素个数
            std::swap(_n, tmp._n);
            return *this;
        }

        // 向哈希表中插入一个元素
        pair<Iterator, bool> Insert(const T& data) {
            // 创建哈希函数对象
            Hash hash;
            // 创建从数据中提取键的函数对象
            KeyOfT koft;
            // 判断要插入的元素是否已经存在于哈希表中
            Iterator it = Find(koft(data));
            if (it != End()) {
                // 如果元素已经存在,返回该元素的迭代器和 false
                return { it, false };
            }

            // 扩容判断
            // 当哈希表的负载因子(元素个数与哈希桶数量之比)达到 1 时,需要扩容
            if (_tables.size() == _n) {
                // 创建一个新的哈希表,容量为下一个质数
                vector<Node*> new_tables(__stl_next_prime(_tables.size() + 1));
                // 遍历旧哈希表的每个桶
                for (int i = 0; i < _tables.size(); i++)
                {
                    Node* cur = _tables[i];
                    // 遍历当前桶中的所有节点
                    while (cur)
                    {
                        // 计算节点在新哈希表中的哈希值
                        size_t new_hashi = hash(koft(cur->_data)) % new_tables.size();
                        // 存储下一个节点
                        Node* next = cur->_next;
                        // 使用头插法将节点插入到新哈希表的对应桶中
                        cur->_next = new_tables[new_hashi];
                        new_tables[new_hashi] = cur;
                        // 移动到下一个节点
                        cur = next;
                    }
                }
                // 交换旧哈希表和新哈希表的指针
                _tables.swap(new_tables);
            }

            // 计算要插入元素在当前哈希表中的哈希值
            size_t hashi = hash(koft(data)) % _tables.size();
            // 创建一个新节点,存储要插入的元素
            Node* newnode = new  Node(data);
            // 使用头插法将新节点插入到对应桶中
            newnode->_next = _tables[hashi];
            _tables[hashi] = newnode;
            // 元素个数加 1
            ++_n;

            // 返回新插入元素的迭代器和 true
            return { Iterator(newnode, this), true };
        }

        // 在哈希表中查找指定键的元素
        Iterator Find(const K& key) {
            // 创建从数据中提取键的函数对象
            KeyOfT koft;
            // 创建哈希函数对象
            Hash hash;
            // 计算要查找元素的哈希值
            size_t hashi = hash(key) % _tables.size();
            Node* cur = _tables[hashi];
            // 遍历对应桶中的所有节点
            while (cur)
            {
                if (koft(cur->_data) == key)
                {
                    // 如果找到匹配的元素,返回该元素的迭代器
                    return Iterator(cur, this);
                }
                cur = cur->_next;
            }
            // 如果没有找到匹配的元素,返回 End 迭代器
            return End();
        }

        // 从哈希表中删除指定键的元素
        bool Erase(const K& key) {
            // 创建从数据中提取键的函数对象
            KeyOfT koft;
            // 计算要删除元素的哈希值
            size_t hashi = hash(key) % _tables.size();
            // 记录前一个节点的指针
            Node* prev = nullptr;
            Node* cur = _tables[hashi];
            // 遍历对应桶中的所有节点
            while (cur)
            {
                if (koft(cur->_data) == key) {
                    if (prev == nullptr)
                    {
                        // 如果要删除的是第一个节点,更新桶的头指针
                        _tables[hashi] = cur->_next;
                    }
                    else
                    {
                        // 否则,更新前一个节点的指针
                        prev->_next = cur->_next;
                    }
                    // 释放要删除节点的内存
                    delete cur;
                    return true;
                }
                else {
                    // 移动前一个节点和当前节点的指针
                    prev = cur;
                    cur = cur->_next;
                }
            }
            // 如果没有找到要删除的元素,返回 false
            return false;
        }

    private:
        // 存储哈希桶的指针数组
        vector<Node*> _tables;
        // 哈希表中元素的个数
        size_t _n = 0;
    };

}
unordered_map
// 防止头文件被重复包含
#pragma once
// 包含自定义的哈希表头文件,因为 unordered_map 依赖于 Hashtable 的实现
#include"Hashtable.h"

// 自定义的命名空间,用于组织自定义的 map 和 set 相关的类和函数
namespace Map_Set_Hash {
    // 定义一个模板类 unordered_map,实现类似标准库中 unordered_map 的功能
    // K 表示键的类型,V 表示值的类型,Hash 是一个哈希函数对象类型,默认使用 HashFun<K>
    template<class K, class V, class Hash = HashFun<K>>
    class unordered_map
    {
        // 内部结构体 MapKeyOfT,用于从键值对中提取键
        // 这在哈希表的查找、插入等操作中用于比较键
        struct MapKeyOfT
        {
            // 重载 () 运算符,返回键值对中的键
            const K& operator()(const pair<K, V>& kv) {
                return kv.first;
            }
        };
    public:
        // 定义迭代器类型,使用哈希表类中的迭代器类型
        // iterator 是普通迭代器类型,用于遍历和修改元素
        typedef typename hash_bucket::Hashtable<K, pair<const K, V>, Hash, MapKeyOfT>::Iterator iterator;
        // const_iterator 是常量迭代器类型,用于遍历元素但不能修改元素
        typedef typename hash_bucket::Hashtable<K, pair<const K, V>, Hash, MapKeyOfT>::ConstIterator const_iterator;

        // 迭代器方法,返回指向 unordered_map 第一个元素的迭代器
        // 调用底层哈希表的 Begin 方法获取迭代器
        iterator begin() {
            return _ht.Begin();
        }

        // 迭代器方法,返回指向 unordered_map 末尾的迭代器
        // 调用底层哈希表的 End 方法获取迭代器
        iterator end() {
            return _ht.End();
        }

        // 常量版本的迭代器方法,返回指向 unordered_map 第一个元素的常量迭代器
        // 调用底层哈希表的 Begin 方法获取常量迭代器
        const_iterator begin() const {
            return _ht.Begin();
        }

        // 常量版本的迭代器方法,返回指向 unordered_map 末尾的常量迭代器
        // 调用底层哈希表的 End 方法获取常量迭代器
        const_iterator end() const {
            return _ht.End();
        }

        // 重载 [] 运算符,用于通过键访问或插入元素
        // 如果键不存在,插入一个新的键值对,值为默认构造的 V()
        // 返回值的引用,方便进行赋值操作
        V& operator[](const K& key) {
            pair<iterator, bool> ret = insert({ key, V() });
            return ret.first->second;
        }

        // 插入方法,向 unordered_map 中插入一个键值对
        // 调用底层哈希表的 Insert 方法进行插入操作
        // 返回一个 pair,包含插入元素的迭代器和插入是否成功的标志
        pair<iterator, bool> insert(const pair<K, V>& key) {
            return _ht.Insert(key);
        }

        // 查找方法,在 unordered_map 中查找指定键值对
        // 调用底层哈希表的 Find 方法进行查找操作
        // 返回找到元素的迭代器,如果未找到则返回 end() 迭代器
        iterator find(const pair<K, V>& key) {
            return _ht.Find(key);
        }

        // 删除方法,从 unordered_map 中删除指定键值对
        // 调用底层哈希表的 Erase 方法进行删除操作
        // 返回删除是否成功的标志
        bool erase(const pair<K, V>& key) {
            return _ht.Erase(key);
        }

    private:
        // 底层使用的哈希表对象,存储键值对数据
        // 模板参数指定了键的类型、值的类型、哈希函数对象类型和提取键的函数对象类型
        hash_bucket::Hashtable<K, pair<const K, V>, Hash, MapKeyOfT> _ht;
    };

    // 测试函数 map_test,用于测试 unordered_map 类的功能
    void map_test() {
        // 创建一个键和值都是 string 类型的 unordered_map 对象 dict
        unordered_map<string, string> dict;
        // 向 dict 中插入一些键值对
        dict.insert({ "sort", "排序" });
        dict.insert({ "字符串", "string" });

        // 尝试插入重复的键值对,这在哈希表中不会实际插入新元素
        dict.insert({ "sort", "排序" });
        dict.insert({ "left", "左边" });
        dict.insert({ "right", "右边" });

        // 使用 [] 运算符修改已存在键的值,或者插入新的键值对
        dict["left"] = "左边,剩余";
        dict["insert"] = "插入";
        dict["string"];  // 这里调用 [] 运算符创建了一个值为默认构造的键值对,但未进行赋值

        // 使用范围 for 循环遍历 dict,输出每个键值对
        for (auto& kv : dict)
        {
            cout << kv.first << ":" << kv.second << endl;
        }
        cout << endl;

        // 使用迭代器遍历 dict,修改值并输出键值对
        unordered_map<string, string>::iterator it = dict.begin();
        while (it != dict.end())
        {
            // 键是 const 的,不能修改,这里是注释掉的示例代码
            //it->first += 'x';
            // 修改值
            it->second += 'x';
            cout << it->first << ":" << it->second << endl;
            // 移动迭代器到下一个元素
            ++it;
        }
        cout << endl;
    }
}
unordered_set
// 防止头文件被重复包含
#pragma once
// 包含之前实现的哈希表的头文件,因为 unordered_set 基于该哈希表实现
#include"Hashtable.h"

namespace Map_Set_Hash {
    // 定义一个模板类 unordered_set,类似于标准库中的 std::unordered_set
    // K 表示集合中元素的类型,Hash 是一个哈希函数对象类型,默认使用 HashFun<K>
    template<class K, class Hash = HashFun<K>>
    class unordered_set {
    private:
        // 用于从元素中提取键的仿函数
        // 对于 unordered_set 而言,元素本身就是键
        struct SetKeyOfT {
            // 重载 () 运算符,直接返回传入的元素
            const K& operator()(const K& key) {
                return key;
            }
        };

    public:
        // 定义迭代器类型
        // iterator 是普通迭代器,可用于遍历和访问集合中的元素
        typedef typename hash_bucket::Hashtable<K, const K, Hash, SetKeyOfT>::Iterator iterator;
        // const_iterator 是常量迭代器,用于在常量对象上遍历和访问元素,不能修改元素
        typedef typename hash_bucket::Hashtable<K, const K, Hash, SetKeyOfT>::ConstIterator const_iterator;

        // 迭代器方法:返回指向集合第一个元素的迭代器
        iterator begin() {
            // 调用底层哈希表的 Begin 方法获取起始迭代器
            return _ht.Begin();
        }

        // 迭代器方法:返回指向集合末尾(最后一个元素之后)的迭代器
        iterator end() {
            // 调用底层哈希表的 End 方法获取结束迭代器
            return _ht.End();
        }

        // 常量版本的 begin 方法:返回指向常量集合第一个元素的常量迭代器
        const_iterator begin() const {
            // 调用底层哈希表的 Begin 方法获取常量起始迭代器
            return _ht.Begin();
        }

        // 常量版本的 end 方法:返回指向常量集合末尾(最后一个元素之后)的常量迭代器
        const_iterator end() const {
            // 调用底层哈希表的 End 方法获取常量结束迭代器
            return _ht.End();
        }

        // 插入元素到集合中
        // key 是要插入的元素
        // 返回一个 pair,包含插入元素的迭代器和插入是否成功的标志(如果元素已存在则插入失败)
        pair<iterator, bool> insert(const K& key) {
            // 调用底层哈希表的 Insert 方法进行插入操作
            return _ht.Insert(key);
        }

        // 在集合中查找指定元素
        // key 是要查找的元素
        // 返回指向找到元素的迭代器,如果未找到则返回 end() 迭代器
        iterator find(const K& key) {
            // 调用底层哈希表的 Find 方法进行查找操作
            return _ht.Find(key);
        }

        // 从集合中删除指定元素
        // key 是要删除的元素
        // 返回删除操作是否成功的标志(如果元素存在则删除成功)
        bool erase(const K& key) {
            // 调用底层哈希表的 Erase 方法进行删除操作
            return _ht.Erase(key);
        }

    private:
        // 底层使用的哈希表对象,存储集合中的元素
        hash_bucket::Hashtable<K, const K, Hash, SetKeyOfT> _ht;
    };

    // 打印 unordered_set 中元素的函数
    // s 是要打印的 unordered_set 常量对象
    void print(const unordered_set<int>& s) {
        // 使用常量迭代器遍历集合
        unordered_set<int>::const_iterator it = s.begin();
        while (it != s.end()) {
            // 注释掉的代码试图修改元素,在 unordered_set 中元素是 const 的,不能修改
            //*it = 1;
            // 输出当前元素
            cout << *it << " ";
            // 移动迭代器到下一个元素
            ++it;
        }
        cout << endl;

        // 使用范围 for 循环遍历集合并输出元素
        for (auto e : s) {
            cout << e << " ";
        }
        cout << endl;
    }

    // 测试 unordered_set 功能的函数
    void set_test() {
        // 定义一个整数数组
        int a[] = { 3, 11, 86, 7, 88, 82, 1, 881, 5, 6, 7, 6 };
        // 创建一个 unordered_set 对象,用于存储整数元素
        unordered_set<int> s;
        // 将数组中的元素插入到集合中
        for (auto e : a) {
            s.insert(e);
        }
        // 调用 print 函数打印集合中的元素
        print(s);

        // 使用普通迭代器遍历集合并输出元素
        unordered_set<int>::iterator it = s.begin();
        while (it != s.end()) {
            // 注释掉的代码试图修改元素,在 unordered_set 中元素是 const 的,不能修改
            //*it = 1;
            cout << *it << " ";
            ++it;
        }
        cout << endl;

        // 使用范围 for 循环遍历集合并输出元素
        for (auto e : s) {
            cout << e << " ";
        }
        cout << endl;

        // 使用常量迭代器遍历集合并输出元素
        unordered_set<int>::const_iterator st = s.begin();
        while (st != s.end()) {
            // 注释掉的代码试图修改元素,在 unordered_set 中元素是 const 的,不能修改
            //*it = 1;
            cout << *st << " ";
            ++st;
        }
        cout << endl;
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/979353.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

绕过information_schema与order by注入以及seacsmv9注入

一:information_schema绕过 1,、sys数据库包含了许多视图&#xff0c;这些视图整合了来自information_schema和performance_schema的数据&#xff0c;攻击者可以利用这些视图来获取数据库结构信息。 -- 获取所有数据库名 SELECT DISTINCT table_schema FROM sys.schema_table_…

编程题 - 汽水瓶【JavaScript/Node.js解法】

‌“学如逆水行舟&#xff0c;不进则退。”‌ ——《增广贤文》 目录 汽水瓶 题目&#xff1a;解答分析&#xff1a;js代码解答 -ACM模式&#xff1a;代码通过&#xff1a;题解分析&#xff1a;简洁思路代码&#xff1a; 汽水瓶 题目&#xff1a; 某商店规定&#xff1a;三个空…

【Java SE】Java中String的内存原理

参考笔记&#xff1a; Java String 类深度解析&#xff1a;内存模型、常量池与核心机制_java stringx、-CSDN博客 解析java中String的内存原理_string s1 new string("ab");内存分析-CSDN博客 目录 1.String初识 2.字符串字面量 3.内存原理图 4. 示例验证 4.…

(0)阿里云大模型ACP-考试回忆

这两天通过了阿里云大模型ACP考试&#xff0c;由于之前在网上没有找到真题&#xff0c;导致第一次考试没有过&#xff0c;后面又重新学习了一遍文档才顺利通过考试&#xff0c;这两次考试内容感觉考试题目90%内容是覆盖的&#xff0c;后面准备分享一下每一章的考题&#xff0c;…

1.2.3 使用Spring Initializr方式构建Spring Boot项目

本实战概述介绍了如何使用Spring Initializr创建Spring Boot项目&#xff0c;并进行基本配置。首先&#xff0c;通过Spring Initializr生成项目骨架&#xff0c;然后创建控制器HelloController&#xff0c;定义处理GET请求的方法hello&#xff0c;返回HTML字符串。接着&#xf…

013作用域

一、基本概念 C语言中&#xff0c;标识符都有一定的可见范围&#xff0c;这些可见范围保证了标识符只能在一个有限的区域内使用&#xff0c;这个可见范围&#xff0c;被称为作用域&#xff08;scope&#xff09;。 软件开发中&#xff0c;尽量缩小标识符的作用域是一项基本原…

IP段转CIDR:原理Java实现

&#x1f9d1; 博主简介&#xff1a;CSDN博客专家&#xff0c;历代文学网&#xff08;PC端可以访问&#xff1a;https://literature.sinhy.com/#/?__c1000&#xff0c;移动端可微信小程序搜索“历代文学”&#xff09;总架构师&#xff0c;15年工作经验&#xff0c;精通Java编…

使用PDFMiner.six解析PDF数据

PDF&#xff08;可移植文档格式&#xff09;文件是由Adobe创建的一种灵活的文件格式&#xff0c;它允许文档在不同的软件、硬件和操作系统中一致地显示。每个PDF文件都包含对固定布局文档的全面描述&#xff0c;包括文本、字体、图形和其他必要的显示元素。pdf通常用于文档共享…

DeepSeek后训练:监督微调和强化学习

注&#xff1a;此文章内容均节选自充电了么创始人&#xff0c;CEO兼CTO陈敬雷老师的新书《自然语言处理原理与实战》&#xff08;人工智能科学与技术丛书&#xff09;【陈敬雷编著】【清华大学出版社】 文章目录 DeepSeek大模型技术系列十二DeepSeek大模型技术系列十二》DeepS…

蓝桥杯好题推荐----高精度乘法

&#x1f308;个人主页&#xff1a;羽晨同学 &#x1f4ab;个人格言:“成为自己未来的主人~” 题目链接 P1303 A*B Problem - 洛谷https://www.luogu.com.cn/problem/P1303 解题思路 这道题的思路&#xff0c;其实和前面差不多&#xff0c;我们主要说一下最为关键的部分&…

【实战 ES】实战 Elasticsearch:快速上手与深度实践-1.3.2Kibana可视化初探

&#x1f449; 点击关注不迷路 &#x1f449; 点击关注不迷路 &#x1f449; 点击关注不迷路 文章大纲 10分钟快速部署Kibana可视化平台1. Kibana与Elasticsearch关系解析1.1 架构关系示意图1.2 核心功能矩阵 2. 系统环境预检2.1 硬件资源配置2.2 软件依赖清单 3. Docker快速部…

low rank decomposition如何用于矩阵的分解

1. 什么是矩阵分解和低秩分解 矩阵分解是将一个矩阵表示为若干结构更简单或具有特定性质的矩阵的组合或乘积的过程。低秩分解&#xff08;Low Rank Decomposition&#xff09;是其中一种方法&#xff0c;旨在将原矩阵近似为两个或多个秩较低的矩阵的乘积&#xff0c;从而降低复…

算法题(81):询问学号

审题&#xff1a; 需要我们根据给出的n值确定录入数据个数&#xff0c;然后根据给出的数据存储学号。再根据m值确定需要输出的学号个数&#xff0c;然后根据数组内容输出学号 思路: 我们可以利用数组进行数据顺序存储&#xff0c;以及随机读取完成本题 由于学号最大为1e9&#…

项目开发时,涉及到的css样式

本文章&#xff0c;主要用来收集vue项目开发时&#xff0c;遇到的各种css样式问题。 1、如何让容器的高度等于浏览器窗口的高度&#xff1f; 问题描述&#xff1a;我们的微软浏览器和谷歌浏览器的窗口高度不一致&#xff0c;但是我们想无论打开哪个浏览器&#xff0c;都让我们项…

萌新学 Python 之 os 模块

os 模块&#xff1a;主要提供程序与操作系统进行交互的接口 先导入模块&#xff1a;import os 1. os.listdir()&#xff0c;获取当前目录的文件&#xff0c;返回到列表中 2. os.mkdir(文件目录, mode 0o777)&#xff0c;创建目录&#xff0c;777 表示读写程序 在当前目录下…

Linux系统下Mplayer的高效人机操作界面设计

1. 项目背景 Mplayer作为经典开源媒体播放器&#xff0c;存在以下交互缺陷&#xff1a; 默认命令行界面需记忆复杂指令&#xff08;如&#xff1a;mplayer -fs -playlist file.list&#xff09; 缺乏可视化播放列表管理 状态信息展示不直观&#xff08;需依赖终端输出&#…

某住宅小区地下车库安科瑞的新能源汽车充电桩的配电设计与应用方案

摘要&#xff1a; 文中以某住宅小区建设工程为例,重点研究了住宅小区地下车库新能源汽车充电桩配电设计,从位置设置、安装方式选择、配电箱设置、配电箱回路设置、供配电系统设计等方面展开分析,提出了民用建筑充电桩设计的科学建议,为新能源充电桩的推广应用提供参考。 关键…

达梦:内存相关参数

目录 28个相关参数1. 内存池相关MEMORY_POOLMEMORY_N_POOLSMEMORY_BAK_POOL 2. 大缓冲区相关HUGE_BUFFERHUGE_BUFFER_POOLS 3. 共享缓冲区相关BUFFERBUFFER_POOLSBUFFER_MODEMAX_BUFFER 4. 快速池相关FAST_POOL_PAGES 5. 回收池相关RECYCLE_POOLS 6. 回滚段池相关ROLLSEG_POOLS…

TCP的三次握手与四次挥手:建立与终止连接的关键步骤

引言 ‌TCP&#xff08;传输控制协议&#xff09;工作在OSI模型的传输层‌。OSI模型将计算机网络功能划分为七个层级&#xff0c;从底层到顶层依次是&#xff1a;物理层、数据链路层、网络层、传输层、会话层、表示层和应用层。传输层负责在网络节点之间提供可靠的端到端通信&a…

游戏引擎学习第129天

仓库:https://gitee.com/mrxiao_com/2d_game_3 小妙招: vscode:定位错误行 一顿狂按F8 重构快捷键:F2 重构相关的变量 回顾并为今天的内容做准备 今天的工作主要集中在渲染器的改进上&#xff0c;渲染器现在运行得相当不错&#xff0c;得益于一些优化和组织上的改进。我们计…