首页 > 其他分享 >【散列】散列表HashTable分离链接法类模板的实现

【散列】散列表HashTable分离链接法类模板的实现

时间:2022-10-18 09:03:34浏览次数:80  
标签:hashVal return 法类 whichList HashTable theLists const 散列 size

分离链接法(separate chaining),做法是将散列到同一个值得所有元素保留到一个链表List中。如果这个元素是个新的元素,那么它将被插入到链表的前端

插入前端的原因是:

常常发生这样的事实:新近插入的元素最有可能不久又被访问。

假设关键字是前10个完全平方数并设散列函数就是 hash(x)=x mod 10,则最后得到的分离链接散列表为下图所示。

定义散列表的装填因子λ为散列表中的元素个数对该表大小的比,分离链接散列表的一般法则是让表的大小大致与预料的元素个数差不多(即λ ≈ 1)。

实现代码:

#include<list>
#include<vector>
#include"UniversalHash.h"

//分离链接散列表的类型声明
template<typename HashedObj>
class HashTable {
private:
	std::vector<std::list<HashedObj>>theLists; //链表的数组
	int currentSize;

	void rehash() 
	{
		std::vector<std::list<HashedObj>> oldLists = theLists;

		//创建两倍大的空表
		theLists.resize(nextPrime(2 * theLists.size()));
		for (auto& thisList : theLists)
			thisList.clear();

		//复制整个表
		currentSize = 0;
		for (auto& thisList : oldLists)
			for (auto& x : thisList)
				insert(std::move(x));
	}
	size_t myhash(const HashedObj& x)const
	{
		static _hash<HashedObj>hf;
		return hf(x) % theLists.size();
	}

public:
	explicit HashTable(int size = 101) :theLists{ nextPrime(101) } {
		makeEmpty();
	}

	bool contains(const HashedObj& x)const
	{
		auto& whichList = theLists[myhash(x)];
		return find(begin(whichList), end(whichList), x) != end(whichList);
	}

	void makeEmpty()
	{
		for (auto& thisList : theLists)
			thisList.clear();
	}

	bool insert(const HashedObj& x)
	{
		auto& whichList = theLists[myhash(x)];
		if (find(begin(whichList), end(whichList), x) != end(whichList))
			return false; //重复元
		whichList.push_back(x);

		//再散列
		if (++currentSize > theLists.size())
			rehash();
		return true;
	}

	bool insert(HashedObj&& x)
	{
		auto& whichList = theLists[myhash(x)];
		if (find(begin(whichList), end(whichList), x) != end(whichList))
			return false; //重复元
		whichList.push_back(std::move(x));

		//再散列
		if (++currentSize > theLists.size())
			rehash();
		return true;
	}

	bool remove(const HashedObj& x)
	{
		auto& whichList = theLists[myhash(x)];
		auto itr = find(begin(whichList), end(whichList), x);
		if (itr == end(whichList))
			return false;
		whichList.erase(itr);
		--currentSize;
		return true;
	}
};

头文件UniversalHash.h:

#pragma once
#include<string>
//通用散列的简单实现
/*
* x代表要哈希的对象,m代表TableSize,a、b是随机选出的
* 散列函数簇 H={H(x)=((ax+b)mod p)mod m,其中1<=a<=p-1,0<=b<=p-1}是通用的。
*/
int universalHash(int x, int a, int b, int p, int m)
{
	return static_cast<int>(((static_cast<long long>(a) * x) + b) % p) % m;
}


/*
* 形如2^n-1的素数叫Mersenne素数,如2^5-1、2^61-1、2^31-1等,
* 涉及Mersenne素数的模运算可以通过一次移位和一次减法实现
*/
const int DIGS = 31;
const int mersennep = (1 << DIGS) - 1;

int universalHash(int x, int a, int b, int m)
{
	long long hashVal = static_cast<long long>(a) * x + b;
	
	hashVal = ((hashVal >> DIGS) + (hashVal & mersennep));
	if (hashVal >= mersennep)
		hashVal -= mersennep;
	return static_cast<int>(hashVal) % m;
}

/**
 * 判断素数
 */
bool isPrime(int n)
{
    if (n == 2 || n == 3)
        return true;

    if (n == 1 || n % 2 == 0)
        return false;

    for (int i = 3; i * i <= n; i += 2)
        if (n % i == 0)
            return false;

    return true;
}

/**
 * 获取下一个比b大的素数
 * Assumes n > 0.
 */
int nextPrime(int n)
{
    if (n % 2 == 0)
        ++n;

    for (; !isPrime(n); n += 2)
        ;

    return n;
}

//使用函数对象,让散列函数只用对象作为参数并返回适当的整形量
template<typename T>
class _hash
{
public:
	size_t operator()(const T& key) {
		size_t hashVal = 0;

		T tmp = key;
		while (tmp > 0) {
			hashVal = 37 * hashVal + tmp % 10;
			tmp /= 10;
		}
		return hashVal;
	}
};

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;
	}
};

标签:hashVal,return,法类,whichList,HashTable,theLists,const,散列,size
From: https://www.cnblogs.com/awst-lee/p/16801382.html

相关文章

  • 【散列】散列表HashTable
    1.概念散列表,又叫哈希表(HashTable),是能够通过给定的关键字的值直接访问到具体对应的值的一个数据结构。散列表的实现常常叫做散列(hashing),散列是一种用于以常数平均......
  • 数据结构与算法分析——C语言描述(第5章 散列)
    目录5.1一般想法5.2散列函数5.3分离链接法(separatechaining)5.4开放定址法(openaddressing)本章讨论散列表(hashtable)ADT,不过它只支持二叉查找树所允许的一部分......
  • 散列表(中)
    目录如何设计散列函数?如何根据装载因子动态扩容如何选择散列冲突解决方法开放寻址法链表法如何设计一个工业级的散列函数散列表的查询效率并不能笼统地说成是O(1)。它跟......
  • Harsh =哈希 =散列
     key-hash-%-index Harsh=哈希=散列HarshCode=哈希码=哈希代码=散列码=散列值哈希函数=散列函数=哈希算法=HarshAlgorithm散列表=Hashtable=哈希表散列冲突=......
  • HashMap 和 Hashtable 有什么区别?
    存储:HashMap运行key和value为null,而Hashtable不允许。线程安全:Hashtable是线程安全的,而HashMap是非线程安全的。推荐使用:在Hashtable的类注释可以看到,Hash......
  • 面经-HashTable与ConcurrentHashMap比较
    HashTable与ConcurrentHashMap比较1.HashTable与ConcurrentHashMap都是线程安全的Map集合。2.HashTable与ConcurrentHashMap的键和值都不能为空。3.HashTable并发度低,整......
  • HASH 散列的一些概念
    1.散列函数(hashfunction)即关键字到表中单元的映射,key->tablePlace,理想情况下,应是一一映射。2.冲突(collision)即不同的关键字散列到同一单元的情况。因为关键字基本上是......
  • Android生成密钥散列
    接入facebook登录和分享时需要在facebook后台添加密钥散列,下面是生成方式第一种(简单,准确)记住要用相应的签名文件进行签名哦try{PackageInfoinf......
  • Redis---hash哈希散列
    1.前言Redishash(哈希散列)是由字符类型的field(字段)和value组成的哈希映射表结构(也称散列表),它非常类似于表格结构。在hash类型中,field与value一一对应,且不允许重......