数据结构——哈希表

哈希表

序言

哈希表又称散列表。
对于一般的数据结构,记录在结构中的相对位置都是随机的,和记录的关键字无关,因此要想找到数据在数据结构中的相对位置需要进行一系列和关键字的”比较”。
例如顺序结构的比较的结果为==或!=,折半查找,二叉树排序树查找和B–树查找时比较的结果为<或>等等。(查找的效率依赖于查找过程中所进行的比较次数)

当然最理想的情况下,我们希望一次存取就可以查找所有元素,这就需要在每个储存位置与它的关键字之间建立一个对应关系f,使其一一对应。

哈希函数

哈希函数的设定非常灵活,他是将一个大集合映射到一个小集合,只要使得任何关键字通过哈希函数得到的哈希值都落在哈希表范围内。
下面举出了几种常见的哈西函数:
直接取值法取关键字或关键字的某个线性函数值做哈西地址,其实平常使用数组时有意无意的使用过这种函数。

H ( k e y ) = k e y 或 H ( k e y ) = a × k e y + b H(key)=key或H(key)=a×key+b H(key)=keyH(key)=a×key+b

数学分析法以r为基的数(如:以10为基的十进制数),并且哈希表中可能出现的关键字都是事先知道的,可取部分关键字的若干位组成哈希地址。(例如数据经常有重复的部分,这部分可以省去。)

平方取中法取关键字平方后的中间几位做哈系地址。有时候我们不能事先知道关键字的全部情况取其中哪几位也不合适,而一个数平方的中间几位与几乎所有位的数有关,具有很大的随机性可以作为哈西地址。

折叠法与平方取中法相似,将一个数截成几个相同的部分依次想加,这个数受所以位数的影响,具有随机性。

除留余数法这是一种最简单,也是最常用的构造哈希函数的方法,可以对关键字直接取模,也可以在折叠,平方取中后取模。(一般情况下,可以选p为质数或不包括小于20的质因数的合数)。
之后的代码样例使用了这个方法。

随机数法选择一个随机函数,取关键值的随机函数值做哈希地址。

处理冲突

当不同的关键值通过哈希函数得到了相同的哈希值是就发生了冲突。
选择好合适的哈希函数可以减少冲突,但因为是大集合向小集合是映射,所以冲突不可避免。
这就需要我们有一种遇到冲突时的处理方法。处理冲突就是找到另一个空的哈希地址。下面给出几种常见的处理冲突的方法。
开放定址法

H i = ( H ( k e y ) + d i ) M O D m i = 1 , 2... , k ( k < = m – 1 ) Hi=(H(key)+di) MOD m i = 1,2...,k(k<=m–1) Hi=(H(key)+di)MODmi=12...k(k<=m1)

di是增量序列,这里有三种取法
(1)取1,2,3,…称线性探测再散列;
(2)取12,-12,22,…称二次探测再散列;
(3)取伪随机数列,称伪随机探测再散列。
线性探测再散列会出现集聚现象,但可以保证一定可以找到还空着的哈希地址。
在哈希法

H i = R H i ( k e y ) i = 1 , 2 , … … , k Hi=RHi(key) i=1,2,……,k Hi=RHi(key)i=1,2,,k
RHi是不同的哈希函数,这种方法不易产生“聚集”,但增加了计算时间。
链地址法
将所有的同义词储存在同一线性链表中。
建立一个公共溢出区
一旦产生冲突就填入公共溢出区。

代码

除留取余法+线性探测再散列

#include<iostream>
#include<algorithm>
#include<string>
using namespace std;

class Hashtable 
{
private:
	string *elem;
	int size;
public:
	Hashtable()
	{
		size = 2000;
		elem = new string[size];
		for (int i = 0; i < size; i++)
		{
			elem[i] = '#';
		}
	}
	~Hashtable()
	{
		delete[] elem;
	}
	int hash(string &index)
	{
		int code = 0;
		for (size_t i = 0; i < index.length(); i++)
		{
			code = (code * 256 + index[i] + 128) % size;
		}
		return code;
	}
	bool search(string &index, int &pos, int &times)
	{
		pos = hash(index);
		times = 0;
		while (elem[pos] != "#"&&elem[pos] != index)
		{
			times++;
			if (times < size)
			{
				pos = (pos + 1) % size;
			}
			else
			{
				return false;
			}
		}
		if (elem[pos] == index)
		{
			return true;
		}
		else
		{
			return false;
		}
	}
	int insert(string &index)
	{
		int pos, times;
		if (search(index, pos, times))
		{
			return 2;
		}
		else if (times < size / 2)
		{
			elem[pos] = index;
			return 1;
		}
		else
		{
			recreate();
			return 0;
		}
	}
	void recreate()
	{
		string *temp_elem;
		temp_elem = new string[size];
		for (int i = 0; i < size; i++)
		{
			temp_elem[i] = elem[i];
		}
		int copy_size = size;
		size *= 2;
		delete[] elem;
		elem = new string[size];
		for (int i = 0; i < size; i++)
		{
			elem[i] = "#";
		}
		for (int i = 0; i < copy_size; i++)
		{
			if (temp_elem[i] != "#")
			{
				insert(temp_elem[i]);
			}
		}
		delete[]temp_elem;
	}
};

int main()
{
	Hashtable hashtable;
	string buffer;
	int n;
	cin >> n;
	for (int i = 1; i <= n; i++)
	{
		cin >> buffer;
		int ans = hashtable.insert(buffer);
		if (ans == 0)
		{
			cout << "insert failed!" << endl;
		}
		else if (ans == 1)
		{
			cout << "insert success!" << endl;
		}
		else if (ans == 2)
		{
			cout << "It already exists" << endl;
		}
	}
	int temp_pos, temp_times;
	cin >> buffer;
	if (hashtable.search(buffer, temp_pos, temp_times))
	{
		cout << "search success!" << endl;
	}
	else
	{
		cout << "search failed!" << endl;
	}


	system("pause");
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
哈希表是一种高效的数据结构,可以用来存储和查找键值对。其,哈希函数将键映射到一个特定的桶,每个桶存储一组键值对。在哈希表,如果两个键被映射到同一个桶,就会发生碰撞。为了解决这个问题,可以使用链表法。 链表法是一种解决哈希表碰撞问题的方法。具体来说,对于哈希表的每个桶,可以使用一个链表来存储所有映射到该桶的键值对。如果发生碰撞,只需要将新的键值对添加到链表的末尾即可。 下面是一个使用链表法实现哈希表的示例代码: ```python class Node: def __init__(self, key, value): self.key = key self.value = value self.next = None class HashTable: def __init__(self, capacity): self.capacity = capacity self.buckets = [None] * capacity def hash_function(self, key): return hash(key) % self.capacity def put(self, key, value): index = self.hash_function(key) node = self.buckets[index] while node: if node.key == key: node.value = value return node = node.next new_node = Node(key, value) new_node.next = self.buckets[index] self.buckets[index] = new_node def get(self, key): index = self.hash_function(key) node = self.buckets[index] while node: if node.key == key: return node.value node = node.next return None def remove(self, key): index = self.hash_function(key) node = self.buckets[index] prev = None while node: if node.key == key: if prev: prev.next = node.next else: self.buckets[index] = node.next return prev = node node = node.next ``` 在这个示例,我们定义了一个Node类来表示哈希表的每个节点,每个节点包含一个键、一个值和一个指向下一个节点的指针。我们还定义了一个HashTable类来实现哈希表,其包含一个桶数组和一些基本的操作方法,如put、get和remove。 在put方法,我们首先使用哈希函数计算出键的索引,然后遍历桶的链表,查找该键是否已经存在于哈希表。如果找到了该键,我们只需要更新其对应的值即可。否则,我们创建一个新的节点,并将其添加到链表的开头。 在get方法,我们同样使用哈希函数计算出键的索引,然后遍历桶的链表,查找该键的值。如果找到了该键,我们返回其对应的值。否则,返回None。 在remove方法,我们首先使用哈希函数计算出键的索引,然后遍历桶的链表,查找该键。如果找到了该键,我们将其从链表删除即可。 总的来说,链表法是一种简单且常用的哈希表解决碰撞问题的方法。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值