解決哈希衝突---開鏈法

在上篇博客中,已經提出了兩種解決哈希衝突的辦法:線性探測,二次探測。

下面呢,在介紹一種解決衝突的辦法---開鏈法(哈希桶)


wKiom1dEOk7w_Wx_AABV7m-vY5A620.png

哈希桶的實現:主要是將哈希衝突的那些值存到鏈表中。


代碼實現:(支持字典查詢)

#pragma once
#include <iostream>
#include <vector>
#include <string>

using namespace std;

template <class T,class V>
struct HashTableNode
{
	HashTableNode(const T& key,const V& value)
		:_key(key)
		,_value(value)
		,_next(NULL)
	{}
	T _key;
	V _value;
	HashTableNode<T,V>* _next;
};

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

template <>
struct __HashFunc<string>
{
	size_t operator()(const string& key)
	{
		size_t hash = 0;
		for(size_t i=0;i<key.size();++i)
		{
			hash += key[i];
		}
		return hash;
	}
};


template <class T,class V,class HashFunc = __HashFunc<T>>
class HashTableBucket   //哈希桶
{
	typedef HashTableNode<T,V> Node;
	typedef HashTableBucket<T,V,HashFunc> Table;
public:
	//構造
	HashTableBucket()
		:_table(NULL)
		,_size(0)
	{}
	HashTableBucket(size_t capacity)
		:_size(0)
	{
		_table.resize(_CheckCapacity(capacity));
	}
	//析構
	~HashTableBucket()
	{
		for(size_t i=0;i<_table.size();++i)
		{
			Node* cur = _table[i];
			while(cur)
			{
				Node* del = cur;
				cur = cur->_next;
				delete del;
			}
			_table[i] = NULL;
		}
	}
	//拷貝
	HashTableBucket(const Table& ht)
		:_size(0)
	{
		_table.resize(ht._table.size()); //開闢空間
		for(size_t i=0;i<ht._table.size();++i)
		{
			Node* cur = ht._table[i];
			while(cur)
			{
				Insert(cur->_key,cur->_value);
				cur = cur->_next;
			}
		}
	}
	//賦值
	/*HashTableBucket<T,V>& operator=(HashTableBucket<T,V> ht)
	{
		_table.swap(ht._table);
		swap(_size,ht._size);
		return *this;
	}*/

	Table& operator=(Table& ht)
	{
		if(this != &ht)
		{
			Table tmp(ht);
			_table.swap(tmp._table);
			swap(_size,tmp._size);
		}
		return *this;
	}
public:
	bool Insert(const T& key,const V& value)
	{
		_CheckCapacity();//檢查容量
		size_t index = _HashFunc(key,_table.size());
		Node* cur = _table[index];
		while(cur)
		{
			if(cur->_key == key)  //防冗餘
			{
				return false;
			}
			cur = cur->_next;
		}
		//頭插
		Node* newNode =new Node(key,value);
		newNode->_next = _table[index];
		_table[index] = newNode;
		++_size;
		return true;
	}
	Node* Find(const T& key)
	{
		size_t index = _HashFunc(key,_table.size());
		Node* cur = _table[index];
		while(cur)
		{
			if(cur->_key == key)
			{
				return cur;
			}
			cur = cur->_next;
		}
		return NULL;
	}
	bool Remove(const T& key)
	{
		size_t index = _HashFunc(key,_table.size());
		Node* cur = _table[index];
		Node* prev = NULL;
		Node* del = NULL;
		if(cur->_key == key)
		{
			del = cur;
			_table[index] = cur->_next;
			delete del;
			return true;
		}	
		prev = cur;
		cur = cur->_next;
		while(cur)
		{
			if(cur->_key == key)
			{
				del = cur;
				prev->_next = cur->_next;
				delete del;
				return true;
			}
			prev = cur;
			cur = cur->_next;
		}
		return false;
	}
	void Print()
	{
		for(size_t i=0;i<_table.size();++i)
		{
			printf("_table[%d]:",i);
			Node* cur = _table[i];
			while(cur)
			{
				cout<<"["<<cur->_key<<","<<cur->_value<<"]"<<"->";
				cur = cur->_next;
			}
			cout<<"NULL"<<endl;
		}	
	}
protected:
	size_t _HashFunc(const T& key,size_t capacity) //哈希函數
	{
		//return key%capacity;
		HashFunc hash;
		return hash(key)%capacity;
	}
	size_t _GetNextPrime(const size_t size)  //獲取下一個素數
	{
		// 使用素數表對齊做哈希表的容量,降低哈希衝突
		const int _PrimeSize = 28;
		static const unsigned long _PrimeList [_PrimeSize] =
		{
			53ul,         97ul,         193ul,       389ul,       769ul,

			1543ul,       3079ul,       6151ul,      12289ul,     24593ul,

			49157ul,      98317ul,      196613ul,    393241ul,    786433ul,

			1572869ul,    3145739ul,    6291469ul,   12582917ul,  25165843ul,

			50331653ul,   100663319ul,  201326611ul, 402653189ul, 805306457ul,

			1610612741ul, 3221225473ul, 4294967291ul
		};
		for(size_t i=0;i<_PrimeSize;++i)
		{
			if(_PrimeList[i] > size)
			{
				return _PrimeList[i];
			}
		}
		return _PrimeList[_PrimeSize-1];
	}

	void _CheckCapacity()
	{
		if(_size == _table.size())
		{
			size_t nextPrime = _GetNextPrime(_size);
			vector<Node*> newtable;
			newtable.resize(nextPrime);
			for(size_t i=0;i<_table.size();++i)
			{
				Node* cur = _table[i];
				while(cur)
				{
					//摘節點
					Node* tmp = cur;
					cur = cur->_next;

					//計算在新表中的位置,頭插
					size_t index = _HashFunc(tmp->_key,nextPrime);
					newtable[index] = tmp;
				}
				_table[i] = NULL;
			}
			_table.swap(newtable); //size,capacity,內容
		}
	}
private:
	vector<Node*> _table;  //哈希表
	size_t _size;          //數據的個數
};

測試代碼:

#include "HashTableBucket.h"


void HashTableListTest()
{
	HashTableBucket<int,int> ht;
	for(size_t i=0;i<50;++i)
	{
		ht.Insert(i,i);
	}
	ht.Insert(107,32);
	ht.Insert(54,45);
	//ht.Insert(65,32);
	/*HashTableNode<int,int>* ret = ht.Find(1);
	if(ret)
	{
		cout<<"["<<ret->_key<<","<<ret->_value<<"]"<<endl;
	}*/
	//ht.Remove(54);
	ht.Remove(1);
	//ht.Print();
}

void HashTableTest()
{
	HashTableBucket<int,int> ht;
	ht.Insert(1,1);
	ht.Insert(11,11);
	ht.Insert(21,21);
	ht.Insert(12,12);
	ht.Insert(23,23);
	ht.Insert(54,57);
	ht.Insert(42,12);
	//ht.Print();
	HashTableBucket<int,int> ht1(ht);
	//ht1.Print();

	HashTableBucket<int,int> ht2;
	ht2 = ht1;
	ht2.Print();

}


void DircFindTest()
{
	HashTableBucket<string,string> ht;
	/*ht.Insert("zhang","張");
	ht.Insert("xiao","小");
	ht.Insert("yu","雨");*/
	//ht.Insert("angzh","田");

	ht.Insert("字典","dirc");
	ht.Insert("錢","money");
	ht.Insert("吃","eat");
	ht.Insert("玩","happy");
	ht.Print();
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章