开放定址法与二次探测法构造散列表

关键字集合{12,67,56,16,25,37,22,29,15,47,48,34}。

开放定址法

开放定址法公式 f i ( k e y ) = ( f i − 1 ( k e y ) + d i )   m o d   m   ( d i = 1 , 2 , 3 ) f_i(key)=(f_{i-1}(key)+d_i)\space mod\space m\space (d_i=1,2,3) fi(key)=(fi1(key)+di) mod m (di=1,2,3)

在这个例子中,我们取 d i = 1 , m = 12 d_i=1,m=12 di=1,m=12所以我们遇到冲突的解决方案是
f i ( k e y ) = ( f i − 1 ( k e y ) + 1 )   m o d   12 f_i(key)=(f_{i-1}(key)+1)\space mod\space 12 fi(key)=(fi1(key)+1) mod 12

散列函数f(key)=key mod 12。

计算前5个数

keymf(key)
12120
67127
56128
16124
25121

前5个都是没有冲突的,直接存入,如下表。

下标01234567891011
关键字1225166756

key=37时,f(37)=37 mod 12 =1,与25冲突,这时候应用上面的公式
f(37)=(f(37) +1)+mod 12=2;
此时2是空闲的,所以把37存入2中。

下标01234567891011
关键字122537166756

接下来的22,29,15,47都是没有冲突的,直接存入

下标01234567891011
关键字1225371516296756 2247

接下来是48
f(48)=48 mod 12=0;冲突
f(48)=(0+1) mod 12=1;冲突

f(48)=(5+1) mod 12=6;没有冲突,填入。

下标01234567891011
关键字1225371516294867562247

接下来是34
f(34)=34 mod 12=10;冲突

最后的34经过11次冲突之后填入下标为9的空格中

下标01234567891011
关键字122537151629486756342247

普通开放定址发代码:

#include <iostream>
#include <cstdlib>
#define HASHSIZE 12/*散列表的大小*/
#define NULLKEY -1/*空白标志*/

using namespace std;
typedef struct
{
	int *elem;
	int count;
}HashTable;

int m = 12; 

bool InitHashTable(HashTable *H)/*初始化*/
{
	H->elem = (int*)malloc(HASHSIZE * sizeof(int));
	for (int i = 0; i < HASHSIZE; i++) H->elem[i] = NULLKEY;
	return true;
}

int Hash(int key)/*哈希函数f(key)=key mod m,这里mod取12*/
{
	return key % m;
}

void InsertHash(HashTable *H, int key)/*开放定址法*/
{
	int addr = Hash(key);/*计算哈希值*/
	while (H->elem[addr] != NULLKEY) addr = (addr + 1) % m;/*如果该地址已被占用,则地址加一继续寻找*/
	H->elem[addr] = key;/*找到空闲地址,插入*/
}

bool SearchHash(HashTable *H, int key, int &addr)/*查找*/
{
	addr = Hash(key);
	while (H->elem[addr] != key)
	{
		addr = (addr + 1) % m;
		if (H->elem[addr] == NULLKEY || addr == Hash(key)) return false;/*如果查找到空闲地址或者又回到了原地,则说明该表中没有该关键字*/
	}
	return true;
}

int main()
{
	HashTable *test=new HashTable;
	InitHashTable(test);

	int a[12] = {12,67,56,16,25,37,22,29,15,47,48,34};/*要插入的key*/
	for (int i = 0; i < 12;i++) InsertHash(test,a[i]);

	int addr;
	SearchHash(test,48, addr);/*查找48所在的地址*/
	cout <<"48的地址是:" <<addr<<endl;

	cout << "散列表:" << endl;
	for (int i = 0; i < HASHSIZE; i++) cout << test->elem[i]<<" ";/*查找哈希数组中各个元素*/

	system("pause");
}

让我们来看下程序输出的结果

在这里插入图片描述
和理论计算的一样。


二次探测法

在上面的例子中,我们发现48和34要经过很多次冲突才能找到合适的位置,特别是最后一个34,最后的一个空位就在第一次哈希值前面,但是由于我们解决冲突的方案是
f i ( k e y ) = ( f i − 1 ( k e y ) + 1 )   m o d   12 f_i(key)=(f_{i-1}(key)+1)\space mod\space 12 fi(key)=(fi1(key)+1) mod 12
使得我们只能向后寻找空闲的位置,而不能向前,那我们能不能改进一下呢?

二次探测法就是解决这个问题的,二次探测法的解决冲突的方案是
f i ( k e y ) = ( f i − 1 ( k e y ) + d i )   m o d   m ( d i = 1 2 , − 1 2 , 2 2 , − 2 2 . . q 2 , − q 2 , q ≤ m / 2 ) f_i(key)=(f_{i-1}(key)+d_i)\space mod\space m(d_i=1^2,-1^2,2^2,-2^2..q^2,-q^2,q\leq m/2) fi(key)=(fi1(key)+di) mod m(di=12,12,22,22..q2,q2,qm/2)
也就是我们遇到冲突时,先往右探测1个单位,如果不行,就往左探测一个单位,如果还不行,就往右探测 2 2 2^2 22个单位,如果还不行,就往左探测 2 2 2^2 22个单位…

二次探测法代码:

#include <iostream>
#include <cstdlib>
#include <math.h>
#define HASHSIZE 12/*散列表的大小*/
#define NULLKEY -1/*空白标志*/

using namespace std;
typedef struct
{
	int *elem;
	int count;
}HashTable;

int m = 12; 

bool InitHashTable(HashTable *H)/*初始化*/
{
	H->elem = (int*)malloc(HASHSIZE * sizeof(int));
	for (int i = 0; i < HASHSIZE; i++) H->elem[i] = NULLKEY;
	return true;
}

int Hash(int key)/*哈希函数f(key)=key mod m,这里mod取12*/
{
	return key % m;
}

void InsertHash(HashTable *H, int key)/*开放定址法*/
{
	int count = 0;/*记录冲突次数,以便调整左右探测方向,冲突次数为偶数时向右探测,为奇数时向左探测*/
	int addr = Hash(key);/*计算哈希值*/
	int temp = addr;
	while (H->elem[temp] != NULLKEY)
	{
		if(count%2==0)
		    temp = (addr + (int)pow((1 + count / 2),2)) % m;/*向右探测*/
		else
			temp = (addr - (int)pow((1 + count / 2), 2)) % m;/*向左探测*/
		count++;
	}
	H->elem[temp] = key;/*找到空闲地址,插入*/
}

bool SearchHash(HashTable *H, int key, int &addr)/*查找*/
{
	int count = 0;
	addr = Hash(key);
	int temp = addr;
	while (H->elem[temp] != key)
	{
		if (count % 2 == 0)
			temp = (addr + (int)pow((1 + count / 2), 2)) % m;/*如果该地址已被占用,则地址加一继续寻找*/
		else
			temp = (addr - (int)pow((1 + count / 2), 2)) % m;
		if (H->elem[temp] == NULLKEY || temp == Hash(key)) return false;/*如果查找到空闲地址或者又回到了原地,则说明该表中没有该关键字*/
		count++;
	}
	addr = temp;
	return true;
}

int main()
{
	HashTable *test=new HashTable;
	InitHashTable(test);

	int a[12] = {12,67,56,16,25,37,22,29,15,47,48,34};/*要插入的key*/
	for (int i = 0; i < 12;i++) InsertHash(test,a[i]);

	int addr;
	SearchHash(test,48, addr);/*查找48所在的地址*/
	cout <<"48的地址是:" <<addr<<endl;

	cout << "散列表:" << endl;
	for (int i = 0; i < HASHSIZE; i++) cout << test->elem[i]<<" ";/*查找哈希数组中各个元素*/

	system("pause");
}

在这里插入图片描述
二次探测法构造的散列表和开放定址法有些不一样,至于二次探测法的散列表结果为什么是这样,可以参考上面开放地址法的过程,这里不再详细写二次探测法散列表的构造过程。

下面是两个散列表的对比

方法下标01234567891011
开放定址法关键字122537151629486756342247
二次探测法关键字122537151629346756482247

参考书籍:《大话数据结构》

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个简单的开放定址散列表(线性探测)的实现代码,包括头文件和实现文件: 头文件 hashtable.h: ```c #ifndef HASHTABLE_H #define HASHTABLE_H #define TABLE_SIZE 1000 typedef struct { int key; int value; } HashNode; typedef struct { HashNode *nodes; int size; } HashTable; HashTable* createHashTable(); void destroyHashTable(HashTable *table); void insert(HashTable *table, int key, int value); int get(HashTable *table, int key); #endif ``` 实现文件 hashtable.c: ```c #include <stdlib.h> #include "hashtable.h" HashTable* createHashTable() { HashTable *table = (HashTable*) malloc(sizeof(HashTable)); table->nodes = (HashNode*) calloc(TABLE_SIZE, sizeof(HashNode)); table->size = TABLE_SIZE; return table; } void destroyHashTable(HashTable *table) { free(table->nodes); free(table); } void insert(HashTable *table, int key, int value) { int hash = key % table->size; while (table->nodes[hash].value != 0) { hash = (hash + 1) % table->size; } table->nodes[hash].key = key; table->nodes[hash].value = value; } int get(HashTable *table, int key) { int hash = key % table->size; while (table->nodes[hash].value != 0) { if (table->nodes[hash].key == key) { return table->nodes[hash].value; } hash = (hash + 1) % table->size; } return 0; } ``` 在主程序中,可以使用以下方来使用这个散列表: ```c #include <stdio.h> #include "hashtable.h" int main() { HashTable *table = createHashTable(); insert(table, 1, 100); insert(table, 2, 200); insert(table, 3, 300); printf("%d\n", get(table, 2)); destroyHashTable(table); return 0; } ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值