哈希表实现-哈希桶法

ops/2024/11/22 21:08:26/

哈希桶方法

由于直接定值法实现哈希表有着明显的弊端——如果多个节点的hash值相同会往后堆积,所以衍生出哈希桶方法

我们的哈希表设置成一个结点指针数组,每个哈希值对应的是一串链表,形状就像一个一个的桶我们就会把hash值相同的节点放到一起,不会出现往后堆积的问题而且还能一定程度解决频繁扩容的问题

节点定义

由于哈希桶是一个一个的链表,所以节点我们需要一个_next指针来形成链表

template<class Val_type>
struct HashNode
{HashNode<Val_type>* _next;Val_type _data;HashNode(const Val_type& data):_next(nullptr), _data(data){}
};

成员/成员函数

 构造函数:这里默认先开十个节点的数组

析构函数:我们需要将每个桶中的每个节点都释放掉,所以需要一个一个的遍历,一个节点接着一个节点的释放就可以了(最后滞空) 

成员:需要一个vector<Node*> 用来控制整个哈希表用一个_n 来维护整个哈希表节点的个数(插入节点++ 删除节点--) 

template<class Key_type, class Val_type, class KeyOfT, class Hash>
class __Hashiterator
{typedef HashNode<Val_type> Node;
public:HashTable(){_table.resize(10,nullptr);//开十个空间_n = 0;}~HashTable(){for (size_t i = 0; i < _table.size(); i++){Node* cur = _table[i];while (cur){Node* next = cur->next;//标记下一个节点delete cur;//直接deletecur = next;}_table[i] = nullptr;}}
private:vector<Node*> _table;size_t _n;
};

插入

 首先看看能不能在表中找到该元素,如果表中有要插入的元素,不再进行插入!

Hash仿函数:用来计算哈希值 

KeyOfT仿函数:用来取出Val_type中的key值 

扩容:如果节点数与桶的数量相同则需要扩容

①开一个新表,容量扩成原来的二倍

②遍历旧表,一个一个的将节点插入新表中的对应桶中

③将新表与旧表交换(这样 一但出函数作用域,就表就会销毁,新表代替掉旧表) 

插入节点:这里直接头插就可以了(方便快捷,时间O(1))。 

 ①:

 

bool insert(const Val_type& val)
{KeyOfT kot;if (Find(kot(val)))return false;Hash hs;if (_n == _table.size())//扩容{vector<Node*>newTable(_table.size() * 2, nullptr);for (size_t i = 0; i < _table.size(); i++){Node* cur = _table[i];//转移hash桶while (cur){Node* next = cur->_next;size_t hashi = hs(kot(cur->_data)) % _table.size();cur->_next = newTable[hashi];newTable[hashi] = cur;cur = next;}_table[i] = nullptr;}_table.swap(newTable);}size_t hashi = hs(kot(val)) % _table.size();Node* newnode = new Node(val);//开新节点//头插newnode->_next = _table[hashi];_table[hashi] = newnode;++_n;return true;
}

搜索

确定要找的节点的哈希值

到该桶里找到该节点

返回节点

Node* Find(const Val_type& val)
{KeyOfT kot;Hash hs;size_t hashi = hs(kot(val)) % _table.size();Node* cur = _table[hashi];while (cur){if (kot(cur->_data) == val)return cur;cur = cur->_next;}return false;
}

删除

找到对应的桶

在桶中找到该节点

删除该节点 

bool erase(const Val_type& val)
{KeyOfT kot;Hash hs;size_t hashi = hs(kot(val)) % _table.size();Node* prev = nullptr;Node* cur = _table[hashi];while (cur)//找到该节点{if (kot(cur->_data) == val)//如果找到{if (prev)//桶中有多个节点{prev->_next = cur->_next;}else//要删除的节点是桶中的第一个节点{_table[hashi] = cur->_next;}delete cur;cur = nullptr;--_n;return true;}prev = cur;cur = cur->_next;}return false;
}

迭代器

 ①我们的迭代器需要知道是哪一个hash表中的迭代器,所以需要一个_ht 来记录指向我们迭代的哈希表

需要维护一个节点指针

template<class Key_type, class Val_type, class KeyOfT, class Hash>
class __Hashiterator
{typedef HashNode<Val_type> Node;typedef __Hashiterator<Key_type,Val_type,KeyOfT,Hash> self;Node* _node;HashTable<Key_type, Val_type, KeyOfT, Hash> _ht;__Hashiterator(Node* node, HashTable<Key_type, Val_type, KeyOfT, Hash>* ht):_node(node),_ht(ht){}
};

重载: 这里主要分析一下operator++

分情况:如果下一个节点是空就需要去下一个右节点的桶中找节点

              如果下一个节点不是空,转移到下一个节点就可以了

Val_type& operator*()
{return _node->_data;
}self& operator++()
{if (_node->next)//当前位置后边还有节点{return _node = _node->_next;}else//换桶{KeyOfT kot;Hash hs;size_t hashi = hs(kot(_node->_data)) % _ht->_table.size();//找下一个桶hashi++;while (hashi < _ht->_table.size()){if (_ht->_table[hashi])//当前桶里有节点,访问这个桶,没节点继续找到有节点的桶{_node = _ht->_table[hashi];break;}}if (hashi == _ht->_table.size())//后边没有桶了{_node = nullptr;}}return *this;
}bool operator!=(const self& s)
{return _node != s._node;
}bool operator== (const self& s)
{return _node == s._node;
}

 begin/end

如果容器没有begin函数和end函数,就不能支持C++11 中的范围for功能

begin只要找到第一个不为空的桶就可以了

iterator begin()
{for (size_t i = 0; i < _table.size(); i++){if (_table[i] != nullptr){return iterator(_table[i], this);}}return end();//如果全部都是空桶
}iterator end()
{return iterator(nullptr, this);
}

源码地址


http://www.ppmy.cn/ops/32470.html

相关文章

PHP 反序列化

一、PHP 序列化 1、对象的序列化 <?php class people{public $nameGaming;private $NationLiyue;protected $Birthday12/22;public function say(){echo "老板你好呀&#xff0c;我是和记厅的镖师&#xff0c;叫我嘉明就行&#xff0c;要运货吗你&#xff1f;"…

正点原子[第二期]Linux之ARM(MX6U)裸机篇学习笔记-12-蜂鸣器

前言&#xff1a; 本文是根据哔哩哔哩网站上“正点原子[第二期]Linux之ARM&#xff08;MX6U&#xff09;裸机篇”视频的学习笔记&#xff0c;在这里会记录下正点原子 I.MX6ULL 开发板的配套视频教程所作的实验和学习笔记内容。本文大量引用了正点原子教学视频和链接中的内容。…

初识C语言——第九天

ASCII定义 在 C 语言中&#xff0c;每个字符都对应一个 ASCII 码。ASCII 码是一个字符集&#xff0c;它定义了许多常用的字符对应的数字编码。这些编码可以表示为整数&#xff0c;也可以表示为字符类型。在 C 语言中&#xff0c;字符类型被定义为一个整数类型&#xff0c;它占…

使用FastGPT+OneAPI在本地使用Llama3

FastGPT 是一个基于 LLM 大语言模型的知识库问答系统&#xff0c;提供开箱即用的数据处理、模型调用等能力。同时可以通过 Flow 可视化进行工作流编排&#xff0c;从而实现复杂的问答场景&#xff01;他的重要特点就是工作流编排。 工作流编排&#xff1a;基于 Flow 模块的工作…

MongoDB详解

目录 一、MongoDB概述 1.MongoDB定义 2.MongoDB主要特点 2.1文档 2.2集合 2.3数据库 2.4数据模型 二、安装MongoDB 1.Windows安装MongoDB 1.1下载MongoDB 1.2安装MongoDB 1.3配置MongoDB 1.3.1可能遇到的问题 1.4安装一盒可视化工具 2.Linux安装MongoDB 2.1下载…

牛客热题:链表中的倒数最后K个节点

&#x1f4df;作者主页&#xff1a;慢热的陕西人 &#x1f334;专栏链接&#xff1a;力扣刷题日记 &#x1f4e3;欢迎各位大佬&#x1f44d;点赞&#x1f525;关注&#x1f693;收藏&#xff0c;&#x1f349;留言 文章目录 牛客热题&#xff1a;链表中的倒数最后K个节点题目链…

【计算机网络】循环冗余校验:Cyclic Redundancy Check

1. 任务目标 利用循环冗余校验&#xff08;CRC&#xff09;检测错误。 循环冗余校验&#xff08;英语&#xff1a;Cyclic redundancy check&#xff0c;通称 CRC&#xff09;是一种根据网上数据包或计算机文件等数据产生简短固定位数校验码的一种散列函数&#xff0c;主要用来…

【重难点算法题】设计哈希集合、哈希映射

文章目录 Tag题目来源解题思路方法一&#xff1a;链地址法 类似题目代码1代码2 写在最后 Tag 【哈希集合】【哈希映射】【链地址法】【数据结构设计】 题目来源 705. 设计哈希集合 解题思路 在解题之前需要先明确两组概念&#xff1a; 哈希表与散列表哈希函数与散列函数 上…