C++の詳しい説明---ハッシュパッケージ化

ハッシュバケットコード

これまでの学習により、次のコードを簡単に記述できるようになったはずです。

#pragma once
#include<iostream>
#include<vector>
using namespace std;
template<class K,class V>
struct HashNode
{
    
    
	HashNode(const pair<K,V>& kv)
		:_kv(kv)
		,_next(nullptr)
	{
    
    }
	pair<K, V> _kv;
	HashNode* _next;
};
template<class K>
struct HashFunc
{
    
    
	size_t operator()(const K& key)
	{
    
    
		return (size_t)key;
	}
};
template<>
struct HashFunc<string>
{
    
    
	size_t operator()(const string& s)
	{
    
    
		size_t res = 0;
		for (auto& ch : s)
		{
    
    
			res *= 131;
			res += ch;
		}
		return res;
	}
};
template<class K, class V, class Hash = HashFunc<K>>
class BucketTable
{
    
    
	typedef HashNode<K, V> Node;
public:
	BucketTable()
		:_n(0)
	{
    
    
		_tables.resize(__stl_next_prime(_tables.size()));
	}
	Node* Find(const K& key)
	{
    
    
		Hash hf;
		size_t pos = hf(key) % _tables.size();
		Node* cur = _tables[pos];
		while (cur)
		{
    
    
			if (cur->_kv.first == key)
			{
    
    
				return cur;
			}
			else
			{
    
    
				cur = cur->_next;
			}
		}
		return nullptr;
	}
	bool insert(const pair<K, V>& kv)
	{
    
    
		if (Find(kv.first))
		{
    
    
			return false;
		}
		if (_n / _tables.size() == 1)//平衡因子为1就更新
		{
    
    
			vector<Node*> newBH;
			newBH.resize(__stl_next_prime(_tables.size()));
			for (int i = 0; i < _tables.size(); i++)
			{
    
    
				Node* cur = _tables[i];
				while (cur)
				{
    
    
					Node* next = cur->_next;
					size_t pos = Hash()(cur->_kv.first);
					cur->_next = newBH[pos];
					newBH[pos] = cur;
					cur = next;
				}
				_tables[i] = nullptr;
			}
		}
		Hash hf;
		size_t pos = hf(kv.first) % _tables.size();
		Node* newnode = new HashNode<K,V>(kv);
		newnode->_next = _tables[pos];
		_tables[pos] = newnode;
		++_n;
		return true;
	}
	bool erase(const K& key)
	{
    
    
		HashFunc<K> HF;
		size_t pos = HF(key) % _tables.size();
		Node* cur = _tables[pos];
		Node* prev = cur;
		while (cur)
		{
    
    
			if (cur->_kv.first == key)
			{
    
    
				if (cur == _tables[pos])
				{
    
    
					_tables[pos] = cur->_next;
				}
				else
				{
    
    
					prev->_next = cur->_next;
				}
				delete cur;
				_n--;
				return true;
			}
			else
			{
    
    
				prev = cur;
				cur = cur->_next;
			}
		}
		return false;
	}
	~BucketTable()
	{
    
    
		for (int i = 0; i < _tables.size(); i++)
		{
    
    
			Node* cur = _tables[i];
			while (cur)
			{
    
    
				Node* next = cur->_next;
				delete cur;
				cur = next;
			}
			_tables[i] = nullptr;
		}
	}
	inline unsigned long __stl_next_prime(unsigned long n)
	{
    
    
		static const int __stl_num_primes = 28;
		static const unsigned long __stl_prime_list[__stl_num_primes] =
		{
    
    
			53, 97, 193, 389, 769,
			1543, 3079, 6151, 12289, 24593,
			49157, 98317, 196613, 393241, 786433,
			1572869, 3145739, 6291469, 12582917, 25165843,
			50331653, 100663319, 201326611, 402653189, 805306457,
			1610612741, 3221225473, 4294967291
		};
		for (int i = 0; i < __stl_num_primes; ++i)
		{
    
    
			if (__stl_prime_list[i] > n)
			{
    
    
				return __stl_prime_list[i];
			}
		}
		return __stl_prime_list[__stl_num_primes - 1];
	}


private:
	vector<Node*> _tables;
	size_t _n;
};

ハッシュバケットの変更

まず第一に、セットとマップをカプセル化するためにハッシュ バケットを使用する必要があります。これまでの赤黒ツリーの経験によれば、セット コンテナー内に格納されるデータは 1 種類だけですが、それでも 2 つのテンプレート パラメーターが使用されることがわかります。カプセル化では、挿入関数のパラメータは値である必要があり、検索関数のパラメータはキーであるため、カプセル化の均一性を確保するために、2 つのパラメータを使用して内部データを表します。セット コンテナ ノード テンプレートの 2 つのパラメータの各パラメータがキーであり、マップ コンテナ ノード テンプレートの 2 つのパラメータがキーと値である場合、ノード クラスを記述するコードは次のようになります。

template<class T>
struct HashNode
{
    
    
	HashNode(const T& data)
		:_data(data)
		, _next(nullptr)
	{
    
    }
	T _data;//这里的_data既可以是map的内部数据也可以是set的内部数据
	HashNode* _next;
};

ノード クラスが変更されているため、ハッシュ バケットの挿入関数も変更して、元のパラメーターのペア タイプをテンプレートの 2 番目のタイプ T に変換する必要があります。また、2 番目のパラメーターにはペアまたはペアが格納される可能性があるためです。はキーですが、find関数とinsert関数は比較のために常にペアの最初のパラメータを使用するため、コンテナ内のデータのキー値を取得するためにファンクターパラメータをここに追加する必要があり、insert関数はfindします。データの比較はファンクター オブジェクトを作成することで行われるため、ここを 1 つずつ変更する必要があります。ペアを介して最初のデータを手動で取得する以前の方法は、ファンクターを介してキーを取得するように変更されます。最初のステップは次のようになります:cur->_kv.firstを変更します その後、次のようになります:kot(cur->_data)上記のハッシュ バケットのすべての状況を変更して、最初のステップを完了します。 次に、unownedmap クラスと unownedset クラスを実装します。 まず、これら 2 つのクラスはベースになっていますハッシュ バケットで実装されているため、両方のクラスにハッシュ バケットのプライベート変数があります。次に、ハッシュ テーブルを使用する場合、異なるファンクターを渡すことができるため、ここのクラスにもファンクター テンプレート パラメーターを追加する必要があります。ハッシュ バケットにはデータ内のキーを取得するためのファンクターが必要であるため、外部のセットとマップ コンテナーでファンクターを作成し、内部ハッシュ バケットに渡す必要があります。この場合のコードは次のようになります。

template<class K,class HashFunc=HashFunc<K>>
class UnorderedSet
{
    
    
public:
	struct KeyOfT
	{
    
    
		const K& operator()(const K& key)
		{
    
    
			return key;
		}
	};
private:
	BucketTable<K, K, KeyOfT, HashFunc> _ht;
};

#include"HashBucket.h"
template<class K, class T,class HashFunc = HashFunc<K>>
class UnorderedMap
{
    
    
public:
	struct KeyOfT
	{
    
    
		const K& operator()(const pair<K,T>& key)
		{
    
    
			return key.first;
		}
	};
private:
	BucketTable<K, pair<const K,T>, KeyOfT, HashFunc> _ht;
};

基本フレームワークを実装すると、内部の検索関数、挿入関数、消去関数が完成しますが、ここでの実装原理はハッシュバケットの内部関数を呼び出すことで関数を実装するため、ここでのコードは次のようになります。

template<class K,class HashFunc=HashFunc<K>>
class UnorderedSet
{
    
    
public:
	struct KeyOfT
	{
    
    
		const K& operator()(const K& key)
		{
    
    
			return key;
		}
	};
	bool insert(const K& key)
	{
    
    
		return _ht.insert(key);
	}
	bool erase(const K& key)
	{
    
    
		return _ht.erase(key);
	}
	HashNode<K>* find(const K& key)
	{
    
    
		return _ht.Find(key);
	}
private:
	BucketTable<K, K, KeyOfT, HashFunc> _ht;
};

template<class K, class T,class HashFunc = HashFunc<K>>
class UnorderedMap
{
    
    
public:
	struct KeyOfT
	{
    
    
		const K& operator()(const pair<K,T>& key)
		{
    
    
			return key.first;
		}
	};
	bool insert(const pair<K,T>& data)
	{
    
    
		return _ht.insert(data);
	}
	bool erase(const K& key)
	{
    
    
		return _ht.erase(key);
	}
	HashNode<K>* find(const K& key)
	{
    
    
		return _ht.Find(key);
	}
private:
	BucketTable<K, pair<const K,T>, KeyOfT, HashFunc> _ht;
};

これらの基本的な機能を使用すると、次のコードを使用して、上記のコードが正しく実装されているかどうかを簡単にチェックできます。ここでのチェック コードは次のとおりです。

int main()
{
    
    
	UnorderedSet<int> set;
	int a[] = {
    
     18, 8, 7, 27, 57, 3, 38, 18 };
	for (auto e : a)
	{
    
    
		set.insert(e);
	}
	set.insert(17);
	set.insert(5);
	if (set.find(7)) {
    
     cout << "存在" << endl; }
	else {
    
     cout << "不存在" << endl; }
	set.erase(7);
	if (set.find(7)) {
    
     cout << "存在" << endl; }
	else {
    
     cout << "不存在" << endl; }
	return 0;
}

このコードの実行結果は次のとおりです。
ここに画像の説明を挿入します
実行結果に問題はありません。これは、上で実装したコードに問題がないことを示しており、map と set のイテレータの実装を続行できます。

イテレータの実装

ハッシュ バケットの各ノードはリンク リストであるため、イテレータの走査はベクトルの最初の位置から開始され、最初のノードが空ではない位置を見つけてから、リンク リストを上から下に走査します。各ノードで、リンク リストの走査が完了した後、現在の位置の後に空ではないベクトル内の次のノードの検索を続ける場合、これがイテレータの実装アイデアです。イテレータの ++ を作成するには、まずイテレータのクラスを作成する必要があります。
まず、イテレータはテンプレートである必要があり、テンプレート パラメータは 4 つです。ここでのコードは次のとおりです。

template<class K, class T, class KeyOfT, class Hash>
class _HashIterator
{
    
    

};

まず、++ 関数は HashNode ノードを記録する必要があるため、このクラスにノード ポインターを作成する必要があります。次に、++ 関数の戻り値はイテレータ自体であるため、ここでは typedef を使用して単純化できます。 ++ では、次のリンク リストの位置を見つけるためにベクトル コンテナを使用する必要があるため、ここでハッシュ バケット クラスも渡す必要があります。ベクトル コンテナを渡すのが難しいため、ハッシュ バケットがここに渡されます。 、ここでのコードは次のようになります。

template<class K, class T, class KeyOfT, class Hash>
struct _HashIterator
{
    
    
	typedef HashNode<T> Node;
	typedef _HashIterator<K, T, KeyOfT, Hash> Self;
	typedef BucketTable<K, T, KeyOfT, Hash> Bt;
	Node* _node;
	Bt* _bt;
	Self& operatpr++()
	{
    
    
	}	
};

次に、++ の演算子のオーバーロードを実装します。まず、現在のノードの次のノードが空であるかどうかを確認します。空でない場合は、下にまだデータがあることを意味します。次のようにノード ポインタのポインティングを変更します。それは次のノードを指します。ノードの次のポインターが空の場合、リンクされたリストの場所を見つける必要があります。まず、配列内のキーを見つけるために keyoft のファンクターを作成し、次に、その値を見つけます。 hashfunc 関数によるデータ変換、そして最後にコンテナのサイズを見つけます。現在の要素の位置を決定でき、次に空ではない次の位置を見つけるループを作成できます。このときのコードは次のとおりです。 :

Self& operator++()
{
    
    
	if (_node->next)
	{
    
    
		//当前节点的下一个节点为空
		_node = _node->next;
	}
	else
	{
    
    
		KeyOfT kot;
		Hash hf;
		size_t hashi = hf(kot(_node->_data)) % _bt->_tables.size();
		++hashi;
		while (hashi < _bt->_tables.size())
		{
    
    
			if (_bt->_tables[hashi])
			{
    
    
				_node = _bt->_tables[hashi];
				break;
			}
			else
			{
    
    
				hashi++;
			}
		}

	}
}	

ループが終了すると、2 つの状況が発生します。1 つ目は、現在の要素が最後の要素で、++ の後に要素が存在しないということです。2 つ目は、次の要素が見つかったという状況です。最初の状況では、次のようにします。現在の _tables.size() と等しいかどうかをハッシュを使用して判断できます。最初のケースが true でない場合、現在の状況は 2 番目のケースであることを意味します。ここではイテレータの _node を null ポインタとして使用します要素の最後の位置に到達したことを表す場合、コードは次のようになります。

Self& operator++()
{
    
    
	if (_node->_next)
	{
    
    
		//当前节点的下一个节点不为空
		_node = _node->_next;
	}
	else
	{
    
    
		KeyOfT kot;
		Hash hf;
		size_t hashi = hf(kot(_node->_data)) % _bt->_tables.size();
		++hashi;
		while (hashi < _bt->_tables.size())
		{
    
    
			if (_bt->_tables[hashi])
			{
    
    
				_node = _bt->_tables[hashi];
				break;
			}
			else
			{
    
    
				hashi++;
			}
		}
		if (hashi == _bt->_tables.size())
		{
    
    
			_node = nullptr;
		}
	}
	return *this;
}

この実装が完了したら、演算子 * と -> の演算子のオーバーロードを実装するだけで、コードは次のようになります。

T& operator *()
{
    
    
	return _node->_data;
}
T* operator ->()
{
    
    
	return &_node->_data;
}
bool operator !=(const Self& s) const
{
    
    
	return _node != s._node;
}

これらの関数を実装した後、イテレータのコンストラクタを実装できます。コンストラクタは、ノード ポインタとハッシュ バケット ポインタを初期化するだけです。コードは次のようになります。

_HashIterator(Node* node,Bt* bt)
	:_node(node)
	,_bt(bt)
{
    
    }

この関数が完了したら、ハッシュ バケット クラスに戻り、イテレータに対応する begin 関数と end 関数を実装します。ここでの begin 関数は、ベクトル コンテナ内の空ではない最初のリンク リストのアドレスを返します。 for ループを作成して継続的に検索することができます。その場合のコードは次のとおりです。

typedef _HashIterator<K, T, KeyOfT, Hash> iterator;
iterator begin()
{
    
    
	for (int i = 0; i < _tables.size(); i++)
	{
    
    
		if (_tables[i])
		{
    
    
			return iterator(_tables[i], this);
		}
	}
}

end 関数の実装原理も同様ですが、要素がないことを示すために nullptr を使用するため、ここのコードは次のように実装されます。

iterator end()
{
    
    
	return iterator(nullptr, this);
}

しかし、実装には問題があります。コードを実行すると、多くのエラーが報告されていることがわかります。その理由は、
ここに画像の説明を挿入します
ここで実装したイテレータにはハッシュ バケットが必要ですが、ハッシュ バケットの実装にはイテレータが必要であるためです。 , ここには自己矛盾した相互参照があるため、問題を解決するためにハッシュバケットクラスを事前に宣言する方法を使用する必要があります。ここでのコードは次のとおりです。

template<class K, class T, class KeyOfT, class Hash >
class BucketTable;//前置声明

template<class K, class T, class KeyOfT, class Hash>
struct _HashIterator
{
    
    
	//....
}

上記のコードを実装した後、この反復子をセットに適用できます。

typedef typename BucketTable<K, K, KeyOfT, HashFunc>::iterator iterator;
iterator begin()
{
    
    
	return _ht.begin();
}
iterator end()
{
    
    
	return _ht.end();
}

これを完了したら、次のコードを使用して上記の正確性をテストできます。

void test2()
{
    
    
	UnorderedSet<int> us;
	us.insert(1);
	us.insert(2);
	us.insert(3);
	us.insert(4);
	us.insert(5);
	UnorderedSet<int>::iterator it1 = us.begin();
	while (it1 != us.end())
	{
    
    
		cout << *it1 << endl;
		it1++;
	}
}

このコードを実行すると、上記の実装に問題があることがわかります。
ここに画像の説明を挿入します
この問題の理由は、
ここに画像の説明を挿入します
ここの _tables がプライベート メンバー変数であり、イテレータ クラスが通常それにアクセスできないためです。そのため、ここでの解決策は次のとおりです。それを解決するためのコードは次のとおりです。

template<class K, class T, class KeyOfT,class Hash >
class BucketTable
{
    
    
	template<class K, class T, class KeyOfT, class Hash>
	friend struct _HashIterator;
	//....
}

これが完了すると、上記のコードを通常どおり実行できるようになり、ここでの実行結果は次のようになります:
ここに画像の説明を挿入します
イテレータを実装した後、find 関数の戻り値と insert 関数の戻り値を変更できます。Insert 戻り値のペア、 find 返されるのはイテレータです。ここを変更すると、角括弧のオーバーロードが実現できます。角括弧のオーバーロードは、最初に挿入関数を呼び出し、次にペア型の戻り値を受け取り、最後に最初と 2 番目の関数を返します。ペア要素の要素. 、つまり Value の場合、ここでのコードは次のようになります。

template<class K, class T,class HashFunc = HashFunc<K>>
class UnorderedMap
{
    
    
public:
	
	struct KeyOfT
	{
    
    
		const K& operator()(const pair<K,T>& key)
		{
    
    
			return key.first;
		}
	};
	typedef typename BucketTable<K, pair<const K,T>, KeyOfT, HashFunc>::iterator iterator;
	iterator begin()
	{
    
    
		return _ht.begin();
	}
	iterator end()
	{
    
    
		return _ht.end();
	}
	pair<iterator,bool> insert(const pair<K,T>& data)
	{
    
    
		return _ht.insert(data);
	}
	bool erase(const K& key)
	{
    
    
		return _ht.erase(key);
	}
	iterator find(const K& key)
	{
    
    
		return _ht.Find(key);
	}
	T& operator[](const K& key)
	{
    
    
		pair<iterator, bool> ret = _ht.insert(make_pair(key, T()));
		return ret.first->second;
	}
private:
	BucketTable<K, pair<const K,T>, KeyOfT, HashFunc> _ht;
};

次のコードを使用してテストできます。

void test3()
{
    
    
	string arr[] = {
    
     "苹果", "西瓜", "香蕉", "草莓", "苹果", "西瓜", 
		"苹果", "苹果", "西瓜", "苹果", "香蕉", "苹果", "香蕉" };
		
	UnorderedMap<string, int> countMap;
	for (auto& e : arr)
	{
    
    
		countMap[e]++;
	}
	for (const auto& kv : countMap)
	{
    
    
		cout << kv.first << ":" << kv.second << endl;
	}
}

コードの実行結果は次のとおりです。
ここに画像の説明を挿入します
コードの実行結果は期待どおりなので、const イテレータがどのように実装されているかを見てみましょう。

定数反復子

以前の経験を根絶し、テンプレート パラメーターを追加することで const イテレーターと通常のイテレーターを統合できることがわかりました。次に、最初にイテレーターのテンプレート パラメーターを変更し、2 つの演算子の逆参照を変更すると、次のコードを取得できます。

template<class K, class T,class Ref,class Ptr, class KeyOfT, class Hash>
struct _HashIterator
{
    
    
	typedef HashNode<T> Node;
	typedef _HashIterator<K, T, Ref,Ptr,KeyOfT, Hash> Self;
	typedef BucketTable<K, T, KeyOfT, Hash> Bt;
		Node* _node;
	Bt* _bt;
	_HashIterator(Node* node,Bt* bt)
		:_node(node)
		,_bt(bt)
	{
    
    }
	Self& operator++()
	{
    
    //....return *this;}	
	Ref operator *()
	{
    
    return _node->_data;}
	Ptr operator ->()
	{
    
    return &_node->_data;}
	bool operator !=(const Self& s) const
	{
    
    return _node != s._node;}
};

次に、ハッシュ バケット クラスのコードを変更し、const イテレータを作成し、begin 関数と end 関数の const バージョンを追加します。ここでのコードはおおよそ次のとおりです。

template<class K, class T, class KeyOfT,class Hash >
class BucketTable
{
    
    
public:
	typedef HashNode<T> Node;
	template<class K, class T, class Ref, class Ptr, class KeyOfT, class Hash>
	friend struct _HashIterator;
	BucketTable()
		:_n(0)
	{
    
    
		_tables.resize(__stl_next_prime(_tables.size()));
	}
	typedef _HashIterator<K, T, T&, T*, KeyOfT, Hash> iterator;
	typedef _HashIterator<K, T,const T&,const T* ,KeyOfT, Hash> const_iterator;
	iterator begin()
	{
    
    //...}
	iterator end()
	{
    
    //...}
	const_iterator begin() const
	{
    
    //...}
	const_iterator end() const 
	{
    
    //...}
	iterator Find(const K& key)
	{
    
    //...}
	bool erase(const K& key)
	{
    
    //...}
	~BucketTable()
	{
    
    //...}
	inline unsigned long __stl_next_prime(unsigned long n)
	{
    
    //...}
private:
	vector<Node*> _tables;
	size_t _n;
};

次に、次のコードを使用してテストできます。

void test4(const UnorderedSet<int>& us)
{
    
    
	UnorderedSet<int>::const_iterator it = us.begin();
	while (it != us.end())
	{
    
    
		cout << *it << endl;
		it++;
	}
	cout << endl;
}
int main()
{
    
    
	UnorderedSet<int> us;
	us.insert(1);
	us.insert(2);
	us.insert(3);
	us.insert(4);
	test4(us);
	return 0;
}

このコードを実行した結果は次のようになります。
ここに画像の説明を挿入します

上記の実装方法に問題があることがわかりました。問題はコンストラクタにありました。begin 関数を使用して const イテレータを作成しました。const で変更されているため、ここで呼び出される begin は const 型の begin です。

const_iterator begin() const
{
    
    
	for (int i = 0; i < _tables.size(); i++)
	{
    
    
		if (_tables[i])
		{
    
    
			return const_iterator(_tables[i], this);
		}
	}
}

ここの begin 関数は、空ではない最初のノードを見つけて const イテレータを構築します。これは const バージョンであるため、ここで使用される _tables[i] も const バージョンです。_tables はベクトル コンテナであり、const バージョンでもあります角括弧の戻り値は構築のためにここで呼び出され、ベクトルの角括弧のオーバーロードも 2 つのバージョンに分割されます: したがって、
ここに画像の説明を挿入します
ここでは const バージョンが返され、反復子は通常のバージョンのポインターを使用して構築されます。

_HashIterator(Node* node,Bt* bt)
		:_node(node)
		,_bt(bt)
	{
    
    }

したがって、ここでのイテレータ オブジェクトの作成は失敗します。ここでノード ポインタに const を追加しても、問題は解決できません。次のエラーが報告されます。

_HashIterator( const Node* node,const Bt* bt)
		:_node(node)
		,_bt(bt)
	{
    
    }

ここに画像の説明を挿入します

ここのパーミッションが拡大されているので問題があるのですが、_nodeもconst属性になると言ったらどうなるでしょうか?例えば:

const Node* _node;
const Bt* _bt;
_HashIterator(const Node* node,Bt* bt)
	:_node(node)
	,_bt(bt)
{
    
    }

この場合、通常のイテレータは内部の内容を変更できますか? それが機能しないので、ここでの解決策は、const イテレータと通常のイテレータを別々に定義することです。たとえば、次のコードです。

template<class K, class T, class KeyOfT, class Hash>
struct const_HashIterator
{
    
    
	typedef HashNode<T> Node;
	typedef const_HashIterator<K, T, KeyOfT, Hash> Self;
	typedef BucketTable<K, T, KeyOfT, Hash> Bt;
	const Node* _node;
	const Bt* _bt;
	const_HashIterator(const Node* node, const Bt* bt)
		:_node(node)
		, _bt(bt)
	{
    
    }
	Self& operator++()
	{
    
    }
	const T& operator *()
	{
    
    }
	const T* operator ->()
	{
    
    }
	bool operator !=(const Self& s) const
	{
    
    }
};

このようにして、上記のコードを実行しても問題は発生しません。
ここに画像の説明を挿入します
つまり、この記事の内容はここで完了です。すべてのコードは次のとおりです。

//哈希桶文件的代码
#pragma once
#include<iostream>
#include<vector>
using namespace std;
template< class T>
struct HashNode
{
    
    
	HashNode(const T& data)
		:_data(data)
		, _next(nullptr)
	{
    
    }
	T _data;
	HashNode<T>* _next;
};
template<class K>
struct HashFunc
{
    
    
	size_t operator()(const K& key)
	{
    
    
		return (size_t)key;
	}
};
template<>
struct HashFunc<string>
{
    
    
	size_t operator()(const string& s)
	{
    
    
		size_t res = 0;
		for (auto& ch : s)
		{
    
    
			res *= 131;
			res += ch;
		}
		return res;
	}
};
template<class K, class T, class KeyOfT, class Hash >
class BucketTable;//前置声明

template<class K, class T, class KeyOfT, class Hash>
struct const_HashIterator
{
    
    
	typedef HashNode<T> Node;
	typedef const_HashIterator<K, T, KeyOfT, Hash> Self;
	typedef BucketTable<K, T, KeyOfT, Hash> Bt;
	const Node* _node;
	const Bt* _bt;
	const_HashIterator(const Node* node, const Bt* bt)
		:_node(node)
		, _bt(bt)
	{
    
    }
	Self& operator++()
	{
    
    
		if (_node->_next)
		{
    
    
			//当前节点的下一个节点为空
			_node = _node->_next;
		}
		else
		{
    
    
			KeyOfT kot;
			Hash hf;
			size_t hashi = hf(kot(_node->_data)) % _bt->_tables.size();
			++hashi;
			while (hashi < _bt->_tables.size())
			{
    
    
				if (_bt->_tables[hashi])
				{
    
    
					_node = _bt->_tables[hashi];
					break;
				}
				else
				{
    
    
					hashi++;
				}
			}
			if (hashi == _bt->_tables.size())
			{
    
    
				_node = nullptr;
			}
		}
		return *this;
	}
	const T& operator *()
	{
    
    
		return _node->_data;
	}
	const T* operator ->()
	{
    
    
		return &_node->_data;
	}
	bool operator !=(const Self& s) const
	{
    
    
		return _node != s._node;
	}
};

template<class K, class T, class KeyOfT, class Hash>
struct _HashIterator
{
    
    
	typedef HashNode<T> Node;
	typedef _HashIterator<K, T,KeyOfT, Hash> Self;
	typedef BucketTable<K, T, KeyOfT, Hash> Bt;
	Node* _node;
	Bt* _bt;
	_HashIterator( Node* node,Bt* bt)
		:_node(node)
		,_bt(bt)
	{
    
    }
	Self& operator++()
	{
    
    
		if (_node->_next)
		{
    
    
			//当前节点的下一个节点为空
			_node = _node->_next;
		}
		else
		{
    
    
			KeyOfT kot;
			Hash hf;
			size_t hashi = hf(kot(_node->_data)) % _bt->_tables.size();
			++hashi;
			while (hashi < _bt->_tables.size())
			{
    
    
				if (_bt->_tables[hashi])
				{
    
    
					_node = _bt->_tables[hashi];
					break;
				}
				else
				{
    
    
					hashi++;
				}
			}
			if (hashi == _bt->_tables.size())
			{
    
    
				_node = nullptr;
			}
		}
		return *this;
	}	
	T& operator *()
	{
    
    
		return _node->_data;
	}
	T* operator ->()
	{
    
    
		return &_node->_data;
	}
	bool operator !=(const Self& s) const
	{
    
    
		return _node != s._node;
	}
};
template<class K, class T, class KeyOfT,class Hash >
class BucketTable
{
    
    
public:	
	typedef HashNode<T> Node;
	template<class K, class T, class KeyOfT, class Hash>
	friend struct _HashIterator;

	template<class K, class T, class KeyOfT, class Hash>
	friend struct const_HashIterator;
	BucketTable()
		:_n(0)
	{
    
    
		_tables.resize(__stl_next_prime(_tables.size()));
	}
	typedef _HashIterator<K, T, KeyOfT, Hash> iterator;
	typedef const_HashIterator<K, T,KeyOfT, Hash> const_iterator;
	iterator begin()
	{
    
    
		for (int i = 0; i < _tables.size(); i++)
		{
    
    
			if (_tables[i])
			{
    
    
				return iterator(_tables[i], this);
			}
		}
	}
	iterator end()
	{
    
    
		return iterator(nullptr, this);
	}
	const_iterator begin() const
	{
    
    
		for (int i = 0; i < _tables.size(); i++)
		{
    
    
			if (_tables[i])
			{
    
    
				return const_iterator(_tables[i], this);
			}
		}
	}
	const_iterator end() const 
	{
    
    
		const_iterator it1(nullptr, this);
		return it1;
	}
	iterator Find(const K& key)
	{
    
    
		Hash hf;
		size_t pos = hf(key) % _tables.size();
		KeyOfT kot;
		Node* cur = _tables[pos];
		while (cur)
		{
    
    
			if (kot(cur->_data) == key)
			{
    
    
				return iterator(cur,this);
			}
			else
			{
    
    
				cur = cur->_next;
			}
		}
		return iterator(nullptr,this);
	}
	pair<iterator,bool> insert(const T& data)
	{
    
    
		KeyOfT kot;
		iterator it = Find(kot(data));
		if (it!=end())
		{
    
    
			return make_pair(it,false);
		}
		if (_n / _tables.size() == 1)//平衡因子为1就更新
		{
    
    
			vector<Node*> newBH;
			newBH.resize(__stl_next_prime(_tables.size()));
			for (int i = 0; i < _tables.size(); i++)
			{
    
    
				Node* cur = _tables[i];
				while (cur)
				{
    
    
					Node* next = cur->_next;
					size_t pos = Hash()(kot(cur->_data));
					cur->_next = newBH[pos];
					newBH[pos] = cur;
					cur = next;
				}
				_tables[i] = nullptr;
			}
		}
		Hash hf;
		size_t pos = hf(kot(data)) % _tables.size();
		Node* newnode = new HashNode< T>(data);
		newnode->_next = _tables[pos];
		_tables[pos] = newnode;
		++_n;
		return make_pair(iterator(newnode,this),true) ;
	}
	bool erase(const K& key)
	{
    
    
		HashFunc<K> HF;
		KeyOfT kot;
		size_t pos = HF(key) % _tables.size();
		Node* cur = _tables[pos];
		Node* prev = cur;
		while (cur)
		{
    
    
			if (kot(cur->_data) == key)
			{
    
    
				if (cur == _tables[pos])
				{
    
    
					_tables[pos] = cur->_next;
				}
				else
				{
    
    
					prev->_next = cur->_next;
				}
				delete cur;
				_n--;
				return true;
			}
			else
			{
    
    
				prev = cur;
				cur = cur->_next;
			}
		}
		return false;
	}
	~BucketTable()
	{
    
    
		for (int i = 0; i < _tables.size(); i++)
		{
    
    
			Node* cur = _tables[i];
			while (cur)
			{
    
    
				Node* next = cur->_next;
				delete cur;
				cur = next;
			}
			_tables[i] = nullptr;
		}
	}
	inline unsigned long __stl_next_prime(unsigned long n)
	{
    
    
		static const int __stl_num_primes = 28;
		static const unsigned long __stl_prime_list[__stl_num_primes] =
		{
    
    
			53, 97, 193, 389, 769,
			1543, 3079, 6151, 12289, 24593,
			49157, 98317, 196613, 393241, 786433,
			1572869, 3145739, 6291469, 12582917, 25165843,
			50331653, 100663319, 201326611, 402653189, 805306457,
			1610612741, 3221225473, 4294967291
		};
		for (int i = 0; i < __stl_num_primes; ++i)
		{
    
    
			if (__stl_prime_list[i] > n)
			{
    
    
				return __stl_prime_list[i];
			}
		}
		return __stl_prime_list[__stl_num_primes - 1];
	}


private:
	vector<Node*> _tables;
	size_t _n;
};
//封装的set的代码
#include"HashBucket.h"

template<class K,class HashFunc=HashFunc<K>>
class UnorderedSet
{
    
    
public:
	
	struct SetOfT
	{
    
    
		const K& operator()(const K& key)
		{
    
    
			return key;
		}
	};
	typedef typename BucketTable<K, K, SetOfT, HashFunc>::iterator iterator;
	typedef typename BucketTable<K, K, SetOfT, HashFunc>::const_iterator const_iterator;
	iterator begin()
	{
    
    
		return _ht.begin();
	}
	iterator end()
	{
    
    
		return _ht.end();
	}
	const_iterator begin() const 
	{
    
    
		return _ht.begin();
	}
	const_iterator end() const
	{
    
    
		return _ht.end();
	}
	pair<iterator,bool> insert(const K& key)
	{
    
    
		return _ht.insert(key);
	}
	bool erase(const K& key)
	{
    
    
		return _ht.erase(key);
	}
	iterator find(const K& key)
	{
    
    
		return _ht.Find(key);
	}
private:
	BucketTable<K, K, SetOfT, HashFunc> _ht;
};
//封装的map的代码
#include"HashBucket.h"
template<class K, class T,class HashFunc = HashFunc<K>>
class UnorderMap
{
    
    
public:
	
	struct MapOfT
	{
    
    
		const K& operator()(const pair<const K,T>& key)
		{
    
    
			return key.first;
		}
	};
	typedef typename BucketTable<K, pair<const K,T>, MapOfT, HashFunc>::iterator iterator;
	typedef typename BucketTable<K, K, MapOfT, HashFunc>::const_iterator const_iterator;
	iterator begin()
	{
    
    
		return _ht.begin();
	}
	iterator end()
	{
    
    
		return _ht.end();
	}
	const_iterator begin() const
	{
    
    
		return _ht.begin();
	}
	const_iterator end() const
	{
    
    
		return _ht.end();
	}
	pair<iterator,bool> insert(const pair< const K,T>& data)
	{
    
    
		return _ht.insert(data);
	}
	bool erase(const K& key)
	{
    
    
		return _ht.erase(key);
	}
	iterator find(const K& key)
	{
    
    
		return _ht.Find(key);
	}
	T& operator[](const K& key)
	{
    
    
		pair<iterator, bool> ret = _ht.insert(make_pair(key, T()));
		return ret.first->second;
	}
private:
	BucketTable<K, pair<const K,T>, MapOfT, HashFunc> _ht;
};

Supongo que te gusta

Origin blog.csdn.net/qq_68695298/article/details/131464716
Recomendado
Clasificación