C/C++进阶 (8)哈希表(STL)

个人主页:仍有未知等待探索-CSDN博客

专题分栏:C++

本文着重于模拟实现哈希表,并非是哈希表的使用。

实现的哈希表的底层用的是线性探测法,并非是哈希桶。

目录

一、标准库中的哈希表

1、unordered_map

2、unordered_set

二、模拟实现哈希表

1、结构

2、注解hashtable的模板参数:

3、重难点

1、二元“!=” 没有找到接收类型的右操作数的运算符​编辑

2、迭代器的本质、const和非const的区别

3、类A在类B的上面,想要在类A中使用以类B为类型的变量,怎么办?

4、类A在类B的上面,想要在类A中使用类B的私有成员,怎么办?

三、代码


一、标准库中的哈希表

这两个是两种不同映射关系的哈希表。

unordered_map:是 Key - Value 的映射关系 (也就是 关键字和键值 之间的映射)

unordered_set:是 Value 的映射关系(也就是只有 键值 的映射)

这两种不同的映射关系从各自的模板参数就能看出来。

1、unordered_map

unordered_map又分为两种,一种是unordered_map,另一种是unordered_multimap。

这两种有什么区别呢?

以unordered_map为例:

这里面的模板参数 Key - T 就是这个容器存储的键值关系。

2、unordered_set

同样:unordered_set又分为两种,一种是unordered_set,另一种是unordered_multiset。

这两个的区别和unordered_map中两个的区别一样。

二、模拟实现哈希表

1、结构

在模拟实现哈希表前,我们要知道整个实现的结构,要有完整性的认识。

这个就是哈希表的大致底层结构。

2、注解hashtable的模板参数:

  • Key:是上层结构(也就是unordered_set、unordered_map)的关键字。
  • T:是存的关键字和键值的映射。

(也就是unordered_set:pair<Key, Key>;unordered_map:pair<Key, Vaule>)

  • Ptr是T*。
  • Ref是T&。
  • KeyofT是一个仿函数,用来取T的Key。因为在这一层也不知道上一层是unordered_set、unordered_map,所以还需要传一个仿函数来进行取值。
  • hashfunc是一个仿函数,用来计算hash映射值。

对于一个整数,怎么存储到哈希表里?需要对其进行hash计算,将这个整数取模于表的大小得到。

对于一个字符串,怎么存储到哈希表里?需要对其进行hash计算,将这个字符串转换成整数,然后将这个整数取模于表的大小得到下标。

如果得到的下标冲突了,就用哈希线性探测法解决冲突。

3、重难点

写完不算难,难的是找bug。

1、二元“!=” 没有找到接收类型的右操作数的运算符

类型不匹配,你可以去你出错的位置看看该类型的模板参数的类型匹不匹配。

2、迭代器的本质、const和非const的区别

迭代器就是模拟的指针的行为。

const迭代器和非const迭代器的区别就是限制其*和->运算符的返回值。

3、类A在类B的上面,想要在类A中使用以类B为类型的变量,怎么办?

在类A的前面加上类B的前置声明即可。

4、类A在类B的上面,想要在类A中使用类B的私有成员,怎么办?

在类B中加上类A的友元。

三、代码

hash.h

#pragma once#include <iostream>
#include <vector>
#include <string>
using namespace std;
enum  State
{Empty,Exist,Delete
};
// hash_func ---> 用来对任意类型进行编码
template<class T>
struct hash_func
{size_t operator()(const T& _val){return (size_t)_val;}
};
// 对string类型进行特化
template<>
struct hash_func<string>
{size_t operator()(const string& val){size_t hash = 0;for (auto e : val){hash *= 131;hash += e;}return hash;}
};// 闭散列 --- 线性探测
// 这里的 T 是 K or pair<K, V>
template <class T>
struct HashNode
{HashNode() {}HashNode(const HashNode&) = default;HashNode(const T& data, const State& state):_data(data),_state(state){}T _data;State _state = Empty;};template <class K, class V, class Ptr, class Ref, class KeyofT, class HashFunc>
class HashTable;template<class K, class T, class Ptr, class Ref, class KeyofT>
struct HashTable_iterator
{typedef HashNode<T> HashNode;typedef HashTable_iterator<K, T, Ptr, Ref, KeyofT> iterator;typedef HashTable<K, T, Ptr, Ref, KeyofT, hash_func<K>> hashtable;HashNode* _node;hashtable* _pht;KeyofT kot;hash_func<K> hs;HashTable_iterator(HashNode* node, hashtable* pht):_node(node), _pht(pht){}bool operator!=(const iterator s){return _node != s._node;}Ref operator*(){return _node->_data;}Ptr operator->(){return &(_node->_data);}iterator& operator++(){int hashindex = 0;for (int i = 0; i < _pht->_tables.size(); i++){// 找到当前节点的位置,寻找下一个节点的位置if (_pht->_tables[i] == _node){hashindex = i;break;}}for (int i = hashindex + 1; i < _pht->_tables.size(); i++){if (_pht->_tables[i] && _pht->_tables[i]->_state == Exist){_node = _pht->_tables[i];return *this;}}_node = nullptr;return *this;}
};template <class K, class V, class Ptr, class Ref, class KeyofT, class HashFunc = hash_func<K>>
class HashTable
{typedef HashNode<V> hashnode;typedef HashTable<K, V, Ptr, Ref, KeyofT, hash_func<K>> hashtable;template<class K, class T, class Ptr, class Ref, class KeyofT>friend struct HashTable_iterator;typedef HashTable_iterator<K, V, Ptr, Ref, KeyofT> iterator;public:hash_func<K> hf;KeyofT kot;HashTable(size_t n = 10):_size(0){_tables.resize(n);}iterator begin(){for (int i = 0; i < _tables.size(); i++){hashnode* cur = _tables[i];if (cur && cur->_state == Exist){// this -> HashTable*return iterator(cur, this);}}return end();}iterator end(){return iterator(nullptr, this);}// V : V or pair<K, V>pair<iterator, bool> insert(const V& e){iterator it = find(kot(e));if (it != end()){return make_pair(it, false);}if (_size * 10 / _tables.size() >= 7){hashtable newt(_tables.size() * 2);for (int i = 0; i < _tables.size(); i ++ ){if (_tables[i]->_state == Exist){newt.insert(_tables[i]->_data);}}_tables.swap(newt._tables);}size_t hashindex = hf(kot(e)) % _tables.size();while (_tables[hashindex] && _tables[hashindex]->_state == Exist){hashindex++;hashindex %= _tables.size();}hashnode* newnode = new hashnode(e, Exist);swap(_tables[hashindex], newnode);delete newnode;_size++;return make_pair(iterator(_tables[hashindex], this), true);}iterator find(const K& key){size_t hashindex = hf(key)% _tables.size();while (_tables[hashindex] && _tables[hashindex]->_state != Empty){if (_tables[hashindex]->_state == Exist&& kot(_tables[hashindex]->_data) == key){return iterator(_tables[hashindex], this);}++hashindex;hashindex %= _tables.size();}return iterator(nullptr, this);}bool erase(const K& key){iterator t = find(key);if (t._node == nullptr) return false;else{t._node->_state = Delete;--_size;}return true;}
private:vector<hashnode*> _tables;size_t _size;
};

unordered_map.h

#pragma once#include "hash.h"template<class K, class V>
class unordered_map
{struct MapKeyofT{const K& operator()(const pair<const K, V>& key){return key.first;}};
public:typedef HashTable<K, pair<const K, V>, pair<const K, V>*, pair<const K, V>&, MapKeyofT, hash_func<K>> HashTable;typedef HashNode<pair<const K, V>> HashNode;typedef HashTable_iterator<K, pair<const K, V>, pair<const K, V>*, pair<const K, V>&, MapKeyofT> iterator;iterator begin(){return _ht.begin();}iterator end(){return _ht.end();}pair<iterator, bool> insert(const pair<const K, V>& kv){return _ht.insert(kv);}iterator find(const K& key){return _ht.find(key);}bool erase(const K& key){return _ht.erase(key);}V& operator[](const K& key){pair<K, V> pkv = make_pair(key, V());pair<iterator, bool> ret = insert(pkv);return ret.first->second;}
private:HashTable _ht;
};//void map01()
//{
//	unordered_map<int, int> s1;
//	vector<int> v = { 1, 8, 34, 5, 3, 4, 8, 1111, 111, 11 };
//
//	for (auto e : v)
//	{
//		s1.insert({e, e});
//	}
//
//	s1.erase(1111);
//	s1.erase(8);
//}
//
//void map02()
//{
//	unordered_map<string, int> s1;
//	vector<string> v = { "1234", "233", "a", "b", "1234", "233", "a", "b" };
//
//	for (auto e : v)
//	{
//		s1.insert({e, 1});
//	}
//
//	s1.erase("1234");
//	s1.erase("8");
//}

unordered_set.h

#pragma once#include "hash.h"template<class K>
class unordered_set
{struct SetKeyofT{const K& operator()(const K& key){return key;}};
public:typedef HashTable<K, K, const K*, const K&, SetKeyofT, hash_func<K>> HashTable;typedef HashNode<K> HashNode;typedef HashTable_iterator<K, K, const K*, const K&, SetKeyofT> iterator;iterator begin(){return _ht.begin();}iterator end(){return _ht.end();}pair<iterator, bool> insert(const K& val){return _ht.insert(val);}iterator find(const K& key){return _ht.find(key);}bool erase(const K& key){return _ht.erase(key);}
private:HashTable _ht;
};//void set01()
//{
//	unordered_set<int> s1;
//	vector<int> v = { 1, 8, 34, 5, 3, 4, 8, 1111, 11, 11};
//
//	for (auto e : v)
//	{
//		s1.insert(e);
//	}
//
//	s1.erase(1111);
//}
//void set02()
//{
//	unordered_set<string> s;
//	vector<string> v = { "1234", "233", "a", "b" };
//
//	for (auto e : v)
//	{
//		s.insert(e);
//	}
//
//	s.erase("1111");
//	s.erase("1234");
//}

谢谢大家!

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Spring -- 使用XML开发MyBatis

T04BF &#x1f44b;专栏: 算法|JAVA|MySQL|C语言 &#x1faf5; 今天你敲代码了吗 文章目录 MyBatis XML配置文件开发配置连接字符串和MyBatis写Mapper层代码添加mapper接口添加UserInfoXmLMapper.xml 操作数据库INSERTDELETE & UPDATE MyBatis XML配置文件开发 实际上,除…

【全面讲解下Docker in Docker的原理与实践】

🌈个人主页: 程序员不想敲代码啊 🏆CSDN优质创作者,CSDN实力新星,CSDN博客专家 👍点赞⭐评论⭐收藏 🤝希望本文对您有所裨益,如有不足之处,欢迎在评论区提出指正,让我们共同学习、交流进步! 👉目录 👉前言👉原理👉实践👉安全和最佳实践👉前言 🦛…

C语言 之 理解指针(4)

文章目录 1. 字符指针变量2. 数组指针变量2.1 对数组指针变量的理解2.2 数组指针变量的初始化 3. 二维数组传参的本质4. 函数指针变量4.1 函数指针变量的创建4.2 函数指针变量的使用 5. 函数指针数组 1. 字符指针变量 我们在前面使用的主要是整形指针变量&#xff0c;现在要学…

阿里云主机 安装RabbitMQ

一、操作系统 用的是Alibaba Cloud Linux release 3 (Soaring Falcon)系统&#xff0c;可以通过命令&#xff1a;lsb_release -a 查看系统信息。 二、安装RabbitMQ RabbitMQ 是基于 Erlang 语言构建的&#xff0c;要安装RabbitMQ&#xff0c;需先安装Erlang环境。通过Erlang V…

小众独立产品推荐 - 独立产品灵感周刊 DecoHack #063

本周刊记录有趣好玩的独立产品设计开发相关内容&#xff0c;每周发布&#xff0c;往期内容同样精彩&#xff0c;感兴趣的伙伴可以 点击订阅我的周刊。为保证每期都能收到&#xff0c;建议邮件订阅。欢迎通过 Twitter 私信推荐或投稿。 &#x1f4bb; 产品推荐 1. Replypulse …

培训第十六天(web服务apache与nginx)

上午 静态资源 根据开发者保存在项目资源目录中的路径访问静态资源html 图片 js css 音乐 视频 f12&#xff0c;开发者工具&#xff0c;网络 1、web基本概念 web服务器&#xff08;web server&#xff09;&#xff1a;也称HTTP服务器&#xff08;HTTP server&#xff09;&am…

备忘录系统

目录 一、 系统简介 1.简介 2需求分析 3 编程环境与工具 二、 系统总体设计 1 系统的功能模块图。 2 各功能模块简介 3项目结构 4 三、 主要业务流程 &#xff08;1&#xff09;用户及管理员登录流程图 &#xff08;2&#xff09;信息添加流程 &#xff0…

信息安全技术解析

在信息爆炸的今天&#xff0c;信息技术安全已成为社会发展的重要基石。随着网络攻击的日益复杂和隐蔽&#xff0c;保障数据安全、提升防御能力成为信息技术安全领域的核心任务。本文将从加密解密技术、安全行为分析技术和网络安全态势感知技术三个方面进行深入探讨&#xff0c;…

基于Java的微博传播分析系统的设计与实现

1 项目介绍 1.1 摘要 本文致力于展示一项创新的微博传播分析系统设计与应用研究&#xff0c;该系统基于Java技术&#xff0c;巧妙利用大数据环境下的社交媒体——微博的庞大用户群及高度活跃特性&#xff0c;旨在深度探索信息传播的内在逻辑与社会影响机制。研究开篇明确定了…

2024非常全的接口测试面试题及参考答案-软件测试工程师没有碰到算我输!

一、前言 接口测试最近几年被炒的火热了&#xff0c;越来越多的测试同行意识到接口测试的重要性。接口测试为什么会如此重要呢&#xff1f; 主要是平常的功能点点点&#xff0c;大家水平都一样&#xff0c;是个人都能点&#xff0c;面试时候如果问你平常在公司怎么测试的&#…

设计模式 之 —— 单例模式

目录 什么是单例模式&#xff1f; 定义 单例模式的主要特点 单例模式的几种设计模式 1.懒汉式&#xff1a;线程不安全 2.懒汉式&#xff1a;线程安全 3.饿汉式 4.双重校验锁 单例模式的优缺点 优点&#xff1a; 缺点&#xff1a; 适用场景&#xff1a; 什么是单例模…

微前端概念

微前端作用 大型应用程序的拆分独立的前端子应用降低程序复杂性&#xff0c;提高开发效率 微前端能力 js隔离css隔离元素隔离生命周期预加载数据通信应用跳转多层嵌套… 微前端实现方案 IframeSingle-spaQiankunMicro-app Iframe <iframe src"https://www.examp…

684.美的集团六三二项目流程变革框架整体规划方案132页PPT

读者朋友大家好&#xff0c;最近有会员朋友咨询晓雯&#xff0c;关于集团公司流程变革框架整体规划的问题&#xff0c;晓雯查找到一份《美的集团632项目流程变革框架整体规划方案》&#xff0c;下面是部分内容分享&#xff0c;欢迎大家下载学习。 知识星球APP搜索【战略咨询文…

基于CentOS Stream 9平台安装JDK17.0.12

官方&#xff1a; https://www.oracle.com/java/technologies/downloads/#java17 1. 下载&#xff1a; https://download.oracle.com/java/17/latest/jdk-17_linux-x64_bin.tar.gz 2. 存放目录 mkdir /usr/local/javacd /usr/local/java3. 解压 tar -zxvf jdk-17_linux-x64_…

JQuery异步请求与Flask后端通信、this和event指针汇总

目录 一.JQuery与Flask通信的三种方法 1.1$.ajax() 1.2$.get() 1.3$.post() 二.forEach()方法 三.this指针 3.1为什么要用this指针 3.2this的指向 3.3this指针的四种绑定方式 3.3.1默认绑定 3.3.2隐式绑定 3.3.3显式绑定 3.3.4new绑定 3.3.5通过标签调用this指针…

【云原生】Kubernetes中crictl的详细用法教程与应用实战

✨✨ 欢迎大家来到景天科技苑✨✨ &#x1f388;&#x1f388; 养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; &#x1f3c6; 作者简介&#xff1a;景天科技苑 &#x1f3c6;《头衔》&#xff1a;大厂架构师&#xff0c;华为云开发者社区专家博主&#xff0c;…

Glyph-ByT5-v2多语言高精度文字海报

微软亚洲研究院、清华大学、北京大学、利物浦大学联合推出渲染高视觉美感文本惊艳海报&#xff0c;效果媲美DALL-E3支持10种不同语言的准确视觉文本渲染项目仓库&#xff1a;https://github.com/AIGText/Glyph-ByT5i68爱六八,链接你我他&#xff1a;https://i68.ltd

基于物联网的区块链算力网络,IGP/BGP协议

目录 基于物联网的区块链算力网络 IGP/BGP协议 IGP(内部网关协议) BGP(边界网关协议) 内部使用ISP的外部使用BGP的原因 一、网络规模和复杂性 二、路由协议的特性 三、满足业务需求 四、结论 基于物联网的区块链算力网络 通 过 多个物联网传感器将本地计算…

鸿蒙HarmonyOS开发:@Observed装饰器和@ObjectLink装饰器:嵌套类对象属性变化

文章目录 一、装饰器二、概述三、限制条件四、装饰器说明五、Toggle组件1、子组件2、接口3、ToggleType枚举4、事件 六、示例演示1、代码2、效果 一、装饰器 State装饰器&#xff1a;组件内状态Prop装饰器&#xff1a;父子单向同步Link装饰器&#xff1a;父子双向同步Provide装…

Windows10安装CMake图文教程

CMake是一个跨平台的开源构建工具&#xff0c;用于管理软件构建过程。CMake允许开发人员使用简单的语法来描述项目的构建过程&#xff0c;而无需直接处理特定于操作系统或编译器的细节。开发人员可以编写CMakeLists.txt文件来指定项目的源文件、依赖项和构建规则&#xff0c;然…