教你实现自己的hash表||链地址法||线性探测法||C++

本文着重介绍一下哈希表的两种实现方式,链地址法和线性探测法(除余留数)。

1.链地址法

我们先来看一下链地址法是怎样解决冲突的。

假如我们要插入一个元素15,15%7 = 1.所以应插入到索引为1的链表。插入后的链表为:

如果我们继续插入一个元素22,22%7=1,所以还插在索引为1的链表,插入后的结果为:

相信大家已经了解了链地址法到底是怎么解决冲突和存储的,接下来看下代码实现(无限扩容的hash表):

#pragma once
# include<iostream>
# include<vector>
#include<list>
using namespace std;
// 计算哈希值的类
template<typename T>
class CHash
{
public:
	int operator()(const T& val)
	{
		// 默认用除留余数法
		return val;
	}
};

template<typename T,typename hashType = CHash<T>>
class HashTable
{
public:
	HashTable(int size = 3, double lf = 0.75)
		:_loadFactor(lf)
	{
		// 给哈希表开辟数组空间的
		_hashVec.resize(size);
	}


	//插入操作
	void Put(const T& value)
	{
		//计算当前的装载因子是否满足条件
		int len = 0;
		for (list<T> ele : _hashVec)
		{
			if (!ele.empty())
			{
				++len;
			}
		}
		//不满足条件进行扩容,元素调整
		if ((double)(len + 1) / _hashVec.size() >= _loadFactor)
		{
			//扩容为以前的大小的2倍
			int size = _hashVec.size();
			_hashVec.resize(2 * size);
			//记录以前的元素
			vector<T> temp;
			for (list<T> &ele : _hashVec)
			{
				for (T value : ele)
				{
					temp.push_back(value);
				}
				//将链表清空
				ele.clear();
			}
			//重存以前的元素
			for (auto ele : temp)
			{
				int index = _hash(ele) % _hashVec.size();
				_hashVec[index].push_back(ele);
			}
		}
		int index = _hash(value) % _hashVec.size();
		_hashVec[index].push_back(value);
	}

	//删除操作
	void Remove(T value)
	{
		int index = _hash(value) % _hashVec.size();
		if (find(_hashVec[index].begin(), _hashVec[index].end(),value)== _hashVec[index].end())
		{
			cout << "查无此元素" << endl;
			return;
		}
		_hashVec[index].remove(value);

	}

	//查询操作
	void Querry(T value)
	{
		int index = _hash(value) % _hashVec.size();
		if (find(_hashVec[index].begin(), _hashVec[index].end(), value) == _hashVec[index].end())
		{
			cout << "查无此元素" << endl;
			return;
		}
		auto it = find(_hashVec[index].begin(), _hashVec[index].end(), value);
		cout << "存在于第" << index << "条链表"<< endl;
	}

	//打印操作
	friend ostream& operator<< <>(ostream& out, const HashTable<T>& tmp);

private:
	vector<list<T>> _hashVec;
	double _loadFactor;//记录加载因子
	hashType _hash;  // 专门计算T类型对象的哈希值的

};
template<typename T>
ostream& operator<< <>(ostream& out, const HashTable<T>& tmp)
{
	int i = 0;
	for (list<T> ele : tmp._hashVec)
	{
		cout << "第" << i << "条链的内容为:";
		for (T value : ele)
		{
			cout << value << ends;
		}
		cout << endl;
		++i;
	}
	return out;


}
int main()
{
	HashTable<int> hashTable;
	hashTable.Put(1);
	cout << "插入1次" << endl;
	cout << hashTable << endl;
	hashTable.Put(2);
	hashTable.Put(3);
	hashTable.Put(4);
	cout << hashTable << endl;
	hashTable.Querry(4) ;
	hashTable.Remove(4);
	cout << hashTable << endl;
	return 0;
}

运行结果如下图所示:

2.线性探测法

来来来,一图明所有:

扫描二维码关注公众号,回复: 10712651 查看本文章

代码实现:

# include<iostream>
# include<string>
# include<vector>
using namespace std;
# define MAX 100

// 计算哈希值的类 
template<typename T>
class CHash
{
public:
	int operator()(const T& val)
	{

		// 默认用除留余数法
		return val;
	}

}; 


// 线性探测法实现的哈希表结构 
template<typename T, typename HashType = CHash<T>>
class CHashTable
{
public:
	CHashTable(int size = 3, double lf = 0.75)
		:_loadFactor(lf)
	{
		// 给哈希表开辟数组空间的
		_hashVec.resize(size);
		
	}

	void put(const T& val)
	{
		//计算当前的装载因子是否满足条件。
		int size = _hashVec.size();
		int len = 0;
		int index = 0;
		for (int i = 0; i < size; ++i)
		{
			if ((_hashVec.begin() + i)->_data != T())
			{
				++len;
			}
		}
		if ((double)((len + 1) / _hashVec.size()) >= _loadFactor)
		{
			_hashVec.resize(2 * _hashVec.size());
			_loadFactor /= 2.0;
			//按照新的_hashVec.size()将元素的存储位置进行调整
			T tmp[MAX];
			int count = 0;
			for (int i = 0; i < size; ++i)
			{
				if (_hashVec[i]._state == STATE_USE)
				{
					tmp[count] = _hashVec[i]._data;
					++count;
					_hashVec[i]._data = T();
					_hashVec[i]._state = STATE_UNUSE;

				}
			}
			//重存扩容以前的元素
			for (int i = 0; i < count; ++i)
			{
				index = _hash(tmp[i]) % _hashVec.size();
				for (int j = index;; j = (j + 1) % _hashVec.size())//因为hash表一直在扩容,所以只要有元素就一定能放下。
				{
					// STATE_UNUSE STATE_USE STATE_USE
					//将元素按照规则放入哈希表。
					if (_hashVec[j]._state == STATE_UNUSE || _hashVec[j]._state == STATE_USED)
					{
						_hashVec[j]._data = tmp[i];
						_hashVec[j]._state = STATE_USE;
						break;
					}
				}
			}
		}
		index = _hash(val) % _hashVec.size();
		for (int i = index;; i = (i + 1) % _hashVec.size())//因为hash表一直在扩容,所以只要有元素就一定能放下。
		{
			// STATE_UNUSE STATE_USE STATE_USE
			//将元素按照规则放入哈希表。
			if ((_hashVec.begin() + i)->_state == STATE_UNUSE || (_hashVec.begin() + i)->_state == STATE_USED)
			{
				(_hashVec.begin() + i)->_data = val;
				(_hashVec.begin() + i)->_state = STATE_USE;
				_loadFactor = (double)(len + 1) / _hashVec.size();
				return;
			}
		}
	}
	T out(T val)
	{
		int len = 0;
		for (int i = 0; i < _hashVec.size(); ++i)
		{
			if ((_hashVec.begin() + i)->_data != T())
			{
				++len;
			}
		}
		int index = _hash(val) % _hashVec.size();
		for (int i = index;; i = (i + 1) % _hashVec.size())//因为hash表一直在扩容,所以只要有元素就一定能放下。
		{
			// STATE_UNUSE STATE_USE STATE_USE
			//将元素按照规则放入哈希表。
			if ((_hashVec.begin() + i)->_state == STATE_UNUSE)
			{
				return -100;
			}
			if ((_hashVec.begin() + i)->_data == val)
			{
				T a = (_hashVec.begin() + i)->_data;
				(_hashVec.begin() + i)->_state = STATE_USED;

				(_hashVec.begin() + i)->_data = T();
				_loadFactor = (double)(len - 1) / _hashVec.size();
				return a;
			}
		}

	}

	friend ostream& operator<< <>(ostream& out, const CHashTable<T, CHash<T>>& tmp);
private:
	// STATE_UNUSE   删除 STATE_USED 0
	enum STATE { STATE_UNUSE, STATE_USE, STATE_USED };
	struct Node
	{
		Node(T data = T())
			:_data(data), _state(STATE_UNUSE)
		{}
		T _data;
		STATE _state;
	};
	vector<Node> _hashVec;
	double _loadFactor; // 记录加载因子
	HashType _hash;  // 专门计算T类型对象的哈希值的
};
template<typename T, typename HashType = CHash<T>>

ostream & operator<< <>(ostream & out, const CHashTable<T, CHash<T>> & tmp)
{
	for (auto it : tmp._hashVec)
	{
		out << it._data << " ";
	}

	return out;


}

int main()
{
	CHashTable<int, CHash<int>> hash(6, 0.75);
	hash.put(1);
	cout << hash << endl;
	hash.put(2);
	cout << hash << endl;
	hash.put(3);
	cout << hash << endl;
	hash.put(4);
	cout << hash << endl;
	hash.put(5);
	cout << hash << endl;
	hash.put(6);
	cout << hash << endl;
	hash.put(7);
	cout << hash << endl;
	hash.out(6);
	cout << hash << endl;
	hash.out(5);
	cout << hash << endl;
	hash.out(4);
	cout << hash << endl;
	hash.out(3);
	cout << hash << endl;
	hash.out(2);
	cout << hash << endl;
	hash.out(1);
	cout << hash << endl;

	system("pause");
	return 0;
}

测试结果:

发布了124 篇原创文章 · 获赞 24 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_42214953/article/details/105074842