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

本文介绍了C++中哈希表的开散列模拟实现,包括准备工作、插入、查找和删除操作。在开散列法中,相同映射关系的值放入同一个桶,通过链表链接。文章还讨论了如何选择哈希表大小以提高效率,并提供了相关测试用例。
摘要由CSDN通过智能技术生成

开散列

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

哈希表开散列的模拟实现

准备工作

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

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

节点结构体

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

		HashNode(const pair<K,V>& kv)
			:_kv(kv)
			,_next(nullptr)
		{}
	};

}

哈希表类

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

	private:
		vector<Node*>_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<K, V>& kv)
	{			//if (Find(kv.first)) return false;
		Hash hash;//仿函数 不理解的可以看看上一篇 哈希表开散列 的博文
		if (_size == _tables.size())
		{
			//存满了需要扩容				
            size_t newsize = _tables.size() == 0 ? 10: _tables.size() * 2;				
            //资本家式扩容
			vector<Node*> 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<int, int> ht;
		for (auto e : a)
		{
			ht.Insert(make_pair(e, e));
		}

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

 开散列的查找

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

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

 _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<K, V>& kv)
	{			
        //...

		if (_size == _tables.size())
		{
			vector<Node*> 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;
	}

};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值