散列表(哈希表)

散列表(hashtable)是基于“开链的做法”。本质是一个vector和每个vector成员维护一个link list,同时vector只存放指针。需要注意针对这个list搜索只能一种线性操作。
hashtable 由桶子(buckets)(本质是vector)和 节点(node)(本质是list)组成。

其中该list 是单向,只有往后的迭代器。

hashtable 的数据结构

所以:buckets 就是桶子,node就是节点。 buckets只存放部分个(即只头部指针)元素数据。

其中node 结构定义: 

 迭代器结构

 

 迭代器永远维系着与整个buckets vector 的关系,并记录当前所指的节点。

hashtable 创建、销毁的内存管理 

  默认也是居于alloc进行分配

1.创建节点。适配器分配内存,并调用构造函数初始化 

2.删除节点:调用析构函数,并释放内存。

 在hashtable构造函数时,对成员变量buckets的默认 初始化内存大小的依据是接近某数并大于某数的质数。后续增加元素,如果其总数大于现在的内存的大小,需要重新调整这个vector的大小,也就需要重新对所有节点重新归属在哪个bucket上

插入元素与表格重整

如果插入元素后,其总数大于原整个vector的大小。就需要重整。重整意味着重新创建新的vector。重新对原来的节点进行归属哪个bucket上(依据于取模运算。value%vector的大小)

在这里使用的是bkt_num函数,就是取模运算。

1.插入的元素不允许重复

插入位置位于list的头部

 2.插入的元素允许重复

重复的数据插入和其值相同元素的后面

 整体删除元素(clear)

1.vector内容设置为空指针,不释放内存空间。

2.所有节点的内存释放掉。

复制(copy_from) 

  

散列函数的构造方法

1.除留余数法

通常:若 散列表长为m,p为大于或等于表长(最好接近m)的最小质数。

2.直接定址法

3.数字分析法

4.平分取中法

5.折叠法

6.随机数法

处理散列冲突的办法:

1.链地址法(也叫分离链接法

就是最上面说的“开链”思维,使用链表的方式连接落在同一地址的数据,缺点是如果同一地址上的链表数据很多,遍历链表的时间就长,整体的散列表性能就变差。所以在大于0.7的填充因子后,就需要重新调整散列表。调整的办法是:再散列

2.开放定址法

3. 再散列函数法

4.公共溢出区法

其他:

1.散列表可用于缓存数据(如web服务器上)

2.散列表适合用于防止重复。

自定义散列表

//自定义散列表
template <class T>
class HashTable
{
public:
	HashTable(int size = 101);
	~HashTable();
	bool insert(const T &value);
	bool remove(const T &value);
	bool contains(const T &value);
	void makeempty();

private:
	vector<list<T>> m_vecList;
	int m_nCurrentSize;

	size_t hashfunc(const T &value);//散列函数
	void rehash();

};

template <class T>
void HashTable<T>::makeempty()
{
	for (auto & list : m_vecList)
	{
		list.clear();		
	}
	m_vecList.clear();
	m_vecList.shrink_to_fit();
}




	HashTable<int> ht;
	for (int i = 0; i < 20; i++)
	{
		ht.insert(i);
	}
	HashTable<std::string> ht2;
	for (int i = 0; i < 20; i++)
	{
		std::string a = std::to_string(i);
		ht2.insert(a);
	}










//分离链接散列表的再散列
template <class T>
void HashTable<T>::rehash()
{
	vector<list<T>> oldvec = m_vecList;
	int isize = m_vecList.size();
	m_vecList.resize(isize*2);
	for (auto &list : m_vecList)
	{
		list.clear();
	}
	m_nCurrentSize = 0;
	for (auto &oldlist : oldvec)
	{
		for (auto &x : oldlist)
		{
			insert(std::move(x));
		}
	}

}

template <class T>
size_t HashTable<T>::hashfunc(const T &value)
{
	static hash<T> hf;
	size_t size = hf(value);
	size %= m_vecList.size();
	return size;
}

//在c++11中,散列函数通过函数对象模板表示
//template<class T>
//class hash
//{
//public:
//	size_t operator()(const T &key) {}
//};

//如string
//template<>
//class hash<std::string>
//{
//public:
//	size_t operator()(const std::string &key) 
//	{
//		size_t hashval = 0;
//		for (char ch : key)
//		{
//			hashval = 37 * hashval + ch;
//		}
//		return hashval;
//	}
//};


template <class T>
bool HashTable<T>::contains(const T &value)
{
	auto &whichlist = m_vecList[hashfunc(value)];
	return find(whichlist.begin(), whichlist.end(), value) != whichlist.end();

}

template <class T>
bool HashTable<T>::remove(const T &value)
{
	auto &whichlist = m_vecList[hashfunc(value)];
	auto iter = find(whichlist.begin(), whichlist.end(), value);
	if (iter == whichlist.end())
	{
		return false;
	}
	whichlist.erase(iter);
	m_nCurrentSize--;
	return true;
}

template <class T>
bool HashTable<T>::insert(const T &value)
{
	auto &whichlist = m_vecList[hashfunc(value)];
	if (find(whichlist.begin(),whichlist.end(), value) != whichlist.end())
	{
		return false;
	}
	whichlist.push_back(value);
	m_nCurrentSize++;
	if (m_nCurrentSize >= m_vecList.size())
	{
		rehash();
	}

	return true;
}

template <class T>
HashTable<T>::~HashTable()
{
	makeempty();
}

template <class T>
HashTable<T>::HashTable(int size /*= 101*/)
{
	m_vecList.resize(size);
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值