处理哈希冲突的闭散列方法-线性探测

说到哈希冲突,就必须谈到哈希函数了。

  • 什么时候哈希函数

    哈希冲突函数hv(i),用于在元素i发生哈希冲突时,将其映射至另一个内存位置。

  • 什么是哈希冲突

    哈希冲突即关键字不同的元素被映射到了同一个内存位置,包括由同义词冲突和非同义词冲突。

处理哈希冲突的方法很多,这里浅谈一下处理哈希冲突的闭散列方法:

  • 线性探测

    如下图所示

在上图中,这里key取8。

实现线性探测代码:
#pragma once
#include<string>
enum Status
{
	EXIST,
	EMPTY,
	DELETE,
};
//如果是数据类型,直接返回数据,定位位置
template<class T>
struct DataType
{
	long long operator()(const T&key)
	{
		return key;
	}
};
//如果是字符串类型,求出字符串ASCII和,根据求出的和定位位置
template<class T>
struct StringType
{
	long long operator()(const string&key)
	{
		long long size = 0;
		for (size_t i = 0; i < key.size(); i++)
		{
			size = size + key[i];
		}
		return size;
	}

};
//定义的哈希类
template<class T,template<class T> class HashFuncer=DataType>
//class HashFuncer = DataType<T>
class HashTable
{
public:
	HashTable()
		:_tables(NULL)
		, _status(NULL)
		, _size(0)
		, _capacity(0)
	{}
	HashTable(const size_t size)
		:_tables(new T[size])
		, _status(new Status[size])
		, _size(0)
		, _capacity(size)
	{
		for (size_t i = 0; i < size; i++)
		{
			_status[i] = EMPTY;
		}
	}
	HashTable(const HashTable<T,HashFuncer>&ht)
	{
		HashTable<T,HashFuncer> tmp(ht._capacity);
		for (size_t i = 0; i < ht._capacity; i++)
		{
			if (ht._status[i] != EMPTY)
			{
				tmp._status[i] = ht._status[i];
				tmp._tables[i] = ht._tables[i];
			}
		}
		tmp._size = ht._size;
		tmp._capacity = ht._capacity;
		this->Swap(tmp);
	}
	~HashTable()
	{
		if (_tables)
		{
			delete[] _tables;
			delete[] _status;
		}
		_size = 0;
		_capacity = 0;
	}
	HashTable<T, HashFuncer>& operator=(const HashTable<T, HashFuncer> &ht)
	{
		if (_tables != ht._tables)
		{
			HashTable<T, HashFuncer> ht1(ht);
			/*HashTable<T, HashFuncer> ht1(ht._capacity);
			for (size_t i = 0; i < ht._capacity; i++)
			{
				if (ht._status[i] != EMPTY)
				{
					ht1._tables[i] = ht._tables[i];
					ht1._status[i] = ht._status[i];
				}
			}
			ht1._size = ht._size;*/
			this->Swap(ht1);
		}
		return *this;
	}
	bool Insert(const T&key)
	{
		_CheckCapacity();
		size_t index = _HashFunc(key);
		while (_status[index] == EXIST)
		{
			if (_tables[index] == key)
			{
				return false;
			}
			++index;
			if (index == _capacity)
			{
				index = 0;
			}
		}
		_status[index] = EXIST;
		_tables[index] = key;
		_size++;
		return true;
	}
	size_t Find(const T&key)
	{
		size_t index = _HashFunc(key);
		while (_status[index] != EMPTY)
		{
			if (_tables[index] == key&&_status[index] != DELETE)
			{
				return index;
			}
			++index;
			if (index == _capacity)
			{
				index = 0;
			}
		}
		return -1;
	}
	bool Remove(const T&key)
	{
		int index = Find(key);
		if (index != -1)
		{
			_status[index] = DELETE;
			return true;
		}
		return false;
	}
	void _CheckCapacity()//载荷因子
	{
		if (_size * 10 >= _capacity * 7)
		{
			HashTable<T,HashFuncer> tmp(2 * _capacity+3);
			for (size_t i = 0; i < _capacity; ++i)
			{
				if (_status[i] != EMPTY)
				{
					tmp.Insert(_tables[i]);
				}
			}
			this->Swap(tmp);
		}
	}
	void PrintTables()
	{
		for (size_t i = 0; i < _capacity; ++i)
		{
			if (_status[i] == EXIST)
			{
				printf("[%d]:E->", i);
				cout << _tables[i];
			}
			else if (_status[i] == DELETE)
			{
				printf("[%d]:D->", i);
				cout << _tables[i];
			}
			else if (_status[i] == EMPTY)
			{
				printf("[%d]:N->NONE", i);
			}
			cout << endl;
		}
		cout << endl;
	}
	void Swap(HashTable<T,HashFuncer> &ht)
	{
		swap(_tables, ht._tables);
		swap(_status, ht._status);
		swap(_size, ht._size);
		swap(_capacity, ht._capacity);
	}
	size_t _HashFunc(const T&key)
	{
		HashFuncer<T> hf;
		return hf(key)%_capacity;
	}
protected:
	T *_tables;
	Status *_status;
	size_t _size;
	size_t _capacity;
};
时间: 2024-10-10 14:38:38

处理哈希冲突的闭散列方法-线性探测的相关文章

哈希冲突的处理【闭散列方法-线性探测和二次探测】

散列表(Hash table,也叫哈希表),是根据关键码值(Key value)而直接进行访问的数据结构.也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度.这个映射函数叫做散列函数,存放记录的数组叫做散列表. 给定表M,存在函数Hash(key),对任意给定的关键字值key,代入函数后若能得到包含该关键字的记录在表中的地址,则称表M为哈希(Hash)表,函数Hash(key)为哈希(Hash) 函数. 构造哈希表的两种方法 1.直接定址法--取关键字的某个线性函数为散列地

哈希表冲突的两个解决方法线性探测和分离链接法

1.线性探测法 就是当要插入的行号发生冲突时,往下一个行号存放哈希值,直到没有冲突. 2.分离链接法 就是将一个行号做成链表的形式,如果有这个行号的冲突便新建一个节点将其插入这个行号的链表中. 在Mahout中,FastByIDMap是基于散列的,但它在处理散列冲突时使用的是线性探测,而非分离链接.因为线性探测不必为每个条目新增加一个额外的Map.Entry对象,节省了内存开销:在数据规模很庞大的时候,这种优势更加体现得出来.

数据结构哈希表的闭散列基本实现

#pragma once #include<string> using namespace std; enum Status//表示当前位置的状态 { EXITS, DELETE, EMPTY, }; template<class K,class V> struct KeyValueNode//KV键值对 { K _key; V _value; KeyValueNode(const K& key=K(), const V& value=V()) :_key(key)

Java学习笔记(2)----散列集/线性表/队列/集合/图(Set,List,Queue,Collection,Map)

1. Java集合框架中的所有实例类都实现了Cloneable和Seriablizable接口.所以,它们的实例都是可复制和可序列化的. 2. 规则集存储的是不重复的元素.若要在集合中存储重复的元素,就需要使用线性表.线性表不仅可以存储重复的元素,而且允许用户指定存储的位置.用户可以通过下标来访问线性表中的元素. 3. Java集合支持三种类型的规则集:散列集HashSet.链式散列集LinkedHashSet和树形集TreeSet.HashSet以一个不可预知的顺序存储元素:LinkedHas

数据结构-散列(1)

一.散列表(Hash table) 1.散列表用来表示集合和字典,通过散列函数建立从元素关键码集合到散列表地址集合的一个映射,搜索时可以直接到达或者逼近具有对应关键码的元素的实际存放地址: 2.散列函数是压缩映像函数,关键码集合比散列表地址集合大得多,所以经过散列函数的计算会把不同的关键码映射到同一个散列地址上,散列地址相同的不同关键码互为同义词: 3.构造散列函数的要求: (1).定义域必须包括需要存储的全部关键码,若散列表允许有m个地址则其值域必须在0到m-1之间: (2).散列函数计算出来

转载:散列冲突的解决策略

冲突解决的策略 尽管散列函数的目标是使得冲突最少,但实际上冲突是无法避免的.因此,我们必须研究冲突解决策略.冲突解决技术可以分为两类:开散列方法( open hashing,也称为拉链法,separate chaining )和闭散列方法( closed hashing,也称为开地址方法,open addressing ).这两种方法的不同之处在于:开散列法把发生冲突的关键码存储在散列表主表之外,而闭散列法把发生冲突的关键码存储在表中另一个槽内. 开散列方法: 1.拉链法 开散列方法的一种简单形

数据结构与算法分析java——散列

1. 散列的概念 散列方法的主要思想是根据结点的关键码值来确定其存储地址:以关键码值K为自变量,通过一定的函数关系h(K)(称为散列函数),计算出对应的函数值来,把这个值解释为结点的存储地址,将结点存入到此存储单元中.检索时,用同样的方法计算地址,然后到相应的单元里去取要找的结点.通过散列方法可以对结点进行快速检索.散列(hash,也称“哈希”)是一种重要的存储方式,也是一种常见的检索方法. 按散列存储方式构造的存储结构称为散列表(hash table).散列表中的一个位置称为槽(slot).散

【数据结构】哈希表的线性探测算法

构造哈希表常用的方法是: 除留余数法--取关键值被某个不大于散列表长m的数p除后的所得的余数为散列地址.HashKey= Key % P. 直接定址法--取关键字的某个线性函数为散列地址HashKey= Key 或 HashKey= A*Key + BA.B为常数. 我在这里主要使用一下除留余数法Hash(key) =Key%P,(P这里是哈希表的长度)p最好是素数考虑降低哈希冲突的原因,我并没有在这上面过于追究此处哈希表长度10,见线性探测图. 哈希表经常遇到的一个问题就是哈希冲突. 哈希冲突

数据结构--散列(分离链接法解决冲突)

散列方法的主要思想是根据结点的关键码值来确定其存储地址:以关键码值K为自变量,通过一定的函数关系h(K)(称为散列函数),计算出对应的函数值来,把这个值解释为结点的存储地址,将结点存入到此存储单元中.检索时,用同样的方法计算地址,然后到相应的 单元里去取要找的结点.通过散列方法可以对结点进行快速检索.散列(hash,也称"哈希")是一种重要的存储方式,也是一种常见的检索方法. 因此,散列函数更像是一种映射,散列函数的选择有很多种,下面以散列函数为关键值对10取余为例,说明散列的插入关键