基于哈希表对unordered_map和unordered_set的封装

news2024/11/23 17:09:55

在这里插入图片描述

本章完整代码gitee仓库:对unordered_map和unordered_set的封装、unordered_map和unordered_set源码

文章目录

    • 🍭1. 哈希表的改造
      • 🍬1.1 模板参数的改造
      • 🍬1.2 增加迭代器
      • 🍬1.3 返回值的修改
    • 🍼2. 对unordered_set的封装
    • 🍶3. 对unordered_map的封装
    • 🥛4. 关于哈希表的长度

🍭1. 哈希表的改造

unordered_mapunordered_set底层都是哈希表的开散列方式。

我们还是像封装mapset类似,先对哈希表改造一下

🍬1.1 模板参数的改造

  • K:关键码类型
  • T:对于unordered_mapT,就是有个键值对;对于unordered_setT就是K
  • KeyOfT:取出元素(主要是为了unordered_map设计)
  • HashFunc:仿函数,将key转换成整数,才能进行取模
template<class K,class T,class KeyOfT,class HashFunc = DefaultHashFunc<K>>
class HashTable
{}

🍬1.2 增加迭代器

多增了PtrRef两个模板参数,用来控制是普通迭代器还是const迭代器

//前置声明
template<class K, class T, class KeyOfT, class HashFunc>
class HashTable;	

template<class K, class T, class Ptr, class Ref, class KeyOfT, class HashFunc>
struct HTIterator
{
    typedef HashNode<T> Node;
    typedef HTIterator<K, T, Ptr, Ref, KeyOfT, HashFunc> Self;
    //参考list迭代器
    typedef HTIterator<K, T, T*, T&, KeyOfT, HashFunc> Iterator;

    Node* _node;
    //迭代器里面并不用修改哈希表内容,直接设置为const
    const HashTable<K, T, KeyOfT, HashFunc>* _pht;

    HTIterator(Node* node, const HashTable<K, T, KeyOfT, HashFunc>* pht)
        :_node(node)
        ,_pht(pht)
    {}
    //通过迭代器为拷贝构造
    //const迭代器为构造
    HTIterator(const Iterator&it)
        :_node(it._node)
        , _pht(it._pht)
    {}

    Ref operator*()
    {
        return _node->_data;
    }

    Ptr operator->()
    {
        return &_node->_data;
    }

    Self& operator++()
    {
        if (_node->_next)
        {
            _node = _node->_next;
        }
        else
        {
            HashFunc hf;
            KeyOfT kot;
            //当前位置
            size_t hashi = hf(kot(_node->_data)) % _pht->_table.size();
            //下一个位置
            ++hashi;
            //找到不为空的桶
            while (hashi < _pht->_table.size())
            {
                if (_pht->_table[hashi])
                {
                    _node = _pht->_table[hashi];
                    return *this;
                }
                else
                {
                    ++hashi;
                }
            }
            _node = nullptr;
        }
        return *this;
    }

    bool operator!=(const Self& s)
    {
        return _node != s._node;
    }
};

🍬1.3 返回值的修改

Insert:

在哈希表里面,我们插入操作的返回值是bool类型,如果要进行封装,我们需要返回指向元素的迭代器(因为unordered_map还要支持[]

pair<iterator,bool> Insert(const T& data)
{
    KeyOfT kot;
    iterator ret = Find(kot(data));
    if (ret!=end())
        return make_pair(ret,false);

    HashFunc hf;
    //扩容 -- 扩容的时候会稍微慢一点 ---^(扩容)-----^(扩容)----------^(扩容)-----.....
    //这里的扩容不能和开放定址法一样采用将旧表元素重新插入新表
    //因为这里涉及到开节点,新表开新节点,旧表释放旧节点,浪费
    if (_n == _table.size())
    {
        size_t newSize = _table.size() * 2;
        vector<Node*> newTable;
        newTable.resize(newSize,nullptr);

        //遍历旧表,将节点牵过来
        for (size_t i = 0; i < _table.size(); i++)
        {
            Node* cur = _table[i];
            while (cur)
            {
                Node* next = cur->_next;
                //头插到新表
                size_t newHashi = hf(kot(cur->_data)) % newSize;
                cur->_next = newTable[newHashi];
                newTable[newHashi] = cur;

                cur = next;
            }
            _table[i] = nullptr;
        }
        _table.swap(newTable);
    }

    size_t hashi = hf(kot(data)) % _table.size();
    //头插
    Node* newNode = new Node(data);
    newNode->_next = _table[hashi];
    _table[hashi] = newNode;

    ++_n;
    return make_pair(iterator(newNode, this), true);
}

Find:

Find操作原先是返回一个节点,现在返回的是迭代器

iterator Find(const K& key)
{
    HashFunc hf;
    KeyOfT kot;
    size_t hashi = hf(key) % _table.size();
    Node* cur = _table[hashi];
    while (cur)
    {
        if (kot(cur->_data) == key)
        {
            return iterator(cur, this);
        }
        cur = cur->_next;
    }
    return end();
}

🍼2. 对unordered_set的封装

unordered_setkey是是不允许的修改的,所以迭代器都是底层都是const_iterator

namespace my_UnorderedSet
{
	template<class K>
	class unordered_set
	{
		struct SetKeyOfT
		{
			const K& operator()(const K& key)
			{
				return key;
			}
		};

	public:
		typedef typename hash_bucket::HashTable<K, K, SetKeyOfT>::const_iterator iterator;
		typedef typename hash_bucket::HashTable<K, K, SetKeyOfT>::const_iterator const_iterator;

		iterator begin() const
		{
			return _ht.begin();
		}

		iterator end() const
		{
			return _ht.end();
		}

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

			//稳定写法
			pair<typename hash_bucket::HashTable<K, K, SetKeyOfT>::iterator, bool> ret = _ht.Insert(key);
			return pair<iterator, bool>(ret.first, ret.second);
		}

		iterator find(const K& key)
		{
			return _ht.Find(key);
		}

		bool erase(const K& key)
		{
			return _ht.Erase(key);
		}


	private:
		hash_bucket::HashTable<K, K,SetKeyOfT> _ht;
	};
}

🍶3. 对unordered_map的封装

unordered_mapkey也是不允许修改的,通过控制pair里面的key值,来禁止对key值的修改(const限制)

namespace my_UnorderedMap
{
	template<class K,class V>
	class unordered_map
	{
		struct MapKeyOfT
		{
			const K& operator()(const pair<const K, V>& kv)
			{
				return kv.first;
			}
		};

	public:
		typedef typename hash_bucket::HashTable<K, pair<const K,V>, MapKeyOfT>::iterator iterator;
		typedef typename hash_bucket::HashTable<K, pair<const K,V>, MapKeyOfT>::const_iterator const_iterator;

		iterator begin()
		{
			return _ht.begin();
		}

		iterator end()
		{
			return _ht.end();
		}

		const_iterator begin() const
		{
			return _ht.begin();
		}

		const_iterator end() const
		{
			return _ht.end();
		}

		pair<iterator, bool> insert(const pair<K,V>& kv)
		{
			return _ht.Insert(kv);
		}
		V& operator[](const K& key)
		{
			pair<iterator, bool> ret = _ht.Insert(make_pair(key, V()));
			return ret.first->second;
		}

		iterator find(const K& key)
		{
			return _ht.Find(key);
		}

		bool erase(const K& key)
		{
			return _ht.Erase(key);
		}
	private:
		hash_bucket::HashTable<K, pair<const K, V>, MapKeyOfT> _ht;
	};
}

🥛4. 关于哈希表的长度

有人提出来过,哈希表的长度最好是用素数SGI库里面就采用的这种方式,提前准备好一份质数表,要扩容的时候,就扩到二倍附近的那个质数

image-20230919113658455

我们也可以放一份质数表到我们的哈希表里面

//质数表
size_t GetNextPrime(size_t prime)
{
    const int PRIMECOUNT = 28;
    static const size_t primeList[PRIMECOUNT] =
    {
    53ul, 97ul, 193ul, 389ul, 769ul,
    1543ul, 3079ul, 6151ul, 12289ul, 24593ul,
    49157ul, 98317ul, 196613ul, 393241ul, 786433ul,
    1572869ul, 3145739ul, 6291469ul, 12582917ul,
   25165843ul,
    50331653ul, 100663319ul, 201326611ul, 402653189ul,
   805306457ul,
    1610612741ul, 3221225473ul, 4294967291ul
    };
    size_t i = 0;
    for (; i < PRIMECOUNT; ++i)
    {
        if (primeList[i] > prime)
            return primeList[i];
    }
    return primeList[i];
}

然后初始化的时候,我们就用这个质数表提供的长度

HashTable()
{
    _table.resize(GetNextPrime(1), nullptr);
}

当然了,这个也没有具体的数据作为支撑,VS2022并没有采用这种方式,g++采用的是这种方式,具体采用哪种方式,看自己的喜好
VS2022:
在这里插入图片描述
g++:
在这里插入图片描述


那么本次分享就到这里,我们下期再见,如果还有下期的话

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

[计算机入门] 电源选项设置

3.10 电源选项设置 有时候我们的电脑一段时间没有用&#xff0c;会自己关掉屏幕或者直接睡眠&#xff0c;这是电源选项没有设置好导致的。 1、打开控制面板&#xff0c;打开其中的电源选项 2、点击左侧上方的选择关闭显示器的时间 3、进入到编辑计划设置界面&#xff0c;在…

听GPT 讲Istio源代码--pilot(6)

在 Istio 中&#xff0c;Pilot 是 Istio 控制平面的一个重要组件&#xff0c;它具有以下作用&#xff1a; 流量管理: Pilot 负责管理和配置服务之间的网络流量。它通过与底层的服务发现机制&#xff08;如 Kubernetes 或 Consul&#xff09;集成&#xff0c;监测服务注册和注销…

C数据结构二.练习题

一.求级数和 2.求最大子序列问题:设给定一个整数序列 ai.az..,a,(可能有负数).设计一个穷举算法,求a 的最大值。例如,对于序列 A {1,-1,1,-1,-1,1,1,1,1.1,-1,-1.1,-1,1,-1},子序列 A[5..9](1,1,1,1,1)具有最大值5 3.设有两个正整数 m 和n,编写一个算法 gcd(m,n),求它们的最大公…

WhatsApp无法收到验证码怎么办?别急,我来教你

最近收到好多小伙伴的问题咨询&#xff0c;而且大多是同一个问题&#xff1a;“WhatsApp无法验证手机号&#xff0c;也就是手机接收不到6位数字的短信验证码&#xff0c;这可如何是好&#xff1f;” 短信验证码收不到&#xff0c;连点几次重复短信后等待时间越来越久点击致电给…

B-小美的子序列(贪心)-- 牛客周赛 Round 12

示例1 输入 3 3 abc def ghi 输出 NO 示例2 输入 8 2 nm ex it td ul qu ac nt 输出 YES 说明 第1行选择第2个字母。 第2行选择第1个字母。 第3行选择第1个字母。 第4行选择第1个字母。 第5行选择第2个字母。 第6行选择第2个字母。 第7行选择第1个字母。 第8行选择第…

Idea创建springboot项目

1、选择file—>new –->project 2、选择“Spring Initializr”&#xff0c;点击“next”&#xff0c;进入工程信息配置界面修改配置信息. 备注&#xff1a;type类型选择“Maven(Generate a Maven based project achieve)”&#xff0c;生成工程路径。 3、点击next按钮&a…

【uniapp+vue3+u-picker】获取中国省市区数据结构,省市区数据三级联动json文件完整版,已实现三级联动效果

前言: 这个功能的实现,中间耽误了几天,在大佬的帮助下终于实现效果,匿名感谢xx大佬 要实现的效果如下: 1、首先需要获取省市区的数据,不考虑后端返数据,自己使用json文件的话,需要获取到完整的中国省市区数据 有个很不错的github源码可供参考,Administrative-divisio…

RK3568开发笔记(十):开发板buildroot固件移植开发的应用Demo,启动全屏显示

若该文为原创文章&#xff0c;转载请注明原文出处 本文章博客地址&#xff1a;https://hpzwl.blog.csdn.net/article/details/133021990 红胖子网络科技博文大全&#xff1a;开发技术集合&#xff08;包含Qt实用技术、树莓派、三维、OpenCV、OpenGL、ffmpeg、OSG、单片机、软硬…

few shot目标检测survey paper笔记(迁移学习)

paper: Few-Shot Object Detection: A Comprehensive Survey (CVPR2021) meta learning需要复杂的情景训练&#xff0c;而迁移学习仅需在一个single-branch结构上做两步训练。 常用的结构是Faster R-CNN&#xff0c;下面是Faster R-CNN的结构图。 RPN的修改 当样本数量很少时…

Vue的进阶使用--模板语法应用

目录 前言 一. Vue的基础语法 1.插值 1.1文本插值 1.2HTML插值 1.3属性插值 1.4Vue演示三元条件运算 2 指令 2.1if&&else指令&#xff08;v-if/v-else-if/v-else&#xff09; 2.2 v-for 指令 2.3 v-on指令(动态参数) 2.4知识点补充之v-if与v-show的区别 3.过…

著名书法家傅成洪在香港第八届“一带一路”高峰论坛上展示艺术与合作的融合

香港第八届“一带一路”高峰论坛于9月13日至14日在香港隆重举行&#xff0c;吸引了来自海内外的6000多名嘉宾&#xff0c;共同回顾“一带一路”倡议的历程&#xff0c;并展望未来的投资和商贸机遇。这一庆祝活动恰逢“一带一路”倡议的10周年&#xff0c;主题定为“携手十载 共…

[Python进阶] Pyinstaller打包模式

5.3 Pyinstaller打包模式 Pyinstaller将Python源码打包成程序有2种打包的方式&#xff1a; 单文件夹模式&#xff1a;指打包后将所有的程序文件放在一个文件夹内。 单文件模式&#xff1a;打包后只有一个可执行文件&#xff0c;全部的依赖文件都已经被打包进去了。 5.3.1 单文…

Linux 信号相关

int kill(pid_t pid, int sig); -功能&#xff1a;给某个进程pid 发送某个信号 参数sig可以使用宏值或者和它对应的编号 参数pid&#xff1a; >0 &#xff1b;将信号发给指定的进程 0&#xff1b;将信号发送给当前的进程组 -1&#xff1b;发送给每一个有权限接受这个信号的…

Postman应用——Variable变量设置(Global、Environment和Collection)

文章目录 Global变量设置Environment变量设置Collection变量设置Global、Environment环境变量预览 Global、Environment和Collection变量使用&#xff0c;点击查看。 Global变量设置 全局变量设置&#xff0c;作用域是所有Collection、Folder和Request&#xff0c;全局变量只有…

混淆矩阵和数据不平衡 (1/3)

一、说明 如果数据集数据不平恒&#xff0c;如何评估分类器的效果&#xff1f;如果分类器不好&#xff0c;如何改进分类器&#xff1f;本篇将讲述不平衡数据下&#xff0c;混淆矩阵的应用。 二、混淆矩阵的基本概念 2.1 连续数据分布 LET将数据视为连续的&#xff0c;分类的或有…

阿里云服务器价格更新,轻量应用服务器108元,云服务器182.04元起

阿里云服务器价格更新了&#xff0c;不同时期阿里云服务器的租用价格不同&#xff0c;目前阿里云在官网活动中新增加了一款经济型e实例规格的云服务器&#xff0c;现在购买阿里云轻量应用服务器最低为108元&#xff0c;购买云服务器最低为182.04元&#xff0c;换算到每天只要0.…

零基础学前端(四)3. 重点讲解 CSS:实战补全百度网站首页

1. 该篇适用于从零基础学习前端的小白 2. 初学者不懂代码得含义也要坚持模仿逐行敲代码&#xff0c;以身体感悟带动头脑去理解新知识 3. 初学者切忌&#xff0c;不要眼花缭乱&#xff0c;不要四处找其它文档&#xff0c;要坚定一个教授者的方式&#xff0c;将其学通透&#xff…

前端学习路线,带你入门程序猿

相信很多想学前端的小伙伴是非常迷茫的 前端知识体系很多&#xff0c;不知从何学起 而且框架也有不少&#xff0c;不知道该如何下手 很多学习前端的小伙伴都没有一个很好的学习路线图&#xff0c;简直可以说是看见啥学啥 而且自己在学习的时候非常吃力&#xff0c;感觉总是学不…

SQLite 学习笔记1 - 简介、下载、安装

SQLite 简介 SQLite是一款非常轻量级的关系数据库系统&#xff0c;支持多数SQL92标准。SQLite 是世界上使用最广泛的数据库引擎。SQLite 内置于所有手机和大多数计算机中&#xff0c;并捆绑在人们每天使用的无数其他应用程序中。 SQLite 是一个由C语音开发的嵌入式库&#xff…

DataGrip汉化

一、关闭DataGrip&#xff0c;下载新的jar包 链接&#xff1a;https://pan.baidu.com/s/1gTHpyMuIME_n8qC9KYbOzA 提取码&#xff1a;ute3 二、把下载的jar包放在lib文件里&#xff0c;把原来自带的jar替换掉 三、打开datagrip