C++--哈希表--开散列(哈希桶的模拟实现)--1110

开散列

开散列概念 开散列法又叫链地址法(拉链法)。首先计算映射位置,具有相同地映射关系的值归于同一子集合,每一个子集合称为一个桶,各个桶中的元素通过一个单链表链接起来,各链表的头结点存储在哈希表中。

C++--哈希表--开散列(哈希桶的模拟实现)--1110_第1张图片

哈希表开散列的模拟实现

准备工作

跟闭散列一样,只是二者在实现的时候,用的存储结构不同

我们写在一个自定义类域 Bucket 里面

节点结构体

namespace Bucket
{
	template
	struct HashNode
	{
		pair _kv;
		HashNode* _next;

		HashNode(const pair& kv)
			:_kv(kv)
			,_next(nullptr)
		{}
	};

}

哈希表类

template>
	class HashTable
	{
		typedef HashNode Node;
	public:
		~HashTable();
		bool Insert(const pair& kv);
		Node* find(const K& key);
		bool Erase(const K& key);

	private:
		vector_tables;//开散列是用数组保存的 个个链表的头结点
		size_t size = 0;//存储有效数据的个数
	};

 哈希表的析构函数

    ~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;
			}
		}

开散列的插入

记得要先写析构函数 因为这里在堆上new了一堆节点,默认生成的构造函数不能满足需要了

	bool Insert(const pair& kv)
	{			//if (Find(kv.first)) return false;
		Hash hash;//仿函数 不理解的可以看看上一篇 哈希表开散列 的博文
		if (_size == _tables.size())
		{
			//存满了需要扩容				
            size_t newsize = _tables.size() == 0 ? 10: _tables.size() * 2;				
            //资本家式扩容
			vector newTables;
			newTables.resize(newsize, nullptr);				
            for (size_t i = 0; i < _tables.size(); i++)
			{
				Node* cur = _tables[i];					
                //将i下标位置存放的链表指针 一个一个插入到新表
				while (cur)
				{
				Node* next = cur->_next;
			   size_t hashi=hash(cur->_kv.first)%_newTables.size();						
                        //头插
					cur->_next = newTables[hashi];
					newTables[hashi] = cur;
					cur = next;
				}
				_tables[i] = nullptr;
			}
			_tables.swap(newTables);
		}
		//剩余位置足够 或者 扩容完毕
		size_t hashi = hash(kv.first) % _tables.size();
		Node* newnode = new Node(kv);			
        newnode->_next = _tables[hashi];
		_tables[hashi] = newnode;
		_size++;			
        return true;
	}

 测试用例

void TestHT1()
	{
		int a[] = { 1, 11, 4, 15, 26, 7, 44,55,99,78 };
		HashTable ht;
		for (auto e : a)
		{
			ht.Insert(make_pair(e, e));
		}

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

C++--哈希表--开散列(哈希桶的模拟实现)--1110_第2张图片

 开散列的查找

		Node* Find(const K& key)
		{
			if (_tables.size() == 0) return nullptr;
			size_t hashi = key % _tables.size();
			Node* cur = _tables[hashi];//时刻记得_tables里面放的是Node*
			while (cur)
			{
				if (cur->_kv.first == key) return cur;
				cur = cur->_next;
			}
			return nullptr;
		}

 开散列的删除

		bool Erase(const K& key)
		{
			if (_tables.size() == 0) return false;
			Hash hash;
			size_t hashi = hash(key) % _tables.size();
			Node* prev = nullptr;
			Node* cur = _tables[hashi];
			while (cur)
			{
				if (cur->_kv.first == key)
				{
					if (prev == nullptr)
					{
						_tables[hashi] = cur->_next;
					}
					else
					{
						prev->_next = cur->_next;
					}
					delete cur;
					_size--;
					return true;
				}
				prev = cur;
				cur = cur->_next;
			}
			return false;
		}

C++--哈希表--开散列(哈希桶的模拟实现)--1110_第3张图片

改进措施及其他功能函数的实现
 

 _tables.size()的选取决定拉链法的效率,一般都采取质数,而非二倍。在源码中,标准库给了一个数组,如下:


	static const size_t __stl_num_primes = 28;
	static const size_t __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
	};

我们对于_tables.size()的改进可以依此

inline size_t next_size(size_t n)
{
	static const size_t __stl_num_primes = 28;
	static const size_t __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<28;i++)
    {
        if(__stl__prime_list[i]>n)
        {
            return __stl__prime_list[i];
        }
    }
    return -1;
}
bool Insert(const pair& kv)
	{			
        //...

		if (_size == _tables.size())
		{
			vector newTables;
			newTables.resize(next_size(_tables.size()),nullptr);				
        //...
        }
        //...
        return true;
	}

为了让使用者可以访问我们的私有成员变量的值,进行封装

//...
{
public:
    size_t Size()
	{
		return _size;
	}

	// 表的长度
	size_t TablesSize()
	{
		return _tables.size();
	}

	// 桶的个数
	size_t BucketNum()
	{
		size_t num = 0;
		for (size_t i = 0; i < _tables.size(); ++i)
		{
			if (_tables[i])
			{
				++num;
			}
		}

		return num;
	}

};

你可能感兴趣的:(C++,哈希算法,c++)