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

news2024/9/9 0:33:35

个人主页:仍有未知等待探索-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.coloradmin.cn/o/1958464.html

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

相关文章

【计算机毕设论文】基于SpringBoot线上学习平台的设计与实现

&#x1f497;博主介绍&#xff1a;✌全平台粉丝5W,高级大厂开发程序员&#x1f603;&#xff0c;博客之星、掘金/知乎/华为云/阿里云等平台优质作者。 【源码获取】关注并且私信我 感兴趣的可以先收藏起来&#xff0c;同学门有不懂的毕设选题&#xff0c;项目以及论文编写等相…

「数组」实现动态数组的功能(C++)

概述 动态数组&#xff0c;顾名思议即可变长度的数组。数组这种数据结构的实现是在栈空间或堆空间申请一段连续的可操作区域。 实现可变长度的动态数组结构&#xff0c;应该有以下操作&#xff1a;申请一段足够长的空间&#xff0c;如果数据的存入导致空间已满&#xff0c;则…

CentOS7安装最新版vim;vim自动补齐配置

想练习一会vim&#xff0c;结果发现敲代码没有空号自动补齐和缩进很难受&#xff0c;所以想配置一下。 配置vim&#xff1a; 可以通过 vim ~/.vimrc 来给 vim 加启动的设定&#xff08;比如set nu&#xff0c;这样就会在每次启动的时候都加上行号。当然过程中可以在底行模式输…

基于SpringBoot+Vue的学生考勤管理系统(带1w+文档)

基于SpringBootVue的学生考勤管理系统(带1w文档) 系统为了数据库结构的灵活性选择MySQL来设计&#xff0c;而java技术&#xff0c;B/S架构则保证了较高的平台适应性。本文主要介绍了系统开发背景&#xff0c;需要完成的功能与开发过程&#xff0c;说明系统设计重点与设计思想。…

如何在linux系统中用conda安装R环境及R包

一、miniconda3的安装不再赘述 二、安装R环境 1. 提前准备好conda的R单独环境 conda env list #查看已有环境 查看R的最新版本&#xff1a;r-project ##创建环境和激活环境 conda create -n R4.4.1 conda activate R4.4.1 备注&#xff1a;激活环境Linux&#xff0c;OS X…

企业如何保证公司内网安全

1. 加强网络安全防护 部署防火墙和入侵检测系统&#xff1a;作为内网安全的第一道防线&#xff0c;防火墙和入侵检测系统能够有效阻止外部攻击和恶意软件的入侵。 数据加密&#xff1a;采用先进的加密技术保护敏感数据&#xff0c;确保数据在传输和存储过程中的安全性。 访问…

【Android面试八股文】荣耀面试算法题: 输出一个给定的字符串的最长回文子序列及其长度!

文章目录 一、真题链接二、如何解决2.1算法思路2.2 算法步骤2.3 Java算法实现 一、真题链接 还好我以前刷过这道题&#xff0c; 其实题目就是LeetCode的 516.最长回文子序列&#xff0c; 地址&#xff1a;https://leetcode.cn/problems/longest-palindromic-subsequence/des…

哈希表专题

题解之前&#xff1a; 1.有关unordered_map的count功能&#xff1a;查询key&#xff01; Leetcode 1.两数之和 解题思路&#xff1a; class Solution { public:vector<int> twoSum(vector<int>& nums, int target) {vector<int> res;// key:具体的数值(便…

AI需求海量涌现,Scaleway如何用Spectrum-X 网络从容应对?

“没有好网络&#xff0c;别玩AIGC。” 如今&#xff0c;随着AI需求的大量涌现&#xff0c;越来越多用户意识到网络在AI集群中的重要性。一个超大规模、超高带宽、超强可靠的网络&#xff0c;可以为AI训练提供强有力支撑&#xff0c;从而节约训练成本、缩短训练时间&#xff0…

VBA快速对比数据行

实例需求&#xff1a;对于存在多行数据&#xff08;示例中为双行&#xff09;的项目&#xff0c;对比同一个项目的每列数据&#xff0c;高亮显示数据不同的单元格。 示例代码如下。 Function GetDiff(ByRef rng1 As Range, ByRef rng2 As Range) As RangeDim i As LongFor i …

dpdk发送udp报文

dpdk接收到udp报文后&#xff0c;自己构造一个udp报文&#xff0c;将收到的报文中的源mac&#xff0c;目的mac&#xff0c;源ip&#xff0c;目的ip&#xff0c;源端口和目的端口交换下顺序填充到新的udp报文中&#xff0c;报文中的负载数据和收到的udp保持一致。 注&#xff1…

CATIA V5R21安装包下载及图文安装教程

大家好&#xff0c;今天给大家分享下catia安装教程 注意安装前请退出杀毒软件&#xff0c;防止误报影响安装进程 下载链接&#xff1a;百度网盘 请输入提取码 提取码&#xff1a;ypc6 01 在电脑D盘新建文件夹命名为CATIA,将下载的软件压缩包放置在该文件夹。 鼠标右击【C…

进行良好的文献综述能否提高学术研究的可信度

VersaBot一键生成文献综述 进行良好的文献综述 对于从多个方面提高学术研究的可信度至关重要&#xff1b; 1. 展示专业知识&#xff1a; 全面的回顾表明您对您所在领域的现有知识和相关理论有深入的了解。这将使您成为权威&#xff0c;并将您的研究置于更广泛的背景下。 2.…

初步入门C ++之类的概念

文章目录 0 Hello World!1 编译过程2 类2.1 类的概念2.2 构造函数与析构函数 0 Hello World! #include <iostream> //相当于#include <stdio.h>int main(int argc, char argv[]) {char c;std::cout << "Hello World!\n" <<…

入门 PyQt6 看过来(案例)14~ 分组

本文分享一个分组框功能&#xff0c;采用pyqt6里的QGroupBox​控件&#xff0c;效果如下&#xff1a;性别和专业分开为两个分组框内&#xff1a; ​ 1 功能实现思路 ui页面布局设计 性别和专业要设计成两个分组框&#xff1a; ​ 逻辑实现 引入信号和槽函数来实现点击单选…

Cybersecurity ASPICE实施策略-基于ISO/SAE 21434-亚远景科技

近几年&#xff0c;随着软件定义汽车和汽车的智能化和网联化&#xff0c;使得汽车融合了现代通信与网络通信技术&#xff0c;实现了车与人、车与车、车与道路、车与云端等智能信息交互和共享&#xff0c;也让车具备了环境感知、协同控制、智能决策等功能&#xff1b;与此同时&a…

构建可定制的表情选择器组件

你好呀&#xff0c;我是小邹。 概述 在当今的交互式Web应用中&#xff0c;表情符号&#xff08;Emoji&#xff09;已成为一种流行的沟通方式。为了提升用户体验并简化开发流程&#xff0c;本教程将引导您如何构建一个可高度定制的表情选择器组件。此组件将允许用户在Web表单中…

力扣621.任务调度器

力扣621.任务调度器 桶思想当桶放不满时 答案为桶面积 maxcount(最后一行) (max - 1)(n1)当桶放的满时 答案为任务总数 tasks.size()最终两者取大即可 class Solution {public:int leastInterval(vector<char>& tasks, int n) {int len tasks.size();vector<…

QT--聊天室

一、设计要求 用QT做一个聊天室&#xff0c; 制作一个服务器和客户端。可以进行注册、登录&#xff0c; 登陆成功后可以使用昵称进行发送、接收消息。 能根据昵称、聊天内容查询历史记录&#xff0c;也可以查询全部聊天记录。 。 二、客户端三级ui界面 三、项目代码 //在…

测试用例:确保软件质量的基石

大家好&#xff0c;我是一名测试开发工程师&#xff0c;已经开源一套【自动化测试框架】和【测试管理平台】&#xff0c;欢迎大家联系我&#xff0c;一起【分享测试知识&#xff0c;交流测试技术】 在当今这个数字化时代&#xff0c;软件已经成为人们日常生活、工作和学习中不可…