天天看點

C++下“hash“的簡單模拟實作以及實作中遇見的問題1.線性探測2.二次探測3.載荷因子4.擴容5.删除(查找,删除,判斷狀态)6.插入特殊值時7.哈希沖突8.開散列9.key對象的要求10.套用map和set11.延伸:擴容12.最終代碼

目錄

  • 1.線性探測
  • 2.二次探測
  • 3.載荷因子
  • 4.擴容
    • 擴容一:
  • 5.删除(查找,删除,判斷狀态)
  • 6.插入特殊值時
    • 初試(字元串)
    • BKDR(解決字元串)
    • BKDR之後的負數和int
    • 預設的不需要傳第三個參數
  • 7.哈希沖突
  • 8.開散列
    • insert
    • Erase
    • 開散列整體代碼
  • 9.key對象的要求
  • 10.套用map和set
    • insert套用
    • ++
      • UnorderedSet.h
      • UnorderedMap.h
      • HashTable.h
    • map[]
    • 拷貝構造
    • 深拷貝
    • 析構函數
  • 11.延伸:擴容
  • 12.最終代碼
    • UnorderedSet.h
    • UnorderedSet.h
    • HashTable.h

1.線性探測

bool Insert(const pair<K, V>& kv)
	{
		size_t start = kv.first % _tables.size();
		size_t i = 0;
		size_t index = start + i;
		while (_tables[index].first == EXITS)
		{
			i++;
			index = start + i;
			index %= _tables;
		}

		_tables[index]._kv = kv;
		_table[index]._status = EXIST;
		++_n;

	}
           
C++下“hash“的簡單模拟實作以及實作中遇見的問題1.線性探測2.二次探測3.載荷因子4.擴容5.删除(查找,删除,判斷狀态)6.插入特殊值時7.哈希沖突8.開散列9.key對象的要求10.套用map和set11.延伸:擴容12.最終代碼

2.二次探測

bool Insert(const pair<K, V>& kv)
	{
		size_t start = kv.first % _tables.size();
		size_t i = 0;
		size_t index = start + i;
		while (_tables[index].first == EXITS)
		{
			i++;
			index = start + i*i;
			index %= _tables;
		}

		_tables[index]._kv = kv;
		_table[index]._status = EXIST;
		++_n;

	}
           
C++下“hash“的簡單模拟實作以及實作中遇見的問題1.線性探測2.二次探測3.載荷因子4.擴容5.删除(查找,删除,判斷狀态)6.插入特殊值時7.哈希沖突8.開散列9.key對象的要求10.套用map和set11.延伸:擴容12.最終代碼

3.載荷因子

引入:當線性探測和二次探測中,清單中的值滿了,或者将近滿的時候,會導緻效率降低

問題:哈希表什麼情況下進行擴容?如何擴容?

散清單的載荷因子定義為:α=填入表中的元素個數/散清單的長度

α 是散清單裝滿程度的标志因子,由于表長是定值,α與“填入表中的元素個數”成正比,是以α越大,表明填入表中的元素越多,産生沖突的可能性就越大;反之,α越小,表明填入表中的元素越少,産生沖突的可能性就越小,實際上,散清單的平均查找長度是載荷因子的α的函數,隻是不同處理沖突的方法有不同的函數。

對于開放定址法,載荷因子是特别重要因素,應嚴格限制在 0.7到0.8以下,超多0.8,查表時的CPU緩存不命中按照指數曲線上升,是以,一些采用開放定址法的hash庫,如Java的系統庫限制了載荷因子為0.75,超過此值将resize散清單。

4.擴容

bool Insert(const pair<K, V>& kv)
	{
		//負載因子到0.7,擴容
		//負載因子越小,沖突機率越低,效率越高,空間浪費越多
		//負載因子越大,沖突機率越高,效率越低,空間浪費越少
		if (_tables.size()==0 || _n*10 / _tables.size() >= 7)
		{
			//擴容
			size_t newSize = _tables.size() == 0 ? 10 : _tables.size() * 2;
			//如何擴容
		}

		//線性探測和二次探測代碼省略。。。
	}
           

問題:當我們擴容時,原來的資料該如何?

C++下“hash“的簡單模拟實作以及實作中遇見的問題1.線性探測2.二次探測3.載荷因子4.擴容5.删除(查找,删除,判斷狀态)6.插入特殊值時7.哈希沖突8.開散列9.key對象的要求10.套用map和set11.延伸:擴容12.最終代碼

擴容一:

#pragma once
#include<vector>

enum Status
{
	EXIST,
	EMPTY,
	DELETE
};

template<class K, class V>
struct HashData
{
	pair<K, V> _kv;
	Status _status = EMPTY;
};



template<class K, class V>
class HashTable
{
public:
	bool Insert(const pair<K, V>& kv)
	{
		//負載因子到0.7,擴容
		//負載因子越小,沖突機率越低,效率越高,空間浪費越多
		//負載因子越大,沖突機率越高,效率越低,空間浪費越少
		if (_tables.size() == 0 || _n * 10 / _tables.size() >= 7)
		{
			//擴容
			size_t newSize = _tables.size() == 0 ? 10 : _tables.size() * 2;

			HashTable<K, V> newHT;
			newHT._tables.resize(newSize);
			for (size_t i = 0; i < _tables.size(); ++i)
			{
				if (_tables[i]._status == EXIST)
				{
					newHT.Insert(_tables[i]._kv);
				}
			}
			_tables.swap(newHT._tables);
		}

		size_t start = kv.first % _tables.size();
		size_t i = 0;
		size_t index = start;
		while (_tables[index]._status == EXIST)
		{
			++i;
			//index = start + i * i;//二次探測
			index = start + i;//線性探測
			index %= _tables.size();
		}

		_tables[index]._kv = kv;
		_tables[index]._status = EXIST;
		++_n;

		return true;
	}

private:
	vector<HashData<K, V>> _tables;
	size_t _n = 0;//有效資料個數
};

void TestHashTables()
{
	HashTable<int, int> ht;
	int a[] = { 2, 12, 22, 32, 42, 52, 62 };
	for (auto e : a)
	{
		ht.Insert(make_pair(e, e));
	}
	ht.Insert(make_pair(72, 72));

}
           
C++下“hash“的簡單模拟實作以及實作中遇見的問題1.線性探測2.二次探測3.載荷因子4.擴容5.删除(查找,删除,判斷狀态)6.插入特殊值時7.哈希沖突8.開散列9.key對象的要求10.套用map和set11.延伸:擴容12.最終代碼

5.删除(查找,删除,判斷狀态)

#pragma once
#include<vector>

enum Status
{
	EXIST,
	EMPTY,
	DELETE
};

template<class K,class V>
struct HashData
{
	pair<K, V> _kv;
	Status _status = EMPTY;
};



template<class K, class V>
class HashTable
{
public:
	bool Erase(const K& key)
	{
		HashData<K, V>* ret = Find(key);
		if (ret)
		{
			ret->_status = DELETE;
			--_n;
			return true;
		}
		return false;
	}

	HashData<K, V>* Find(const K& key)
	{
		if (_tables.size() == 0)
			return nullptr;
		size_t start = key % _tables.size();
		size_t i = 0;
		size_t index = start;
		while (_tables[index]._status != EMPTY)
		{
			if (_tables[index]._kv.first == key && _tables[index]._status==EXIST)
			{
				return &_tables[index];
			}
			++i;
			//index = start + i * i;//二次探測
			index = start + i;//線性探測
			index %= _tables.size();
		}
		return nullptr;
	}


	bool Insert(const pair<K, V>& kv)
	{
		//負載因子到0.7,擴容
		//負載因子越小,沖突機率越低,效率越高,空間浪費越多
		//負載因子越大,沖突機率越高,效率越低,空間浪費越少
		if (_tables.size()==0 || _n*10 / _tables.size() >= 7)
		{
			//擴容
			size_t newSize = _tables.size() == 0 ? 10 : _tables.size() * 2;

			HashTable<K, V> newHT;
			newHT._tables.resize(newSize);
			for (size_t i = 0; i < _tables.size(); ++i)
			{
				if (_tables[i]._status == EXIST)
				{
					newHT.Insert(_tables[i]._kv);
				}
			}
			_tables.swap(newHT._tables);
		}

		size_t start = kv.first % _tables.size();
		size_t i = 0;
		size_t index = start;
		while (_tables[index]._status == EXIST)
		{
			++i;
			//index = start + i * i;//二次探測
			index = start + i;//線性探測
			index %= _tables.size();
		}

		_tables[index]._kv = kv;
		_tables[index]._status = EXIST;
		++_n;

		return true;
	}

private:
	vector<HashData<K, V>> _tables;
	size_t _n=0;//有效資料個數
};

void TestHashTables()
{
	HashTable<int, int> ht;
	int a[] = { 2,12,22,32,42,52,62 }; 
	for (auto e : a)
	{
		ht.Insert(make_pair(e,e));
	}
	ht.Insert(make_pair(72, 72));
	ht.Insert(make_pair(42,42));//測試查找重複值

	cout << ht.Find(12) << endl;
	ht.Erase(12);
	cout << ht.Find(12) << endl;
}
           

6.插入特殊值時

初試(字元串)

當插入的是字元串時,

struct HashStr
{
	size_t operator()(const string& s)
	{
		size_t value = 0;
		for (auto ch : s)
		{
			value += ch;
		}
		return value;
	}
};

void Hashstring()
{
	HashStr hs;
	cout << hs("left") << endl;
	cout << hs("right") << endl;
	cout << hs("abcd") << endl;
	cout << hs("aadd") << endl;
	cout << hs("acbd") << endl;
}
           

缺點:

C++下“hash“的簡單模拟實作以及實作中遇見的問題1.線性探測2.二次探測3.載荷因子4.擴容5.删除(查找,删除,判斷狀态)6.插入特殊值時7.哈希沖突8.開散列9.key對象的要求10.套用map和set11.延伸:擴容12.最終代碼

BKDR(解決字元串)

struct HashStr
{
	size_t operator()(const string& s)
	{
		//BKDR
		size_t value=0;
		for (auto ch : s)
		{
			value *= 31;
			value += ch;
		}
		return value;
	}
};
void Hashstring()
{
	HashStr hs;
	cout << hs("left") << endl;
	cout << hs("right") << endl;
	cout << hs("abcd") << endl;
	cout << hs("aadd") << endl;
	cout << hs("acbd") << endl;
}
           
C++下“hash“的簡單模拟實作以及實作中遇見的問題1.線性探測2.二次探測3.載荷因子4.擴容5.删除(查找,删除,判斷狀态)6.插入特殊值時7.哈希沖突8.開散列9.key對象的要求10.套用map和set11.延伸:擴容12.最終代碼

BKDR之後的負數和int

/begin//

此處為相比之前的修改或難點

//end

#pragma once
#include<vector>

enum Status
{
	EXIST,
	EMPTY,
	DELETE
};

template<class K,class V>
struct HashData
{
	pair<K, V> _kv;
	Status _status = EMPTY;
};
//begin/
template<class K>
struct Hash
{
	size_t operator()(const K& key)
	{
		return key;
	}
};
//end/

struct HashStr
{
	size_t operator()(const string& s)
	{
		//BKDR
		size_t value=0;
		for (auto ch : s)
		{
			value *= 31;
			value += ch;
		}
		return value;
	}
};
/begin//
template<class K, class V, class HashFunc>
/end//
class HashTable
{
public:
	bool Erase(const K& key)
	{
		HashData<K, V>* ret = Find(key);
		if (ret)
		{
			ret->_status = DELETE;
			--_n;
			return true;
		}
		return false;
	}

	HashData<K, V>* Find(const K& key)
	{
		if (_tables.size() == 0)
			return nullptr;

		HashFunc hf;
		size_t start = hf(key) % _tables.size();
		size_t i = 0;
		size_t index = start;
		while (_tables[index]._status != EMPTY)
		{
			if (_tables[index]._kv.first == key && _tables[index]._status==EXIST)
			{
				return &_tables[index];
			}
			++i;
			//index = start + i * i;//二次探測
			index = start + i;//線性探測
			index %= _tables.size();
		}
		return nullptr;
	}


	bool Insert(const pair<K, V>& kv)
	{
		//負載因子到0.7,擴容
		//負載因子越小,沖突機率越低,效率越高,空間浪費越多
		//負載因子越大,沖突機率越高,效率越低,空間浪費越少
		if (_tables.size()==0 || _n*10 / _tables.size() >= 7)
		{
			//擴容
			size_t newSize = _tables.size() == 0 ? 10 : _tables.size() * 2;

			HashTable<K, V,HashFunc> newHT;
			newHT._tables.resize(newSize);
			for (size_t i = 0; i < _tables.size(); ++i)
			{
				if (_tables[i]._status == EXIST)
				{
					newHT.Insert(_tables[i]._kv);
				}
			}
			_tables.swap(newHT._tables);
		}

		HashFunc hf;
		size_t start = hf(kv.first) % _tables.size();
		size_t i = 0;
		size_t index = start;
		while (_tables[index]._status == EXIST)
		{
			++i;
			//index = start + i * i;//二次探測
			index = start + i;//線性探測
			index %= _tables.size();
		}

		_tables[index]._kv = kv;
		_tables[index]._status = EXIST;
		++_n;

		return true;
	}

private:
	vector<HashData<K, V>> _tables;
	size_t _n=0;//有效資料個數
};

void test3()
{
	Hash<int> hint;
	cout << hint(-9) << endl;
	cout << hint(9) << endl;
}

           
C++下“hash“的簡單模拟實作以及實作中遇見的問題1.線性探測2.二次探測3.載荷因子4.擴容5.删除(查找,删除,判斷狀态)6.插入特殊值時7.哈希沖突8.開散列9.key對象的要求10.套用map和set11.延伸:擴容12.最終代碼

當傳入int時,也可以隐式類型轉化

C++下“hash“的簡單模拟實作以及實作中遇見的問題1.線性探測2.二次探測3.載荷因子4.擴容5.删除(查找,删除,判斷狀态)6.插入特殊值時7.哈希沖突8.開散列9.key對象的要求10.套用map和set11.延伸:擴容12.最終代碼

預設的不需要傳第三個參數

采用特化

//特化
template<>
struct Hash<string>
{
	size_t operator()(const string& s)
	{
		//BKDR
		size_t value = 0;
		for (auto ch : s)
		{
			value *= 31;
			value += ch;
		}
		return value;
	}

};

//struct HashStr
//{
//	size_t operator()(const string& s)
//	{
//		//BKDR
//		size_t value=0;
//		for (auto ch : s)
//		{
//			value *= 31;
//			value += ch;
//		}
//		return value;
//	}
//};

//...

void test4()
{
	HashTable<string, string> ht;
	ht.Insert(make_pair("red", "紅色"));
	ht.Insert(make_pair("blue", "藍色"));
}
           

7.哈希沖突

閉散列/開放定址法:不同位置沖突的資料會互相影響,相鄰位置的沖突會争搶位置,互相影響效率。

優化

開散列/拉鍊法/哈希桶:相鄰位置沖突,不會再争搶位置,不會互相影響

C++下“hash“的簡單模拟實作以及實作中遇見的問題1.線性探測2.二次探測3.載荷因子4.擴容5.删除(查找,删除,判斷狀态)6.插入特殊值時7.哈希沖突8.開散列9.key對象的要求10.套用map和set11.延伸:擴容12.最終代碼

控制負載因子–負載因子到了就擴容

極端場景防止一個桶位置沖突太多,連結清單太長

當一個桶長度超過一定值以後,轉化紅黑樹

補充:不僅可以尾插,也可以頭插,頭插更友善點。

8.開散列

思路參考上一個标題“哈希沖突”,不同點:這裡是頭插

insert

namespace LinkHash
{
	template<class K,class V>
	struct HashNode
	{
		pair<K, V> _kv;
		HashNode<K, V>* _next = nullptr;

		HashNode(const pair<K,V>& kv)
			:_kv(kv)
			,_next(nullptr)
		{}
	};
	template<class K>
	struct Hash
	{
		size_t operator()(const K& key)
		{
			return key;
		}
	};

	//特化
	template<>
	struct Hash<string>
	{
		size_t operator()(const string& s)
		{
			//BKDR
			size_t value = 0;
			for (auto ch : s)
			{
				value *= 31;
				value += ch;
			}
			return value;
		}

	};


	template<class K,class V,class HashFunc = Hash<K>>
	class HashTable
	{
		typedef HashNode<K, V> Node;
	public:

		Node* Find(const K& key)
		{
			if (_tables.empty())
				return nullptr;

			HashFunc hf;
			size_t index = hf(key) % _tables.size();
			Node* cur = _tables[index];
			while (cur)
			{
				if (cur->_kv.first == key)
					return cur;
				cur = cur->_next;
			}
			return nullptr;
		}
		bool Insert(const pair<K, V>& kv)
		{
			Node* ret = Find(kv.first);
			if (ret)
				return false;
			if (_n ==  _tables.size())
			{
				size_t newSize = _tables.size() == 0 ? 10 : 2 * _tables.size();

				vector<Node*> newTables;
				newTables.resize(newSize);
				for (size_t i = 0; i < _tables.size(); i++)
				{
					Node* cur = _tables[i];
					while(cur)
					{
						Node* next = cur->_next;
						HashFunc hf;
						size_t index = hf(cur->_kv.first) % newTables.size();
						//頭插,效率比較高
						cur->_next = newTables[index];
						newTables[index] = cur;

						cur = next;

					}
					_tables[i] = nullptr;
				}
				newTables.swap(_tables);
			}
			HashFunc hf;
			size_t index = hf(kv.first) % _tables.size();
			Node* newnode = new Node(kv);
			//頭插
			newnode->_next = _tables[index];
			_tables[index] = newnode;

			++_n;
			return true;
		}

	private:
		vector<Node*> _tables;
		size_t _n=0;//有效資料個數
	};

	void TestHashTable()
	{
		int a[] = { 2,12,22,32,42,52,62,72,82,5,15,25};
		HashTable<int, int> ht;
		for (auto e : a)
		{
			ht.Insert(make_pair(e,e));
		}

		ht.Insert(make_pair(1, 1));
	}
}
           

Erase

bool Erase(const K& key)
		{
			if (_tables.empty())
				return false;

			HashFunc hf;
			size_t index = hf(key) % _tables.size();
			Node* prev = nullptr;
			Node* cur = _tables[index];
			while (cur)
			{
				if (cur->_kv.first == key)
				{
					if (prev == nullptr) {
						//頭删
						_tables[index] = cur->_next;
					}
					else {
						//中間删除and尾删
						prev->_next = cur->_next;
					}
					--_n;
					delete cur;
					return true;
				}
				else
				{
					prev = cur;
					cur = cur->_next;
				}
			}
			return false;
		}
           

開散列整體代碼

namespace LinkHash
{
	template<class K,class V>
	struct HashNode
	{
		pair<K, V> _kv;
		HashNode<K, V>* _next = nullptr;

		HashNode(const pair<K,V>& kv)
			:_kv(kv)
			,_next(nullptr)
		{}
	};
	template<class K>
	struct Hash
	{
		size_t operator()(const K& key)
		{
			return key;
		}
	};

	//特化
	template<>
	struct Hash<string>
	{
		size_t operator()(const string& s)
		{
			//BKDR
			size_t value = 0;
			for (auto ch : s)
			{
				value *= 31;
				value += ch;
			}
			return value;
		}

	};


	template<class K,class V,class HashFunc = Hash<K>>
	class HashTable
	{
		typedef HashNode<K, V> Node;
	public:
		bool Erase(const K& key)
		{
			if (_tables.empty())
				return false;

			HashFunc hf;
			size_t index = hf(key) % _tables.size();
			Node* prev = nullptr;
			Node* cur = _tables[index];
			while (cur)
			{
				if (cur->_kv.first == key)
				{
					if (prev == nullptr) {
						//頭删
						_tables[index] = cur->_next;
					}
					else {
						//中間删除and尾删
						prev->_next = cur->_next;
					}
					--_n;
					delete cur;
					return true;
				}
				else
				{
					prev = cur;
					cur = cur->_next;
				}
			}
			return false;
		}

		Node* Find(const K& key)
		{
			if (_tables.empty())
				return nullptr;

			HashFunc hf;
			size_t index = hf(key) % _tables.size();
			Node* cur = _tables[index];
			while (cur)
			{
				if (cur->_kv.first == key)
					return cur;
				cur = cur->_next;
			}
			return nullptr;
		}
		bool Insert(const pair<K, V>& kv)
		{
			Node* ret = Find(kv.first);
			if (ret)
				return false;
			if (_n ==  _tables.size())
			{
				size_t newSize = _tables.size() == 0 ? 10 : 2 * _tables.size();

				vector<Node*> newTables;
				newTables.resize(newSize);
				for (size_t i = 0; i < _tables.size(); i++)
				{
					Node* cur = _tables[i];
					while(cur)
					{
						Node* next = cur->_next;
						HashFunc hf;
						size_t index = hf(cur->_kv.first) % newTables.size();
						//頭插,效率比較高
						cur->_next = newTables[index];
						newTables[index] = cur;

						cur = next;

					}
					_tables[i] = nullptr;
				}
				newTables.swap(_tables);
			}
			HashFunc hf;
			size_t index = hf(kv.first) % _tables.size();
			Node* newnode = new Node(kv);
			//頭插
			newnode->_next = _tables[index];
			_tables[index] = newnode;

			++_n;
			return true;
		}

	private:
		vector<Node*> _tables;
		size_t _n=0;//有效資料個數
	};

	void TestHashTable()
	{
		int a[] = { 2,12,22,32,42,52,62,72,82,5,15,25};
		HashTable<int, int> ht;
		for (auto e : a)
		{
			ht.Insert(make_pair(e,e));
		}

		ht.Insert(make_pair(1, 1));
	}
}
           

9.key對象的要求

C++下“hash“的簡單模拟實作以及實作中遇見的問題1.線性探測2.二次探測3.載荷因子4.擴容5.删除(查找,删除,判斷狀态)6.插入特殊值時7.哈希沖突8.開散列9.key對象的要求10.套用map和set11.延伸:擴容12.最終代碼

10.套用map和set

insert套用

#pragma once
#include"HashTable.h"

namespace sakeww
{
	template<class K,class hash= Hash<K>>
	class unordered_set
	{
		struct SetKeyOfT
		{
			const K& operator()(const K& key)
			{
				return key;
			}
		};
	public:
		bool insert(const K& key)
		{
			return _ht.Insert(key);
		}

	private:
		LinkHash::HashTable<K, K,SetKeyOfT,hash> _ht;
	};
}
           
#pragma once
#include"HashTable.h"

namespace sakeww
{
	template<class K,class V,class hash = Hash<K>>
	class unordered_map
	{
		struct MapKeyOfT
		{
			const K& operator()(const pair<K, V>& kv)
			{
				return kv.first;
			}
		};

		bool insert(const pair<K, V>& kv)
		{
			return _ht.Insert(kv);
		} 

	private:
		LinkHash::HashTable<K, pair<K, V>,MapKeyOfT,hash> _ht;
	};
}

           

++

困難點:

UnorderedSet ,HashTale二者要互相調用對方的命名空間

++的模拟實作

因為使用的是單向連結清單,是以隻能++,不能 減減

UnorderedSet.h

#pragma once
#include"HashTable.h"

namespace sakeww
{
	template<class K,class hash= Hash<K>>
	class unordered_set
	{
		struct SetKeyOfT
		{
			const K& operator()(const K& key)
			{
				return key;
			}
		};
	public:
		typedef typename LinkHash::HashTable<K, K, SetKeyOfT, hash>::iterator iterator;
		iterator begin()
		{
			return _ht.begin();
		}
		iterator end()
		{
			return _ht.end();
		}
		
		bool insert(const K& key)
		{
			return _ht.Insert(key);
		}

	private:
		LinkHash::HashTable<K, K,SetKeyOfT,hash> _ht;
	};
	
	void test2()
	{
		unordered_set<int> us;
		us.insert(2);
		us.insert(22);
		us.insert(12);
		us.insert(32);
		us.insert(42);
		us.insert(2);
		us.insert(33);
		us.insert(45);

		unordered_set<int>::iterator it = us.begin();
		while (it != us.end())
		{
			cout << *it << " ";
			++it;
		}
		cout << endl;
	}
}
           

UnorderedMap.h

#pragma once
#include "HashTable.h"

namespace sakeww
{
	template<class K, class V, class hash = Hash<K>>
	class unordered_map
	{
		struct MapKeyOfT
		{
			const K& operator()(const pair<K, V>& kv)
			{
				return kv.first;
			}
		};
	public:
		typedef typename LinkHash::HashTable<K, pair<K, V>, MapKeyOfT, hash>::iterator iterator;
		iterator begin()
		{
			return _ht.begin();
		}

		iterator end()
		{
			return _ht.end();
		}
		
		pair<iterator,bool> insert(const pair<K, V>& kv)
		{
			return _ht.Insert(kv);
		}

	private:
		LinkHash::HashTable<K, pair<K, V>, MapKeyOfT, hash> _ht;
	};
	void testMap()
	{
		unordered_map<string, string> dict;
		dict.insert(make_pair("sort", "排序"));
		dict.insert(make_pair("string", "字元串"));
		dict.insert(make_pair("map", "地圖"));
		cout << dict["sort"] << endl;//充當查找
		cout << dict["left"] << endl;//插入
		dict["right"] = "右邊";//插入和修改

		unordered_map<string, string>::iterator it = dict.begin();
		while (it != dict.end())
		{
			cout << it->first << ":" << it->second << endl;
			++it;
		}
		cout << endl;
	}
}
           

HashTable.h

namespace LinkHash
{
	template<class T>
	struct HashNode
	{
		T _data;
		HashNode<T>* _next;

		HashNode(const T& data)
			:_data(data)
			,_next(nullptr)
		{}
	};
begin//
	template<class K, class T, class KeyOfT, class HashFunc>
	class HashTable;
///end///
	template<class K, class T, class Ref, class Ptr, class KeyOfT, class HashFunc>
	struct __HTIterator
	{
		typedef HashNode<T> Node;
		typedef __HTIterator<K, T, Ref, Ptr, KeyOfT, HashFunc> Self;
		 
		Node* _node; 
		HashTable<K, T, KeyOfT, HashFunc>* _pht;

		__HTIterator(Node* node, HashTable<K, T, KeyOfT, HashFunc>* pht)
			:_node(node)
			,_pht(pht)
		{}

		Ref& operator*()
		{
			return _node->_data;
		}
		Ptr& operator->()
		{
			return &_node->_data;
		}

		Self& operator++()
		{
			if (_node->_next)
			{
				_node = _node->_next;
			}
			else
			{
				KeyOfT kot;
				HashFunc hf;
				size_t index = hf(kot(_node->_data)) % _pht->_tables.size();
				++index;

				// 找下一個不為空的桶
				while (index< _pht->_tables.size()) //_pht->_tables.size()表的大小
				{
					if (_pht->_tables[index])
					{
						break;
					}
					else
						++index;
				}

				if (index == _pht->_tables.size())
				{
					_node = nullptr;
				}
				else
				{
					_node = _pht->_tables[index];
				}
			}
			return *this;
		}

		bool operator!=(const Self& s) const
		{
			return _node != s._node;
		}

		bool operator==(const Self& s) const
		{
			return _node == s._node;
		}
	};



	template<class K,class T,class KeyOfT,class HashFunc>
	class HashTable
	{
		typedef HashNode<T> Node;
/begin/
		template<class K, class T, class Ref, class Ptr, class KeyOfT, class HashFunc>
		friend struct __HTIterator;
//end
	public:
		typedef __HTIterator<K, T, T&, T*, KeyOfT, HashFunc> iterator;

		iterator begin()
		{
			for (size_t i = 0; i < _tables.size(); i++)
			{
				if (_tables[i])
				{
					return iterator(_tables[i],this);
				}
			}
			return end();
		}
		iterator end()
		{
			return iterator(nullptr, this);
		}

		bool Erase(const K& key)
		{
			if (_tables.empty())
				return false;

			HashFunc hf;
			size_t index = hf(key) % _tables.size();
			Node* prev = nullptr;
			Node* cur = _tables[index];
			KeyOfT kot;
			while (cur)
			{
				if (kot(cur->_data) == key)
				{
					if (prev == nullptr) {
						//頭删
						_tables[index] = cur->_next;
					}
					else {
						//中間删除and尾删
						prev->_next = cur->_next;
					}
					--_n;
					delete cur;
					return true;
				}
				else
				{
					prev = cur;
					cur = cur->_next;
				}
			}
			return false;
		}

		Node* Find(const K& key)
		{
			if (_tables.empty())
				return nullptr;

			HashFunc hf;
			size_t index = hf(key) % _tables.size();
			Node* cur = _tables[index];
			KeyOfT kot;
			while (cur)
			{
				if (kot(cur->_data) == key)
					return cur;
				cur = cur->_next;
			}
			return nullptr;
		}
		bool Insert(const T& data)
		{
			KeyOfT kot;
			Node* ret = Find(kot(data));
			if (ret)
				return false;
			if (_n ==  _tables.size())
			{
				size_t newSize = _tables.size() == 0 ? 10 : 2 * _tables.size();

				vector<Node*> newTables;
				newTables.resize(newSize);
				for (size_t i = 0; i < _tables.size(); i++)
				{
					Node* cur = _tables[i];
					while(cur)
					{
						Node* next = cur->_next;
						HashFunc hf;
						size_t index = hf(kot(cur->_data)) % newTables.size();
						//頭插,效率比較高
						cur->_next = newTables[index];
						newTables[index] = cur;

						cur = next;

					}
					_tables[i] = nullptr;
				}
				newTables.swap(_tables);
			}
			HashFunc hf;
			size_t index = hf(kot(data)) % _tables.size();
			Node* newnode = new Node(data);
			//頭插
			newnode->_next = _tables[index];
			_tables[index] = newnode;

			++_n;
			return true;
		}

	private:
		vector<Node*> _tables;
		size_t _n=0;//有效資料個數
	};


}
           

map[]

V& operator[](const K& key)
		{
			auto ret = _ht.Insert(make_pair(key, V()));
			return ret.first->second;
		}
           

将傳回值改為疊代器

iterator Find(const K& key)
		{
			if (_tables.empty())
				return end();

			HashFunc hf;
			size_t index = hf(key) % _tables.size();
			Node* cur = _tables[index];
			KeyOfT kot;
			while (cur)
			{
				if (kot(cur->_data) == key)
					return iterator(cur, this);
				else
					cur = cur->_next;
			}
			return end();
		}
           

将傳回值改為 pair<iterator,bool> 類型

pair<iterator,bool> Insert(const T& data)
		{
			KeyOfT kot;
			iterator ret = Find(kot(data));
			if (ret != end())
				return make_pair(ret,false);
			if (_n ==  _tables.size())
			{
				size_t newSize = _tables.size() == 0 ? 10 : 2 * _tables.size();

				vector<Node*> newTables;
				newTables.resize(newSize);
				for (size_t i = 0; i < _tables.size(); i++)
				{
					Node* cur = _tables[i];
					while(cur)
					{
						Node* next = cur->_next;
						HashFunc hf;
						size_t index = hf(kot(cur->_data)) % newTables.size();
						//頭插,效率比較高
						cur->_next = newTables[index];
						newTables[index] = cur;

						cur = next;

					}
					_tables[i] = nullptr;
				}
				newTables.swap(_tables);
			}
			HashFunc hf;
			size_t index = hf(kot(data)) % _tables.size();
			Node* newnode = new Node(data);
			//頭插
			newnode->_next = _tables[index];
			_tables[index] = newnode;

			++_n;
			return make_pair(iterator(newnode,this), true);
		}
           

拷貝構造

HashTable(const HashTable<K, T, KeyOfT, HashFunc>& ht)
		{
			_tables.resize(ht._tables.size());
			for (size_t i = 0; i < ht._tables.size(); ++i)
			{
				Node* cur = ht._tables[i];
				while (cur)
				{
					Node* copy = new Node(cur->_data);
					copy->_next = _tables[i];
					_tables[i] = copy;
					cur = cur->_next;
				}
			}
		}
           

因為自己已經寫了拷貝構造函數,是以系統就不會預設生成一份拷貝構造函數

那麼

private:
		vector<Node*> _tables;
		size_t _n=0;//有效資料個數
           

系統就不會預設生成一份,

解決:

//方法一:
		//初始化清單,
		//HashTable()
		//{}

		//方法二:
		//顯示指定編譯器去生成預設構造函數
		HashTable() = default;

		HashTable(const HashTable<K, T, KeyOfT, HashFunc>& ht)
		{
			_tables.resize(ht._tables.size());
			for (size_t i = 0; i < ht._tables.size(); ++i)
			{
				Node* cur = ht._tables[i];
				while (cur)
				{
					Node* copy = new Node(cur->_data);
					copy->_next = _tables[i];
					_tables[i] = copy;
					cur = cur->_next;
				}
			}
		}
           

深拷貝

//可以不寫模闆參數,拷貝構造和指派可以不寫
		//const HashTable<K, T, KeyOfT, HashFunc>& operator=(const HashTable<K, T, KeyOfT, HashFunc>)
		Self& operator=(Self copy)
		{
			swap(_n, copy._n);
			_tables.swap(copy._tables);
			return *this;
		}
           

析構函數

~HashTable()
		{
			for (size_t i = 0; i < _tables.size(); ++i)
			{
				Node* cur = _tables[i];
				while (cur)
				{
					Node* next = cur->_next;
					delete cur;
					cur = next;
				}
				_tables[i] = nullptr;
			}
		}
           

11.延伸:擴容

hash的大小為素數最好(大佬證明的)

那麼擴容時,應該如何擴容?

C++下“hash“的簡單模拟實作以及實作中遇見的問題1.線性探測2.二次探測3.載荷因子4.擴容5.删除(查找,删除,判斷狀态)6.插入特殊值時7.哈希沖突8.開散列9.key對象的要求10.套用map和set11.延伸:擴容12.最終代碼
size_t GetNextPrime(size_t num)
		{
			static const unsigned long __stl_prime_list[28] =
			{
				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 (size_t i = 0; i < 28; ++i)
			{
				if (__stl_prime_list[i] > num)
				{
					return __stl_prime_list[i];
				}
			}

			return __stl_prime_list[27];
		}

		pair<iterator, bool> Insert(const T& data)
		{
			KeyOfT kot;
			iterator ret = Find(kot(data));
			if (ret != end())
				return make_pair(ret, false);

			HashFunc hf;
			// 負載因子 == 1時擴容
			if (_n == _tables.size())
			{
				//size_t newSize = _tables.size() == 0 ? 10 : _tables.size() * 2;
				size_t newSize = GetNextPrime(_tables.size());
				
				if (newSize > _tables.size())
				{
					vector<Node*> newTables;
					newTables.resize(newSize);
					for (size_t i = 0; i < _tables.size(); ++i)
					{
						Node* cur = _tables[i];
						while (cur)
						{
							Node* next = cur->_next;

							size_t index = hf(kot(cur->_data)) % newTables.size();
							// 頭插
							cur->_next = newTables[index];
							newTables[index] = cur;

							cur = next;
						}

						_tables[i] = nullptr;
					}

					_tables.swap(newTables);
				}
			}


			size_t index = hf(kot(data)) % _tables.size();
			Node* newnode = new Node(data);
			// 頭插
			newnode->_next = _tables[index];
			_tables[index] = newnode;

			++_n;
			return make_pair(iterator(newnode, this), false);;
		}
           

12.最終代碼

UnorderedSet.h

#pragma once
#include"HashTable.h"

namespace sakeww
{
	template<class K,class hash= Hash<K>>
	class unordered_set
	{
		struct SetKeyOfT
		{
			const K& operator()(const K& key)
			{
				return key;
			}
		};
	public:
		typedef typename LinkHash::HashTable<K, K, SetKeyOfT, hash>::iterator iterator;
		iterator begin()
		{
			return _ht.begin();
		}
		iterator end()
		{
			return _ht.end();
		}
		
		pair<iterator, bool> insert(const K& key)
		{
			return _ht.Insert(key);
		}

	private:
		LinkHash::HashTable<K, K,SetKeyOfT,hash> _ht;
	};

	void test1()
	{
		unordered_set<int> us;
		us.insert(2);
		us.insert(22);
		us.insert(12);
		us.insert(32);
		us.insert(42);
		us.insert(2);
		us.insert(33);
		us.insert(45);
		
		unordered_set<string> uss;
		uss.insert("left");
		uss.insert("right");
		uss.insert("red");
	}
	void test2()
	{
		unordered_set<int> us;
		us.insert(2);
		us.insert(22);
		us.insert(12);
		us.insert(32);
		us.insert(42);
		us.insert(2);
		us.insert(33);
		us.insert(45);

		unordered_set<int>::iterator it = us.begin();
		while (it != us.end())
		{
			cout << *it << " ";
			++it;
		}
		cout << endl;


		unordered_set<int> copy(us);
		unordered_set<int>::iterator cit = copy.begin();
		while (cit != us.end())
		{
			cout << *cit << " ";
			++cit;
		}
		cout << endl;
	}
}


           

UnorderedSet.h

#pragma once
#include "HashTable.h"

namespace sakeww
{
	template<class K, class V, class hash = Hash<K>>
	class unordered_map
	{
		struct MapKeyOfT
		{
			const K& operator()(const pair<K, V>& kv)
			{
				return kv.first;
			}
		};
	public:
		typedef typename LinkHash::HashTable<K, pair<K, V>, MapKeyOfT, hash>::iterator iterator;
		iterator begin()
		{
			return _ht.begin();
		}

		iterator end()
		{
			return _ht.end();
		}

		V& operator[](const K& key)
		{
			auto ret = _ht.Insert(make_pair(key, V()));
			return ret.first->second;
		}

		pair<iterator,bool> insert(const pair<K, V>& kv)
		{
			return _ht.Insert(kv);
		}

	private:
		LinkHash::HashTable<K, pair<K, V>, MapKeyOfT, hash> _ht;
	};
	void testMap()
	{
		unordered_map<string, string> dict;
		dict.insert(make_pair("sort", "排序"));
		dict.insert(make_pair("string", "字元串"));
		dict.insert(make_pair("map", "地圖"));
		cout << dict["sort"] << endl;//充當查找
		cout << dict["left"] << endl;//插入
		dict["right"] = "右邊";//插入和修改

		unordered_map<string, string>::iterator it = dict.begin();
		while (it != dict.end())
		{
			cout << it->first << ":" << it->second << endl;
			++it;
		}
		cout << endl;

		unordered_map<string, string> copy(dict);
		for (auto& kv : copy)
		{
			cout << kv.first <<":" << kv.second << endl;
		}

	}
}

           

HashTable.h

#pragma once
#include<vector>

namespace CloseHash
{
	enum Status
	{
		EXIST,
		EMPTY,
		DELETE
	};

	template<class K, class V>
	struct HashData
	{
		pair<K, V> _kv;
		Status _status = EMPTY;
	};

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

	//特化
	template<>
	struct Hash<string>
	{
		size_t operator()(const string& s)
		{
			//BKDR
			size_t value = 0;
			for (auto ch : s)
			{
				value *= 31;
				value += ch;
			}
			return value;
		}

	};

	//struct HashStr
	//{
	//	size_t operator()(const string& s)
	//	{
	//		//BKDR
	//		size_t value=0;
	//		for (auto ch : s)
	//		{
	//			value *= 31;
	//			value += ch;
	//		}
	//		return value;
	//	}
	//};

	template<class K, class V, class HashFunc = Hash<K>>
	class HashTable
	{
	public:
		bool Erase(const K& key)
		{
			HashData<K, V>* ret = Find(key);
			if (ret)
			{
				ret->_status = DELETE;
				--_n;
				return true;
			}
				
			return false;
		}

		HashData<K, V>* Find(const K& key)
		{
			if (_tables.size() == 0)
				return nullptr;

			HashFunc hf;
			size_t start = hf(key) % _tables.size();
			size_t i = 0;
			size_t index = start;
			while (_tables[index]._status != EMPTY)
			{
				if (_tables[index]._kv.first == key && _tables[index]._status == EXIST)
				{
					return &_tables[index];
				}
				++i;
				//index = start + i * i;//二次探測
				index = start + i;//線性探測
				index %= _tables.size();
			}
			return nullptr;
		}


		bool Insert(const pair<K, V>& kv)
		{
			//負載因子到0.7,擴容
			//負載因子越小,沖突機率越低,效率越高,空間浪費越多
			//負載因子越大,沖突機率越高,效率越低,空間浪費越少
			if (_tables.size() == 0 || _n * 10 / _tables.size() >= 7)
			{
				//擴容
				size_t newSize = _tables.size() == 0 ? 10 : _tables.size() * 2;

				HashTable<K, V, HashFunc> newHT;
				newHT._tables.resize(newSize);
				for (size_t i = 0; i < _tables.size(); ++i)
				{
					if (_tables[i]._status == EXIST)
					{
						newHT.Insert(_tables[i]._kv);
					}
				}
				_tables.swap(newHT._tables);
			}

			HashFunc hf;
			size_t start = hf(kv.first) % _tables.size();
			size_t i = 0;
			size_t index = start;
			while (_tables[index]._status == EXIST)
			{
				++i;
				//index = start + i * i;//二次探測
				index = start + i;//線性探測
				index %= _tables.size();
			}

			_tables[index]._kv = kv;
			_tables[index]._status = EXIST;
			++_n;

			return true;
		}

	private:
		vector<HashData<K, V>> _tables;
		size_t _n = 0;//有效資料個數
	};

	void TestHashTables()
	{
		HashTable<int, int> ht;
		int a[] = { 2,12,22,32,42,52,62 };
		for (auto e : a)
		{
			ht.Insert(make_pair(e, e));
		}
		ht.Insert(make_pair(72, 72));
		ht.Insert(make_pair(42, 42));

		cout << ht.Find(12) << endl;
		ht.Erase(12);
		cout << ht.Find(12) << endl;

		ht.Insert(make_pair(-1, -1));
		ht.Insert(make_pair(-5, -5));
	}

	//void Hashstring()
	//{
	//	HashStr hs;
	//	cout << hs("left") << endl;
	//	cout << hs("right") << endl;
	//	cout << hs("abcd") << endl;
	//	cout << hs("aadd") << endl;
	//	cout << hs("acbd") << endl;
	//
	//	HashTable<string, string, HashStr> ht;
	//	ht.Insert(make_pair("red", "紅色"));
	//	ht.Insert(make_pair("blue", "藍色"));
	//}

	void test3()
	{
		Hash<int> hint;
		cout << hint(-9) << endl;
		cout << hint(9) << endl;
	}

	void test4()
	{
		HashTable<string, string> ht;
		ht.Insert(make_pair("red", "紅色"));
		ht.Insert(make_pair("blue", "藍色"));
	}
}

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

//特化
template<>
struct Hash<string>
{
	size_t operator()(const string& s)
	{
		//BKDR
		size_t value = 0;
		for (auto ch : s)
		{
			value *= 31;
			value += ch;
		}
		return value;
	}

};

namespace LinkHash
{
	template<class T>
	struct HashNode
	{
		T _data;
		HashNode<T>* _next;

		HashNode(const T& data)
			:_data(data)
			,_next(nullptr)
		{}
	};

	template<class K, class T, class KeyOfT, class HashFunc>
	class HashTable;

	template<class K, class T, class Ref, class Ptr, class KeyOfT, class HashFunc>
	struct __HTIterator
	{
		typedef HashNode<T> Node;
		typedef __HTIterator<K, T, Ref, Ptr, KeyOfT, HashFunc> Self;
		 
		Node* _node; 
		HashTable<K, T, KeyOfT, HashFunc>* _pht;

		__HTIterator(Node* node, HashTable<K, T, KeyOfT, HashFunc>* pht)
			:_node(node)
			,_pht(pht)
		{}

		Ref& operator*()
		{
			return _node->_data;
		}
		Ptr operator->()
		{
			return &_node->_data;
		}

		Self& operator++()
		{
			if (_node->_next)
			{
				_node = _node->_next;
			}
			else
			{
				KeyOfT kot;
				HashFunc hf;
				size_t index = hf(kot(_node->_data)) % _pht->_tables.size();
				++index;

				// 找下一個不為空的桶
				while (index< _pht->_tables.size()) //_pht->_tables.size()表的大小
				{
					if (_pht->_tables[index])
					{
						break;
					}
					else
						++index;
				}

				if (index == _pht->_tables.size())
				{
					_node = nullptr;
				}
				else
				{
					_node = _pht->_tables[index];
				}
			}
			return *this;
		}

		bool operator!=(const Self& s) const
		{
			return _node != s._node;
		}

		bool operator==(const Self& s) const
		{
			return _node == s._node;
		}
	};



	template<class K,class T,class KeyOfT,class HashFunc>
	class HashTable
	{
		typedef HashNode<T> Node;

		template<class K, class T, class Ref, class Ptr, class KeyOfT, class HashFunc>
		friend struct __HTIterator; 

		typedef HashTable<K, T, KeyOfT, HashFunc> Self;
	public:
		typedef __HTIterator<K, T, T&, T*, KeyOfT, HashFunc> iterator;

		//初始化清單,
		//HashTable()
		//{}

		//顯示指定編譯器去生成預設構造函數
		HashTable() = default;

		HashTable(const HashTable<K, T, KeyOfT, HashFunc>& ht)
		{
			_tables.resize(ht._tables.size());
			for (size_t i = 0; i < ht._tables.size(); ++i)
			{
				Node* cur = ht._tables[i];
				while (cur)
				{
					Node* copy = new Node(cur->_data);
					copy->_next = _tables[i];
					_tables[i] = copy;
					cur = cur->_next;
				}
			}
		}

		//可以不寫模闆參數,拷貝構造和指派可以不寫
		//const HashTable<K, T, KeyOfT, HashFunc>& operator=(const HashTable<K, T, KeyOfT, HashFunc>)
		Self& operator=(Self copy)
		{
			swap(_n, copy._n);
			_tables.swap(copy._tables);
			return *this;
		}

		~HashTable()
		{
			for (size_t i = 0; i < _tables.size(); ++i)
			{
				Node* cur = _tables[i];
				while (cur)
				{
					Node* next = cur->_next;
					delete cur;
					cur = next;
				}
				_tables[i] = nullptr;
			}
		}


		iterator begin()
		{
			for (size_t i = 0; i < _tables.size(); i++)
			{
				if (_tables[i])
				{
					return iterator(_tables[i],this);
				}
			}
			return end();
		}
		iterator end()
		{
			return iterator(nullptr, this);
		}

		bool Erase(const K& key)
		{
			if (_tables.empty())
				return false;

			HashFunc hf;
			size_t index = hf(key) % _tables.size();
			Node* prev = nullptr;
			Node* cur = _tables[index];
			KeyOfT kot;
			while (cur)
			{
				if (kot(cur->_data) == key)
				{
					if (prev == nullptr) {
						//頭删
						_tables[index] = cur->_next;
					}
					else {
						//中間删除and尾删
						prev->_next = cur->_next;
					}
					--_n;
					delete cur;
					return true;
				}
				else
				{
					prev = cur;
					cur = cur->_next;
				}
			}
			return false;
		}

		iterator Find(const K& key)
		{
			if (_tables.empty())
				return end();

			HashFunc hf;
			size_t index = hf(key) % _tables.size();
			Node* cur = _tables[index];
			KeyOfT kot;
			while (cur)
			{
				if (kot(cur->_data) == key)
					return iterator(cur, this);
				else
					cur = cur->_next;
			}
			return end();
		}
		size_t GetNextPrime(size_t num)
		{
			static const unsigned long __stl_prime_list[28] =
			{
				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 (size_t i = 0; i < 28; ++i)
			{
				if (__stl_prime_list[i] > num)
				{
					return __stl_prime_list[i];
				}
			}

			return __stl_prime_list[27];
		}

		pair<iterator, bool> Insert(const T& data)
		{
			KeyOfT kot;
			iterator ret = Find(kot(data));
			if (ret != end())
				return make_pair(ret, false);

			HashFunc hf;
			// 負載因子 == 1時擴容
			if (_n == _tables.size())
			{
				//size_t newSize = _tables.size() == 0 ? 10 : _tables.size() * 2;
				size_t newSize = GetNextPrime(_tables.size());
				if (newSize > _tables.size())
				{
					vector<Node*> newTables;
					newTables.resize(newSize);
					for (size_t i = 0; i < _tables.size(); ++i)
					{
						Node* cur = _tables[i];
						while (cur)
						{
							Node* next = cur->_next;

							size_t index = hf(kot(cur->_data)) % newTables.size();
							// 頭插
							cur->_next = newTables[index];
							newTables[index] = cur;

							cur = next;
						}

						_tables[i] = nullptr;
					}

					_tables.swap(newTables);
				}

			}


			size_t index = hf(kot(data)) % _tables.size();
			Node* newnode = new Node(data);
			// 頭插
			newnode->_next = _tables[index];
			_tables[index] = newnode;

			++_n;
			return make_pair(iterator(newnode, this), false);;
		}
	private:
		vector<Node*> _tables;
		size_t _n=0;//有效資料個數
	};
}
           

繼續閱讀