C++深入探究哈希表如何封装出unordered_set和unordered_map

目录
  • 封装前的哈希代码
  • 泛型
  • 获取key
  • 自定义哈希规则
  • 哈希表模板参数解释
  • 迭代器
    • 结构
    • operator++()
    • 构造函数
    • 重载运算符
  • 小问题
  • 代码汇总
    • Hash.h
    • MyUnordered_map.h
    • MyUnordered_set.h

默认你已经实现了哈希表(开散列)

封装前的哈希代码

namespace HashBucket
{
	template<class K,class V>
	struct HashNode
	{
		pair<K, V> _kv;
		HashNode* _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:
		Node* Find(const K& key)//Find函数返回值一般都是指针,通过指针访问这个自定义类型的成员
		{
			Hash hash;
			if (_tables.size() == 0)//表的大小为0,防止取余0
			{
				return nullptr;
			}
			size_t index = hash(key) % _tables.size();//找到桶号
			Node* cur = _tables[index];
			while (cur)
			{
				if (cur->_kv.first == key)
				{
					return cur;
				}
				else
				{
					cur = cur->_next;
				}
			}
			return nullptr;
		}
		size_t GetNextPrime(size_t prime)
		{
			const int PRIMECOUNT = 28;
			static const size_t primeList[PRIMECOUNT] =
			{
				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
			};
			//ul表示unsigned long
			size_t i = 0;
			for (; i < PRIMECOUNT; ++i)
			{
				if (primeList[i] > prime)
					return primeList[i];
			}
			return primeList[i];
		}
		bool Insert(const pair<K, V>& kv)
		{
			if (Find(kv.first))//有相同的key直接返回false
			{
				return false;
			}
			//if(_n==0||_n==_tables.size())
			Hash hash;
			if (_n == _tables.size())//最开始_n为0,而_tables.size()也为0所以可以简化为一行代码
			{
				//增容
				//size_t newSize = _tables.size() == 0 ? 10 : _tables.size() * 2;
				size_t newSize = GetNextPrime(_tables.size());
				vector<Node*>newTables;
				newTables.resize(newSize, nullptr);
				for (int i = 0; i < _tables.size(); i++)
				{
					Node* cur = _tables[i];
					while (cur)
					{
						Node* next = cur->_next;//记录下一个位置
						size_t index = hash(cur->_kv.first) % newTables.size();
						cur->_next = newTables[index];//cur当头
						newTables[index] = cur;//更新vector里的头
						cur = next;
					}
				}
				_tables.swap(newTables);//把新表的数据放入旧表中
			}
			size_t index = hash(kv.first) % _tables.size();//算出桶号
			//头插
			Node* newNode = new Node(kv);
			newNode->_next = _tables[index];
			_tables[index]=newNode;
			++_n;//别忘记更新有效数据的个数
			return true;
		}
		bool Erase(const K& key)
		{
			//if (!Find(key))//找不到这个元素
			// 这么写也可以,但是后面删除的过程中会顺带遍历整个桶
			//{
			//	return false;
			//}
			if (_tables.size() == 0)//哈希表为空
			{
				return false;
			}
			Hash hash;
			size_t index = hash(key) % _tables.size();
			Node* cur = _tables[index];
			Node* prev = nullptr;//记录前一个位置
			while (cur)
			{
				if (cur->_kv.first == key)//找到这个元素了
				{
					if(cur==_tables[index])//元素是头结点
					{
						_tables[index] = cur->_next;
					}
					else//不是头结点
					{
						prev->_next = cur->_next;
					}
					delete cur;
					cur = nullptr;
					_n--;
					return true;
				}
				else
				{
					prev = cur;
					cur = cur->_next;
				}
			}
			return false;
		}
		~HashTable()//哈希桶采用的链表结构 需要释放每个链表
		{
			for (int i=0;i<_tables.size();i++)
			{
				Node* cur = _tables[i];
				if (cur == nullptr)
				{
					continue;
				}
				else
				{
					cur = cur->_next;
				}
				while (cur)
				{
					Node* next = cur->_next;
					delete cur;
					cur = next;
				}
				_tables[i] = nullptr;
			}
			_n = 0;
		}
		HashTable() {};
	private:
		vector<Node*>_tables;//存的是链表首元素的指针
		size_t _n=0;//有效数据
	};

泛型

封装时想直接搭出unordered_set/unordered_map的结构,发现行不通

于是从哈希表的结构入手,先把一些类型改成泛型

template<class T>
struct HashNode
{
	T _data;
	HashNode* _next;
	HashNode(const T&data)
		:_data(data)
		, _next(nullptr)
	{}
};

结点的KV结构改成T ,改变结点的类型后HashTable里的结点类型也需要更改

typedef HashNode<K,V>的模板也需要改为typedef HashNode Node;

获取key

明确unordered_map是KV结构,unordered_set是K模型的结构。

获取key后可以做很多事情,比如查找和算出桶号

封装前哈希结点的类型是pair<K,V>,现在的类型是T。

pair<K,V>kv , 可以通过kv.first来获取key。

默认int、double、string等类型的key就是本身。(也可以自定义)

类型T既可能是pair也可能是一个int类型等等,那应该怎么得到类型T的key?借助模板+仿函数。

以unordered_map为例

unordered_map类中实现仿函数

哈希表中增加一个模板参数KeyOfT来获取T类型的Key

同理unordered_set里仿函数的实现

之后把所有与.first有关的都用模板实例化的kot来获取key

自定义哈希规则

去掉哈希表模板参数里哈希函数的默认值 在unordered_set/unordered_map加上第三个模板参数Hash自定义哈希规则

封装前的哈希表

template<class K,class V,class Hash=HashFunc<K>>
class HashTable{};

现在的哈希表

template<class K, class T, class KeyOfT, class Hash>
//去掉哈希表的默认值,哈希函数由unordered_map传入
class HashTable{};
template<class K,class V,class Hash=HashFunc<K>>
class unordered_map{
    private:
		HashBucket::HashTable<K, pair<K, V>, MapKeyOfT,Hash> _ht;
};

解释:实例化对象时便可以传入模板参数达到自自定义哈希规则的效果。

哈希表模板参数解释

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

看完上面的对这四个参数应该有大概的了解了。这里一齐解释一下为什么这么写。

第一个参数K:key的类型就是K。查找函数是根据key来查找的,所以需要K。

第二个参数T:哈希表结点存储的数据类型。比如int,double,pair,string等。

第三个参数KeyOfT:拿到T类型(结点数据类型)的key。

第四个参数Hash:表示使用的哈希函数

//哈希函数
template<class K>
struct HashFunc
{
	const K& operator()(const K& key)
	{
		return key;
	}
};
template<>//针对string的模板特化
struct HashFunc <string>
{
	size_t operator()(const string& key)
	{
		size_t value = 0;
		for (auto e : key)
		{
			value = value * 13 + (size_t)e;//*131是BKDR发明的字符串哈希算法,*131等数效率更高
		}
		return value;
	}
};

HashFunc(kot(T)) 取出这个类型的key的映射值

迭代器

unordered_set/unordered_map的迭代器是单向迭代器

迭代器只能++,不能 –

结构

Self表示自己

operator++()

前置++

实现思路:如果当前结点的下一个不为空 直接访问即可

如果下一个结点为空,就得找下一个桶 怎么找?根据当前指向的数据算出桶号,再把桶号+1,一直往后面找,直到找到一个桶不为空,或者找完了整个容器都没找到,就返回空

Self& operator++()//找到桶的下一个元素
{
	Hash hash;
	KeyOfT kot;
	Node* tmp = _node;//记录当前位置,用来计算当前桶号
	_node = _node->_next;//当前元素肯定不为空 所以不会有空指针引用的问题
	//如果下一个为空,就找下一个不为空的桶
	if (_node == nullptr)//下一个元素为空
	{
		//找下一个不为空的桶,所以需要传入这张表
		size_t index = hash(kot(tmp->_data)) % (_ht->_tables.size());
		index++;
		while (index < _ht->_tables.size() && _ht->_tables[index] == nullptr)//一直往后找
		{
			index++;
		}
		if (index == _ht->_tables.size())//找到最后一个元素了仍然没找到,说明当前已经是最后一个元素了
		{
			_node = nullptr;
		}
		else
		{
			_node = _ht->_tables[index];

		}
		return *this;
	}
	else//下一个元素不为空
	{
		return *this;
	}
}

构造函数

构造函数得到结点所在的哈希表

HTIterator(Node* node, HT* ht)//不仅需要知道指向的结点,由于++需要找下一个桶,所以需要哈希结点所在的哈希表
	:_node(node)
	, _ht(ht)
{}

重载运算符

重载除了++以外的一些运算符

T* operator->()//auto it=m.begin()  *it可以拿到数据,所以返回值是T*
{
	return &(_node->_data);
}
T& operator*()
{
	return _node->_data;
}
bool operator!= (const Self& s)const
{
	return s._node != _node;
}

T为pair时可以通过it->first拿到key。

小问题

你会发现这样一个现象,迭代器里面用了哈希表,哈希表里用了迭代器,也即两个类互相引用

如果迭代器写在哈希表前面,那么编译时编译器就会发现哈希表是无定义的(编译器只会往前/上找标识符)。

如果哈希表写在迭代器前面,那么编译时编译器就会发现迭代器是无定义的。

为了解决这个问题,得用一个前置声明解决,即在迭代器和哈希表的定义前加一个类的声明。

template<class K, class T, class KeyOfT, class Hash>
class HashTable;//模板需要也需要进行声明
template<class K, class T, class KeyOfT, class Hash>
struct HTIterator{};
...
template<class K, class T, class KeyOfT, class Hash>
class HashTable{};//具体实现

迭代器里借助一个指针访问了哈希表的数据。但是哈希表的数据被private修饰,所以在类外不能访问,用友元解决。

在哈希表里面声明迭代器友元类(表示迭代器是哈希表的朋友,可以访问哈希表所有的数据)

const pair<const K,V>!=const pair<K,V>

写代码时的一个bug

相关的例子

解释:调试看了一下地址,传进仿函数的时候参数用的引用接收,但是因为类型不同,所以仿函数参数接收时进行了一次拷贝才拿到了sort和排序两个字符串,但也因此那个参数成临时变量了,所以返回了一块被销毁的空间的引用 为什么变成空串?因为string析构后那块空间成空串了

简单来说 仿函数没有拿到真实的那块空间 而是拷贝后形参的空间

不能识别迭代器是类型还是成员导致模板报错,加上typename解决。

typedef typename HashBucket::HashTable<K, K, SetKeyOfT, Hash>::iterator iterator;

typename是告诉编译器这是一个类型 等这个类实例化了再去找里面的东西

代码汇总

github代码汇总

Hash.h

namespace ck
{
	template<class K>
	struct HashFunc
	{
		const K& operator()(const K& key)
		{
			return key;
		}
	};
	template<>
	struct HashFunc <string>
	{
		size_t operator()(const string& key)
		{
			size_t value = 0;
			for (auto e : key)
			{
				value = value * 13 + (size_t)e;//*131是BKDR发明的字符串哈希算法,*131等数效率更高
			}
			return value;
		}
	};
	namespace HashBucket
	{
		template<class T>
		struct HashNode
		{
			T _data;
			HashNode* _next;
			HashNode(const T&data)
				:_data(data)
				, _next(nullptr)
			{}
		};
		template<class K, class T, class KeyOfT, class Hash>
		class HashTable;
		template<class K, class T, class KeyOfT, class Hash>
		struct HTIterator
		{
			typedef HTIterator<K, T, KeyOfT, Hash> Self;//自身
			typedef HashNode<T> Node;
			typedef HashTable<K, T, KeyOfT, Hash> HT;
			Node* _node;//通过Node*去访问数据 不过自定义类型++不能访问到下一个元素,所以需要封装
			HT* _ht;
			HTIterator(Node* node, HT* ht)//不仅需要知道指向的结点,由于++需要找下一个桶,所以需要哈希结点所在的哈希表
				:_node(node)
				, _ht(ht)
			{}
			Self& operator++()//找到桶的下一个元素
			{
				Hash hash;
				KeyOfT kot;
				//const K& key = kot(_node->_data);//记录这个不为空元素的key  有问题类型不匹配导致接收到的key是空串
				Node* tmp = _node;
				_node = _node->_next;//当前元素肯定不为空 所以不会有空指针引用的问题
				//如果下一个为空,就找下一个不为空的桶
				if (_node == nullptr)//下一个元素为空
				{
					//找下一个不为空的桶,所以需要传入这张表
					size_t index = hash(kot(tmp->_data)) % (_ht->_tables.size());
					index++;
					while (index < _ht->_tables.size() && _ht->_tables[index] == nullptr)//一直往后找
					{
						index++;
					}
					if (index == _ht->_tables.size())//找到最后一个元素了仍然没找到,说明当前已经是最后一个元素了
					{
						_node = nullptr;
					}
					else
					{
						_node = _ht->_tables[index];
					}
					return *this;
				}
				else//下一个元素不为空
				{
					return *this;
				}
			}
			T* operator->()//auto it=m.begin()  ‘it->' 去访问数据成员所以返回值是T*
			{
				return &(_node->_data);
			}
			T& operator*()
			{
				return _node->_data;
			}
			bool operator!= (const Self& s)const
			{
				return s._node != _node;
			}
		};
		template<class K, class T,  class KeyOfT, class Hash>
		class HashTable
		{
			typedef HashNode<T> Node;
		public:
			template<class K, class T, class KeyOfT, class Hash>
			friend struct HTIterator;
			Node* Find(const K& key)//Find函数返回值一般都是指针,通过指针访问这个自定义类型的成员
			{
				Hash hash;
				KeyOfT kot;
				if (_tables.size() == 0)//表的大小为0,防止取余0
				{
					return nullptr;
				}
				size_t index = hash(key) % _tables.size();//找到桶号
				Node* cur = _tables[index];
				while (cur)
				{
					if (kot(cur->_data) == key)
					{
						return cur;
					}
					else
					{
						cur = cur->_next;
					}
				}
				return nullptr;
			}
			size_t GetNextPrime(size_t prime)
			{
				const int PRIMECOUNT = 28;
				static const size_t primeList[PRIMECOUNT] =
				{
					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
				};
				//ul表示unsigned long
				size_t i = 0;
				for (; i < PRIMECOUNT; ++i)
				{
					if (primeList[i] > prime)
						return primeList[i];
				}
				return primeList[i];
			}
			typedef HTIterator<K,T,KeyOfT,Hash> iterator;
			iterator begin()
			{
				for (size_t i = 0; i < _tables.size(); i++)
				{
					if (_tables[i])
					{
						return iterator(_tables[i], this);
					}
				}
				return iterator(nullptr, this);
			}
			iterator end()
			{
				return iterator(nullptr, this);//第二个指针就是自己
			}
			pair<iterator,bool> Insert(const T& data)
			{
				KeyOfT kot;
				Node* tmp = Find(kot(data));
				if (tmp)//有相同的key直接返回false
				{
					return make_pair(iterator(tmp, this), false);
				}
				//if(_n==0||_n==_tables.size())
				Hash hash;
				if (_n == _tables.size())//最开始_n为0,而_tables.size()也为0所以可以简化为一行代码
				{
					//增容
					//size_t newSize = _tables.size() == 0 ? 10 : _tables.size() * 2;
					size_t newSize = GetNextPrime(_tables.size());
					vector<Node*>newTables;
					newTables.resize(newSize, nullptr);
					for (int i = 0; i < _tables.size(); i++)
					{
						Node* cur = _tables[i];
						while (cur)
						{
							Node* next = cur->_next;//记录下一个位置
							size_t index = hash(kot(cur->_data)) % newTables.size();
							cur->_next = newTables[index];//cur当头
							newTables[index] = cur;//更新vector里的头
							cur = next;
						}
					}
					_tables.swap(newTables);//把新表的数据放入旧表中
				}
				size_t index = hash(kot(data)) % _tables.size();//算出桶号
				//头插
				Node* newNode = new Node(data);
				newNode->_next = _tables[index];
				_tables[index] = newNode;
				++_n;//别忘记更新有效数据的个数
				return make_pair(iterator(newNode, this), true);
			}
			bool Erase(const K& key)
			{
				//if (!Find(key))//找不到这个元素
				// 这么写也可以,但是后面删除的过程中会顺带遍历整个桶
				//{
				//	return false;
				//}
				if (_tables.size() == 0)//哈希表为空
				{
					return false;
				}
				Hash hash;
				KeyOfT kot;
				size_t index = hash(key) % _tables.size();
				Node* cur = _tables[index];
				Node* prev = nullptr;//记录前一个位置
				while (cur)
				{
					if (kot(cur->_data) == key)//找到这个元素了
					{
						if (cur == _tables[index])//元素是头结点
						{
							_tables[index] = cur->_next;
						}
						else//不是头结点
						{
							prev->_next = cur->_next;
						}
						delete cur;
						cur = nullptr;
						_n--;
						return true;
					}
					else
					{
						prev = cur;
						cur = cur->_next;
					}
				}
				return false;
			}
			~HashTable()//哈希桶采用的链表结构 需要释放每个链表
			{
				for (int i = 0; i < _tables.size(); i++)
				{
					Node* cur = _tables[i];
					if (cur == nullptr)
					{
						continue;
					}
					else
					{
						cur = cur->_next;
					}
					while (cur)
					{
						Node* next = cur->_next;
						delete cur;
						cur = next;
					}
					_tables[i] = nullptr;
				}
				_n = 0;
			}
			HashTable() {};
		private:
			vector<Node*>_tables;//存的是链表首元素的指针
			size_t _n = 0;//有效数据
		};
	}
}

MyUnordered_map.h

#include "Hash.h"
namespace ck
{
	template<class K,class V,class Hash=HashFunc<K>>
	class unordered_map
	{
		struct MapKeyOfT
		{
			const K& operator()(const pair< K, V>& kv) const
			{
				return kv.first;
			}
		};
		typedef typename HashBucket::HashTable<K, pair<K, V>, MapKeyOfT, Hash>::iterator iterator;
	public:
		iterator begin()
		{
			return _ht.begin();
		}
		iterator end()
		{
			return  _ht.end();
		}
		pair<iterator, bool> insert(const pair<const K,V>& kv)
		{
			return _ht.Insert(kv);
		}
		bool erase(const K& key)
		{
			return _ht.Erase(key);
		}
		bool find(const K& key)
		{
			return _ht.Find(key);
		}
		V& operator[](const K& key)
		{
			auto it = insert(make_pair(key, V()));
			return (it.first)->second;
		}
	private:
		HashBucket::HashTable<K, pair<K, V>, MapKeyOfT,Hash> _ht;
	};
}

MyUnordered_set.h

#include "Hash.h"
namespace ck
{
	template<class K,class Hash=HashFunc<K>>
	class unordered_set
	{
		struct SetKeyOfT
		{
			const K& operator()(const K& key)
			{
				return key;
			}
		};
	public:
		typedef typename HashBucket::HashTable<K, K, SetKeyOfT, Hash>::iterator iterator;
	public:
		iterator begin()
		{
			return _ht.begin();
		}
		iterator end()
		{
			return  _ht.end();
		}
		pair<iterator, bool> insert(const K& kv)
		{
			return _ht.Insert(kv);
		}
		bool erase(const K& key)
		{
			return _ht.Erase(key);
		}
		bool find(const K& key)
		{
			return _ht.Find(key);
		}
	private:
		HashBucket::HashTable<K, K, SetKeyOfT, Hash> _ht;
	};
	private:
    	HashBucket::HashTable<K, pair<K, V>, MapKeyOfT,Hash> _ht;
    };
}

到此这篇关于C++深入探究哈希表如何封装出unordered_set和unordered_map的文章就介绍到这了,更多相关C++哈希表内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • C++基础算法基于哈希表的索引堆变形

    目录 问题来源 问题简述 问题分析 代码展示 问题来源 此题来自于Hackerrank中的QHEAP1问题,考查了对堆结构的充分理解.成功完成此题,对最大堆或者最小堆的基本操作实现就没什么太大问题了. 问题简述 实现一个最小堆,对3种类型的输入能给出正确的操作: "1 v" - 表示往堆中增加一个值为v的元素 "2 v" - 表示删去堆中值为v的元素 "3" - 表示打印出堆中最小的那个元素 注意:题目保证了要删的元素必然是在堆中的,并且在任何时

  • C++ 实现哈希表的实例

    C++ 实现哈希表的实例 该散列表的散列函数采用了除法散列函数.乘法散列函数.全域散列函数,每一个槽都是使用有序单向链表实现. 实现代码: LinkNode.h #include<iostream> using namespace std; class Link; class LinkNode { private: int key; LinkNode* next; friend Link; public: LinkNode():key(-1),next(NULL){} LinkNode(int

  • C++哈希表之线性探测法实现详解

    目录 1.哈希表-线性探测法理论 1.1.哈希表的增加元素 1.2.哈希表的查询操作 1.3.哈希表的删除操作 2.哈希表-线性探测法代码实现 2.1.素数表中的素数 1.哈希表-线性探测法理论 线性探测法的理论我们在上一篇博客已经阐述了. 现在我们来看看线性探测法的增删查的代码思想: 1.1.哈希表的增加元素 注意: 往后遍历寻找空闲位置的时候,要注意是环形遍历哦!不然访问数组就越界了. 在添加元素,发生位置被占用,即发生哈希冲突后,在向后遍历寻找空闲位置的时候,我们要知道,这个空闲的位置是有

  • C++中使用哈希表(unordered_map)的一些常用操作方法

    目录 1.建立基本数据类型的哈希表 2.向哈希表中添加元素 1).insert函数 2).用数组方法直接添加 3.成员函数 begin(),end()函数 find()查找函数 count()查找函数 size()函数 empty()函数 clear()函数 swap()函数 哈希表的遍历 第一种遍历 第二种遍历 补充:实际应用 总结 1.建立基本数据类型的哈希表 unordered_map<int,int> m; //<string,string>,<char,char&g

  • C++深入探究哈希表如何封装出unordered_set和unordered_map

    目录 封装前的哈希代码 泛型 获取key 自定义哈希规则 哈希表模板参数解释 迭代器 结构 operator++() 构造函数 重载运算符 小问题 代码汇总 Hash.h MyUnordered_map.h MyUnordered_set.h 默认你已经实现了哈希表(开散列) 封装前的哈希代码 namespace HashBucket { template<class K,class V> struct HashNode { pair<K, V> _kv; HashNode* _n

  • JS模拟实现哈希表及应用详解

    本文实例讲述了JS模拟实现哈希表及应用.分享给大家供大家参考,具体如下: 在算法中,尤其是有关数组的算法中,哈希表的使用可以很好的解决问题,所以这篇文章会记录一些有关js实现哈希表并给出解决实际问题的例子. 说明: 这篇文章所写并不是真正意义的哈希表,只是与哈希表的使用有相似之处. 第一部分:相关知识点 属性的枚举: var person = { name: "zzw", sex: "Male", age: 21 }; for (var prop in person

  • js实现HashTable(哈希表)的实例分析

    一.javascript哈希表简介 javascript里面是没有哈希表的,一直在java,C#中有时候用到了这一种数据结构,javascript里面若没有,感觉非常不顺手.细细看来,其实javascript的object的属性其实与哈希表非常类似. 如: var person = {}; person["name"] = "关羽"; 我们只需要在其基础上再封装一些HashTable的函数,就能够得到一个精简版的哈希表. 加入函数如下: 函数名 说明 返回值 add

  • PHP内核探索:哈希表碰撞攻击原理

    下面通过图文并茂的方式给大家展示PHP内核探索:哈希表碰撞攻击原理. 最近哈希表碰撞攻击(Hashtable collisions as DOS attack)的话题不断被提起,各种语言纷纷中招.本文结合PHP内核源码,聊一聊这种攻击的原理及实现.  哈希表碰撞攻击的基本原理 哈希表是一种查找效率极高的数据结构,很多语言都在内部实现了哈希表.PHP中的哈希表是一种极为重要的数据结构,不但用于表示Array数据类型,还在Zend虚拟机内部用于存储上下文环境信息(执行上下文的变量及函数均使用哈希表结

  • 详解JavaScript实现哈希表

    目录 一.哈希表原理 二.哈希表的概念 三.哈希化冲突问题 1.链地址法 2.开放地址法 四.哈希函数的实现 五.封装哈希表 六.哈希表操作 1.插入&修改操作 2.获取操作 3.删除操作 4.判断哈希表是否为空 5.获取哈希表的元素个数 七.哈希表扩容 1.哈希表扩容思想 2.哈希表扩容实现 八.完整代码  一.哈希表原理 哈希表是一种非常重要的数据结构,几乎所有的编程语言都有直接或者间接的应用这种数据结构,它通常是基于数组实现的,当时相对于数组,它有更多的优势: 它可以提供非常快速的插入-删

  • TypeScript 基础数据结构哈希表 HashTable教程

    目录 前言 1. 哈希表介绍和特性 2. 哈希表的一些概念 3. 地址冲突解决方案 3.1 方案一:链地址法 3.2 方案二:开放地址法 4. 哈希函数代码实现 5. 哈希表封装 5.1 整体框架 v1 版 5.2 添加 put 方法 v2 版 5.3 添加 get 方法 v3 版 5.4 添加 delete 方法 v4 版 6. 哈希表的自动扩容 前言 哈希表是一种 非常重要的数据结构,几乎所有的编程语言都有 直接或者间接 的应用这种数据结构. 很多学习编程的人一直搞不懂哈希表到底是如何实现的

  • Perl哈希表用法解析

    本文和大家重点讨论一下Perl哈希表的概念,Perl语言和其他编程语言各有各的特点,这里和大家分享一下Perl哈希表的概念,其实Perl哈希表是一种结构. Perl哈希表Perl哈希表是一种结构.key/value.访问Perl哈希表元素$Perl哈希表{$some_key}当给Perl哈希表选择名字时,最好这样思考:Perl哈希表元素的名字和key之间可以用for来连接.如thefamily_nameforfredisflintstone. 要引用整个Perl哈希表,使用百分号(%)作为前缀.

  • jquery自动将form表单封装成json的具体实现

    前端页面: 复制代码 代码如下: <span style="font-size:14px;"> <form action="" method="post" id="tf"> <table width="100%" cellspacing="0" cellpadding="0" border="0"> <tr

  • javascript 哈希表(hashtable)的简单实现

    首先简单的介绍关于属性的一些方法: 属性的枚举: for/in循环是遍历对象属性的方法.如 复制代码 代码如下: var obj = { name : 'obj1', age : 20, height : '176cm' } var str = ''; for(var name in obj) { str += name + ':' + obj[name] + '\n'; } alert(str); 输出为:name:obj1 age:20 height:176cm 检查属性是否存在: in运算

随机推荐