当前位置: 首页 > news >正文

哈希表(闭散列)的实现

目录

概念及定义

闭散列的介绍

闭散列底层实现

哈希表的定义

哈希表的构造

哈希表扩容

哈希表插入

哈希表删除

哈希表查找


概念及定义

哈希表,也成为散列表,在C++中unordered_set和unordered_map的底层实现依靠的都是哈希表。

map和set的底层是红黑树。unordered_set和unordered_map的底层是哈希表,unordered说明其数据的存储时没有顺序的。

哈希表是通过将插入数据的大小和位置建立联系,从而缩短查找数据的时间。

哈希表在进行数据的删除,查找,插入的时候不需要进行循环,其时间复杂度平均是O(1)。但是实际在使用的时候,其效率与红黑树差不多。

常见的哈希函数有:直接定值法,除留余数法,平方取中法....

此篇博客将会采用除留余数法进行哈希表的实现。

哈希表底层使用数组实现,除留余数法:将插入的整数模上数组大小得到的余数就是其对应的下标位置。

哈希冲突:对于哈希表来说必定会出现不同的数据映射到相同位置,值和位置就出现了多对一的关系,将这种现象称为哈希冲突。

解决哈希冲突的方法:1)闭散列(线性探测,二次探测);2)开散列(链地址法);3)多次散列。

此篇文章将对闭散列的实现方式进行分析。

闭散列的介绍

闭散列,也叫开放定值法,当出现哈希冲突的时候,即需要插入数据的位置被占用时,按规则去找下一个空位置,即去占别人的位置。

可以看到插入44的时候,下标为4的位置已经被占的,所以先后找空位置进行插入。

闭散列底层实现

依据除留余数法来确定数据的地址,再用开放定址法解决哈希冲突。

通过除留余数法得出数据对应的插入位置,如果该位置已经被占用,则先后找,直到找到空位置后停止。同理在查找数据是否存在的时候,也是先找到对应位置,如果不是再向后查找,直到找到或到空位置停止。

思考:在查找数据时,如果一个数据被删除,则该位置是空,那还继续向后查找吗???

如图,当将5删除后,查找44时,到5位置为空就停止了,不再继续往后走了。这是不符合预期的,所以对每个数据要有三个状态:存在数据,不存在数据,数据被删除。

哈希表的定义

哈希表底层的数组,此处直接使用vector来代替。

enum STATE
{
	EXIST,    //存在数据
	DELETE,   //数据被删除
	EMPTY    //不存在数据
};

template<class K,class V>
struct HashDate
{
	pair<K, V> _kv;   //存放数据
	STATE _state;      //存储状态
};

template<class K, class V>
class HashTable
{
	typedef HashDate<K, V> HashDate;


private:
	vector<HashDate> _table;
	size_t _n;    //存储数组中有效个数
};

哈希表的构造

使用vector的resize函数为vector开初始空间;实现HashDate的构造,在每次插入新数据的时候,其HashDate的状态都是EXIST存在。


template<class K,class V>
struct HashDate
{
    //默认构造函数
    HashDate()
	    :_state(EMPTY)
    { }

	HashDate(const pair<K,V>& kv)   //构造函数
		:_kv(kv)
		,_state(EXIST)
	{ }
}


template<class K, class V>
class HashTable
{
	typedef HashDate<K, V> HashDate;
public:
	HashTable()
	{
		_table.resize(10);   //vector初始有10个数组空间
		_n = 0;
	}
}

哈希表扩容

当对哈希表插入数据,发生哈希冲突时,要向后找空位置进行插入。所以如果数组空间比较满就会出现一直向后找空位置的现象,这也导致插入效率降低。

所以哈希表不能太满,太满会导致查找和插入的效率降低。当哈希表满时,需要对哈希表进行及时扩容。

哈希表中引入载荷因子来控制哈希表是否扩容。

载荷因子 = 填入数据/哈希表的长度。

对于开放开放定址法:载荷因子需要控制在0.7~0.8左右。此处在代码实现时,我们会严格控制在0.7以下。

当对哈希表进行扩容时,也就意味着数据的映射位置会发生改变。原本冲突的数据可能扩容后不再冲突,原本不冲突的数据扩容后可能会出现冲突。所以在每次扩容时,需要对数据进行重新插入。

//对哈希表进行扩容
void More()
{ 
	//此处需要强转为double类型,否则无法得到小数
	if ((double)_n / _table.size() >= 0.7)
	{
		//进行扩容
		//采用创建新的哈希表的方法
		HashTable newtable;
		newtable._table.resize(_table.size() * 2);  //扩容时,直接扩为原数据的2倍
		//将数据进行插入
		for(auto e:_table)
		{
			newtable.Insert(e);
		}
		//将两个数组进行交换
		_table.swap(newtable._table);
	}
}

哈希表插入

bool Insert(const pair<K, V> kv)
{
	//判断是否需要扩容
	More();

	size_t hashi = kv.first % _table.size();
	while (_table[hashi]._state == EXIST)
	{
		if (_table[hashi]._kv.first == kv.first)
		{
			//哈希表中已经存在,不能再插入
			return false;
		}
		++hashi;
		hashi = hashi % _table.size();
	}
	//找到空位置,进行插入
	_table[hashi] = kv;
    _n++;
	return true;
}

哈希表删除

哈希表的删除:在找到数据后,世界将该数据的状态改变即可,将EXIST改为DELETE。

//删除
bool Erase(const K& key)
{
	size_t hashi = key % _table.size();
	while (_table[hashi] == EXIST)
	{
		if (_table[hashi]._kv.first == key)
		{
			//找到了,将该位置进行删除,直接改变状态即可
			_table[hashi]._state = DELETE;
			return true;
		}

		++hashi;
		hashi = hashi % _table.size();
	}
	//没找到
	return false;
}

哈希表查找

根据开头分析:哈希表的查找,在遇到DELETE和EXIST状态的数据时,均不停止,在遇到EMPTY状态的时候才会停止。

bool Find(const K& key)
{
	size_t hashi = key % _table.size();
	while (_table[hashi]._state != EMPTY)
	{
		if (_table[hashi]._kv.first == key && _table[hashi]._state == EXIST)
		{
			return true;
		}
		++hashi;
		hashi = hashi % _table.size();
	}
	//没找到
	return false;
}

到此哈希表的基本功能已经实现,但是会发现:但是存储string类型的时候,string是不能取模的,关于该问题将会在后面《哈希表的封装》中进行详细分析。

相关文章:

  • 匿名函数自调用
  • Draw.io 全面解析与竞品分析:图表绘制工具的深度对比
  • 这是一份简单优雅的Prompt Engineering教程
  • 25.4.6学习总结
  • C++ 中为什么构造函数不需要实现虚函数,而析构函数需要?
  • 线程同步的学习与应用
  • 设计模式简述(八)中介者模式
  • 从扩展黎曼泽塔函数构造物质和时空的结构-15
  • swift-11-init、deinit、可选链、协议、元类型
  • STM32F103C8T6单片机的起始点:使用GPIO输出点亮我们的第一个小灯(HAL库版本)
  • 【简历全景认知】简历的历史演变与当代定位:从羊皮卷到算法博弈的艺术
  • 设计模式简述(十)责任链模式
  • 请问你怎么看待测试,指导哪些测试的类型,有用过哪些测试方法?
  • Nmap全脚本使用指南!NSE脚本全详细教程!Kali Linux教程!(六)
  • 人脸识别系统(人脸识别、前后端交互、Python项目)
  • 初识数据结构——Java集合框架解析:List与ArrayList的完美结合
  • 如何判断JVM中类和其他类是不是同一个类
  • Window进程监控工具,能自动重启进程和卡死检测
  • 【Linux篇】基础IO - 文件描述符的引入
  • Spring 中的 @Autowired 和 @Resource
  • c 怎么做网站/购买链接怎么买
  • 商城网站建设都需要多少钱/网站流量查询站长之家
  • 武汉科技职业学院有哪些专业/seo教学培训
  • dz论坛怎么做视频网站吗/汕头网站设计
  • 网站建设 中企动力/网站seo啥意思
  • 上海做网站/西安seo外包服务