亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

哈希桶處理哈希沖突

發布時間:2020-05-14 23:03:08 來源:網絡 閱讀:17842 作者:威尼斯小艇 欄目:編程語言

    哈希桶:哈希桶就是盛放不同key鏈表的容器(即是哈希表),我們可以把每個key的位置看作是一個指針,該指針所指向的位置里放了一個鏈表可以認為是指針數組,故該方法也叫開鏈式。

    相比閉散列,哈希桶提高了空間利用率:在實現哈希表時,常見的方法是線性探測、二次探測,這兩個算法的具體實現可以查看我的博客。但是這兩個算法有一個共同點就是:空間利用率低。為什么這么說呢?線性探測、二次探測的高效性很大程度上要取決于它的載荷因子,載荷因子即:存放關鍵字個數 / 空間大小。

    通過查閱資料,我發現,使用素數做除數可以減少哈希沖突。見下:

素數表:使用素數做除數可以減少哈希沖突

     // 使用素數表對齊做哈希表的容量,降低哈希沖突

     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

    };

下圖進行哈希桶處理哈希沖突的展示

哈希桶處理哈希沖突

下面通過庫中的vactor進行存放指向鏈表的指針,每個結點里包含_key,_value和_next

#pragma
template<class K>
struct DefaultHashFunc
{
	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 int)(*str++);
	}
	return (hash & 0x7FFFFFFF);
}
template<>
struct DefaultHashFunc<string>
{
	size_t operator()(const string& str)
	{
		return BKDRHash(str.c_str());
	}
};
template<class K, class V>
struct HashTableNode//結點
{
	K _key;
	V _value;
	HashTableNode* _next;
	HashTableNode(const K& key, const V& value)
		:_key(key)
		, _value(value)
		, _next(NULL)
	{}
};
template<class K, class V, class HashFunc = DefaultHashFunc<K>>
class HashTableBucket
{
	typedef HashTableNode<K, V> Node;
public:
	HashTableBucket();
	HashTableBucket(const HashTableBucket<K, V, HashFunc>& htb);
	HashTableBucket<K, V, HashFunc>& operator=(HashTableBucket<K, V, HashFunc> htb);
	void PrintTables();
	bool Insert(const K& key,const V& value);//防冗余,在刪除和查找時只需要key
	Node* Find(const K& key);
	bool Remove(const K& key);
protected:
	size_t _HashFunc(const K& key);
	size_t _GetNextPrime(size_t size);//獲取下一個素數(利用素數表,使用素數做除數可以減少哈希沖突)
	void _CheckExpand();
private:
	vector<Node*> _tables;//開鏈式為指針數組,指針指向鏈表
	size_t _size;//有效數據數,vector中的size()為有效空間數
};

實現_HashFunc(const K& key),通過偽函數來判斷不同類型的key所在鏈表的位置。

template<class K, class V, class HashFunc = DefaultHashFunc<K>>
size_t HashTableBucket<K, V, HashFunc>::_HashFunc(const K& key)
{
	HashFunc htb;
	return htb(key) % (_tables.size());//htb(key)偽函數
}

1. 插入函數的實現(Insert)

(1)檢查容量。調用_CheckExpand()函數檢查負載因子a,考慮是否擴張,當a為1時進行擴容。

(2)檢查插入的key是否已經存在,不存在返回false,存在進行(3)操作。

(3)進行頭插。

template<class K, class V, class HashFunc = DefaultHashFunc<K>>
bool HashTableBucket<K, V, HashFunc>::Insert(const K& key, const V& value)
{//防冗余,在刪除和查找時只需要key
	_CheckExpand();//檢查是否擴容
	for (size_t i = 0; i < _tables.size(); ++i)
	{
		Node* cur = _tables[i];
		while (cur)
		{//如果插入的元素存在就返回false
			if (cur->_key == key)
			{
				return false;
			}
			cur = cur->_next;
		}
	}	
	//頭插
	size_t index = _HashFunc(key);
	Node* tmp = new Node(key, value);
	tmp->_next = _tables[index];
	_tables[index] = tmp;
	++_size;
	return true;

2. 查找函數的實現(Find)

(1)調用_HashFunc()函數找到要尋找的Key所在的鏈表位置。

(2)通過遍歷鏈表查找key。

template<class K, class V, class HashFunc = DefaultHashFunc<K>>
HashTableNode<K, V>* HashTableBucket<K, V, HashFunc>::Find(const K& key)//查找
{
	size_t index = _HashFunc(key);//鏈表結點位置
	Node* cur = _tables[index];
	while (cur)
	{
		if (cur->_key == key)
		{
			return cur;
		}
		cur = cur->_next;
	}
	return NULL;
}

3. 刪除函數的實現(Remove)

(1)調用Find()函數,判斷需要刪除的key是否存在,不存在就返回false,存在就進行(2)操作。

(2)調用_HashFunc()函數找到key所在鏈表的位置,先通過遍歷鏈表找到del結點的上一個結點prev,然后使prev的下一個結點指向del的下一個結點。

template<class K, class V, class HashFunc = DefaultHashFunc<K>>
bool HashTableBucket<K, V, HashFunc>::Remove(const K& key)//刪除
{
	if (Find(key) == NULL)
	{
		return false;
	}
	size_t index = _HashFunc(key);
	//需要找到刪除結點的前后結點
	Node* del = Find(key);
	Node* next = del->_next;
	Node* prev = _tables[index];
	while (prev)
	{
		if (prev->_next == del)
		{
			break;
		}
		prev = prev->_next;
	}
	if (next)//如果next存在時,進行鏈接
	{
		prev->_next = next;
	}
	del = NULL;
	return true;
}

檢查是否需要擴容_CheckExpand()的實現。

template<class K, class V, class HashFunc = DefaultHashFunc<K>>
void HashTableBucket<K, V, HashFunc>::_CheckExpand()//檢查負載因子,考慮是否擴容
{
	if (_size >= _tables.size())//負載因子達到了1,進行擴容
	{
		size_t NewSize = _GetNextPrime(_size);
		//進行結點復制
		vector<Node*> NewTables;
		NewTables.resize(NewSize);
		for (size_t i = 0; i < _tables.size(); ++i)
		{
			Node* cur = _tables[i];
			while (cur)//頭插
			{
				Node* tmp = cur;
				cur = cur->_next;
				size_t index = _HashFunc(tmp->_key);//重新確定元素在表中位置
				tmp->_next = NewTables[index];
				NewTables[index] = tmp;
			}
		}
		_tables.swap(NewTables);//調用vector中的swap接口進行交換
	}
}
template<class K, class V, class HashFunc = DefaultHashFunc<K>>
size_t HashTableBucket<K, V, HashFunc>::_GetNextPrime(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[i - 1];
	}
	return _PrimeList[_PrimeSize];//如果size大于或等于素數表中數據,就返回表中最大數
}

測試用例如下,實現字典(可以一對多)查詢。

HashTableBucket<string, vector<string>> dict;
	vector<string> v;
	v.push_back("manager");
	dict.Insert("經理", v);

	v.clear();
	v.push_back("移動");
	v.push_back("距離");
	dict.Insert("remove",v);
	HashTableNode<string, vector<string>>* ret = dict.Find("remove");
	ret->_value.push_back("搬家");

	vector<string>& words = ret->_value;
	for (size_t i = 0; i < words.size(); ++i)//打印對應的多個字符串
	{
		cout << words[i].c_str() << endl;
	}
向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

永登县| 盐津县| 辽中县| 临武县| 泰州市| 绥宁县| 临沂市| 漳平市| 响水县| 平乡县| 两当县| 汤阴县| 德阳市| 义乌市| 鄱阳县| 于田县| 苍梧县| 邹城市| 盘山县| 白河县| 密山市| 岑巩县| 邮箱| 龙口市| 甘洛县| 通化县| 信丰县| 石渠县| 施甸县| 扎鲁特旗| 平和县| 内江市| 神农架林区| 海丰县| 潼南县| 贺兰县| 嘉黎县| 桐柏县| 集安市| 酒泉市| 福清市|