HashTable-散列表/哈希表,是根据关键字(key)而直接访问在内存存储位置的数据结构。

它通过一个关键值的函数将所需的数据映射到表中的位置来访问数据,这个映射函数叫做散列函数,存放记录的数组叫做散列表。

   构造哈希表的方法:

     1.直接定址法--取关键字的某个线性函数为散列地址,Hash(Key)= Key 或 Hash(Key)= A*Key + B,A、B为常数。

     2.除留余数法--取关键值被某个不大于散列表长m的数p除后的所得的余数为散列地址。Hash(Key)= Key % P。

    3.平方取中法

     4.折叠法

     5.随机数法

     6.数学分析法

  常用方法为直接定址法,除留余数法。

K类型代码:

#pragma once

#include <string>

enum Status
{
	EXIST,
	DELETE,
	EMPTY,
};

// 仿函数
template<class K>
struct DefaultHashFuncer
{
	size_t operator() (const K& key)
	{
		return key;
	}
};

static size_t BKDRHash(const char * str)
{
	unsigned int seed = 131; // 31 131 1313 13131 131313
	unsigned int hash = 0;
	while (*str )
	{
		hash = hash * seed + (unsigned char)(*str++);
	}
	return (hash & 0x7FFFFFFF);
}

template<>
struct DefaultHashFuncer<string>
{
	size_t operator()(const string& str)
	{
		//size_t value = 0;
		//for (size_t i = 0; i < str.size(); ++i)
		//{
		//	value += str[i];
		//}

		//return value;

		return BKDRHash(str.c_str());
	}
};

template<class K, class HashFuncer = DefaultHashFuncer<K> >
class HashTable
{
public:
	HashTable()
		:_tables(NULL)
		,_status(NULL)
		,_size(0)
		,_capacity(0)
	{}

	HashTable(size_t size)
		:_tables(new K[size])
		,_status(new Status[size])
		,_size(0)
		,_capacity(size)
	{
		//memset(_status, 0, sizeof(Status)*_size);
		for (size_t i = 0; i < _capacity; ++i)
		{
			_status[i] = EMPTY;
		}
	}

	HashTable(const HashTable<K, HashFuncer>& ht)
	{
		HashTable<K, HashFuncer> tmp(ht._capacity);
		for (size_t )
		{}
	}

	HashTable<K, HashFuncer>& operator=(HashTable<K, HashFuncer> ht);

	~HashTable()
	{
		if (_tables)
		{
			delete[] _tables;
			delete[] _status;
		}

		_size = 0;
		_capacity = 0;
	}

	bool Insert(const K& key)
	{
		/*if (_size == _capacity)
		{
			cout<<"Full"<<endl;
			return false;
		}*/

		_CheckCapacity();

		size_t index = _HashFunc(key);
		// 线性探测

		while(_status[index] == EXIST)
		{
			if (_tables[index] == key)
			{
				return false;
			}

			++index;
			if (index == _capacity)
				index = 0;
		}
		
		_status[index] = EXIST;
		_tables[index] = key;
		++_size;

		return true;
	}

	int Find(const K& key)
	{
		int i = 0; 
		size_t index = _HashFunc(key);
		while (_status[index] != EMPTY)
		{
			if (_tables[index] == key
				&& _status[index] != DELETE)
			{
				return index;
			}

			++i;
			index = _HashFunc(key)+i*i;
			++index;
			if (index == _capacity)
			{
				index = 0;
			}
		}

		return -1;
	}

	bool Remove(const K& key)
	{
		int index = Find(key);
		if (index != -1)
		{
			_status[index] = DELETE;
			return true;
		}

		return false;
	}

	void Swap(HashTable<K, HashFuncer>& ht)
	{
		swap(_tables, ht._tables);
		swap(_size, ht._size);
		swap(_status, ht._status);
		swap(_capacity, ht._capacity);
	}

	size_t _HashFunc(const K& key)
	{
		//
		//return key%_capacity;
		HashFuncer hf;
		return hf(key)%_capacity;
	}

	void PrintTables()
	{
		for (size_t i = 0 ; i < _capacity; ++i)
		{
			if (_status[i] == EXIST)
			{
				printf("[%d]:E->", i);
				cout<<_tables[i];
			}
			else if (_status[i] == DELETE)
			{
				printf("[%d]:D->", i);
				cout<<_tables[i];
			}
			else
			{
				printf("[%d]:N", i);
			}

			cout<<endl;
		}
	}

	void _CheckCapacity()
	{
		if (_size*10 >= _capacity*7)
		{
			/*K* tmpTables = new K[2*_capacity];
			K* tmpStatus = new Status[2*_capacity];
			for(size_t i = 0; i < _capacity; ++i)
			{
				if ()
				{
				}
			}*/

			HashTable<K, HashFuncer> tmp(2*_capacity);
			for (size_t i = 0; i < _capacity; ++i)
			{
				if (_status[i] == EXIST)
				{
					tmp.Insert(_tables[i]);
				}
			}

			this->Swap(tmp);
		}
	}


protected:
	K* _tables;
	Status* _status;
	size_t _size;
	size_t _capacity;
};

    a:变量分析:

    1.K类型的数组,用来存储key。

    2.Status类型的数组,用来标志每一个位置状态。

    3._size,用于表示有效数据个数。

    4._capacity,容量

    b:难点分析

    1.使用仿函数计算不同类型数据的Key。

    2.处理哈希冲突以及载荷因子。

wKiom1c1Z7bQFOPLAAD1Xk3F9FA371.png


KV类型的代码

#pragma once
#include <string>

enum Status
{
	EXIST,
	DELETE,
	EMPTY,
};
template<class K, class V>
struct KeyValue
{
	K _key;
	V _value;

	KeyValue(const K& key = K(), const V& value = V())
		:_key(key)
		,_value(value)
	{}
};

template<class K>
struct DefaultHashFuncer
{
	size_t operator() (const K& key)
	{
		return key;
	}
};

static size_t BKDRHash(const char * str)
{
	unsigned int seed = 131; // 31 131 1313 13131 131313
	unsigned int hash = 0;
	while (*str )
	{
		hash = hash * seed + (unsigned char)(*str++);
	}
	return (hash & 0x7FFFFFFF);
}

template<>
struct DefaultHashFuncer<string>
{
	size_t operator()(const string& str)
	{
		//size_t value = 0;
		//for (size_t i = 0; i < str.size(); ++i)
		//{
		//	value += str[i];
		//}

		//return value;

		return BKDRHash(str.c_str());
	}
};

template<class K, class V,
 class HashFuncer = DefaultHashFuncer<K> >
class HashTable
{
	typedef  KeyValue<K, V> KV;
public:
	HashTable(size_t size)
		:_tables(new KV[size])
		,_status(new Status[size])
		,_size(0)
		,_capacity(size)
	{
		//memset(_status, 0, sizeof(Status)*_size);
		for (size_t i = 0; i < _capacity; ++i)
		{
			_status[i] = EMPTY;
		}
	}

	~HashTable()
	{
		if (_tables)
		{
			delete[] _tables;
			delete[] _status;
		}

		_size = 0;
		_capacity = 0;
	}

	bool Insert(const K& key, const V& value)
	{
		if (_size == _capacity)
		{
			cout<<"Full"<<endl;
			return false;
		}

		//_CheckCapacity();

		// 二次方探测
		int i = 1;
		size_t index = _HashFunc0(key);
		while(_status[index] == EXIST)
		{
			if (_tables[index]._key == key)
			{
				return false;
			}

			index = _HashFunci(index, i++);
		}
		
		_status[index] = EXIST;
		_tables[index] = KV(key, value);
		++_size;

		return true;
	}

	KV* Find(const K& key);

	size_t _HashFunc0(const K& key)
	{
		HashFuncer hf;
		return hf(key)%_capacity;
	}

	size_t _HashFunci(size_t prevHash, int i)
	{
		return (prevHash + 2*i - 1)%_capacity;
	}

protected:
	KV* _tables;
	Status* _status;
	size_t _size;
	size_t _capacity;
};

同理上面K类型,不同的是_tables的每一个元素是一个KeyValue<K,V>类型的结构体。


处理哈希冲突的闭散列方法

 1.线性探测

  2.二次探测


wKioL1c1araTWCI1AAFsKE2tbRw673.png

   以上就是本人在学习过程中的一些经验总结。当然,本人能力有限,难免会有纰漏,希望大家可以指正。