散列表查找(哈希表)

散列函数构造经常要考虑:

1.散列表的长度

2.关键字的长度

3.关键字的分布情况

4.计算散列函数所需的时间

5.记录的查找频率

一个“好”的散列函数应遵循一下两条原则

(1)函数计算要简单,每一关键字只能有一个散列地址与之对应

(2)函数的值域需在表长范围内,计算出的散列地址的分布应均匀,尽可能减少冲突

 构造散列函数的几种常用分析法:数字分析法、平方取中法、折叠法、除留余数法

处理冲突的方法:

1.开放地址法,Hi = ( H(key) + di )% m,i=1,2,3,4,5,6,......(线性探测法、二次探测法)

2.链地址法(把具有相同散列地址的记录放在同一个单链表中)

 

提示:线性探测法在处理冲突的过程中容易产生二次聚集,使得散列地址不相同的记录又产生新的冲突;而链地址法处理冲突不会发生类似的情况,因为散列地址不同的记录在不同的链表中,所以链地址法的平均查找长度小于开放地址法。

 

线性探测法简单例子(用学生姓名当关键字,用哈希表存储学生信息):

#include<iostream>
using namespace std;

#define OK 1
#define SUCCESS 1
#define FAIL 0
#define HASHSIZE 12 //哈希表大小
#define NULLKEY '#'

struct Node {//元素结点
	char name[100];//学生姓名
	float high;//身高
	float weight;//体重
};

struct HashTable {//哈希表
	Node *elem;//声明元素结点
	int count;//结点数量
};

int m;

//初始化
bool InitHashTable(HashTable *H) {
	int i;
	H->count = HASHSIZE;
	m = HASHSIZE;
	H->elem = new Node[HASHSIZE];//建立元素对象
	//初始化元素
	for (i = 0; i < H->count; i++)
		H->elem[i].name[0] = NULLKEY;//把名字初始化为 # 
	return OK;
}

//获取对应的地址
int Hash(int key) {//散列函数
	return key%HASHSIZE;  //除留余数法
}

//插入元素到哈希表
void InsertHash(HashTable *H) {
	char key[10]; //关键字(姓名)
	cin >> key;

	int addr = Hash(key[0]);//获取哈希地址

	while (H->elem[addr].name[0]!= NULLKEY)//如果该地址已存在元素,则利用公式找到下一个地址,然后进行存储
		addr = (addr + 1) % m;

	//对元素进行赋值
	strcpy_s(H->elem[addr].name, key);
	cin >> H->elem[addr].high;
	cin >> H->elem[addr].weight;
}

//利用关键字在哈希表搜索
bool SearchHash(HashTable *H, int &addr) {
	char key[10];
	cin >> key; //输入关键字
	addr = Hash(key[0]);//利用散列公式获取关键字对应的地址

	while (strcmp(key, H->elem[addr].name) != 0) {//如果该地址没有存放当前搜索的关键字,
		addr = (addr + 1) % m;//则利用冲突公式找到下一个地址,再进行判断
		if (addr == Hash(key[0]) || H->elem[addr].name[0] == '#') {//如果遍历直到返回到原来的地址,或者找到该地址存储的关键字为空,则查找失败
			return FAIL;
		}
	}
	return SUCCESS;
}

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

	int num, addr;
	cin >> num;//插入元素数量
	while (num--)InsertHash(h);
	cin >> num;//查找次数
	while (num--) {
		if (SearchHash(h, addr))
			cout << h->elem[addr].high << " " << h->elem[addr].weight << endl;
		else 
			cout << "Not Found!" << endl;
	}
	return 0;
}

链地址法:

#include<iostream>
using namespace std;
#define NULLKEY '#'
#define TABLESIZE 49
#define SUCCESS 1
#define FAIL 0


struct Node {
	char name[100];
	int age;
	Node *next;//指向相同散列地址的下一个结点
};

struct HashTable {
	int count;
	Node *elem;
};

int m;
void InitHashTable(HashTable *H) {
	m = TABLESIZE;
	int i;
	H->elem = new Node[TABLESIZE];
	for (i = 0; i < m; i++) {
		H->elem[i].name[0] = '#';// #代表还没有放进数据
		H->elem[i].next = NULL;
	}
}

//散列函数
int getAddr(int key) {
	return key - 95;
}

void InsertHash(HashTable *H) {
	char key[20];
	int addr;

	cin >> key;
	addr = getAddr(key[0]);
	Node *p, *t;

	if (H->elem[addr].name[0] == '#') {//如果该地址没有放数据
		strcpy_s(H->elem[addr].name, key);
		cin >> H->elem[addr].age;

		//创建一个还没有放进数据的结点
		t = new Node;
		t->name[0] = '#';
		H->elem[addr].next = t;
	}
	else {
		//遍历到最后一个结点(没有放数据的结点)
		p = H->elem + addr;
		while (p->name[0] != '#')p = p->next;
		//把数据放进这个结点
		strcpy_s(p->name, key);
		cin >> p->age;
		//创建一个还没有放进数据的结点
		t = new Node;
		t->name[0] = '#';
		p->next = t;
	}
}

bool SearchHash(HashTable *H,Node *&p) {
	char key[20];
	int addr;
	cin >> key;
	addr = getAddr(key[0]);

	if (strcmp(H->elem[addr].name, key) != 0) {//如果该地址放的数据和搜索的关键字不一样
		p = H->elem[addr].next;//p指向下一个地址
		while (strcmp(p->name, key) != 0 && p->name[0] != '#')//如果关键字匹配不成功 和 还没有链表尾巴,
			p = p->next;//则p指向下一个结点
	}
	else {
		p = H->elem + addr;
	}

	if (p->name[0] == '#')return FAIL;//如果p指向链表的尾巴,则搜索失败,返回0
	return SUCCESS;
}

int main() {
	HashTable *h = new HashTable;
	InitHashTable(h);
	int count;
	cin >> count;
	while (count--)InsertHash(h);
	cin >> count;
	while (count--) {
		Node *addrPoint = NULL;
		if (SearchHash(h, addrPoint)) {
			cout << addrPoint->age <<  endl;
		}
		else cout << "Not found!" << endl;
	}
	return 0;
}

 

 

 

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值