哈希表——数据结构——day8

哈希表的定义

存储位置=f(关键字)
散列技术 (哈希表的技术)是在记录的存储位置和它的关键字之间建立一个确定的对应关系 f,使得每个关键字 key 对应一个存储位置f(key)。查找时,根据这个确定的对应关系找到给定值 key 的映射 f(key),若查找集合中存在这个记录,则必定在f(key)的位置上。
这里我们把这种对应关系f称为散列函数,又称为哈希(Hash)函数。
采用散列技术将记录存储在一块连续的存储空间中,这块连续存储空间称为散列表或哈希表(Hash table)。

散列表查找步骤

整个散列过程其实就是两步。

(1)在存储时,通过散列函数计算记录的散列地址,并按此散列地址存储该记录 。就像张三丰我们就让他在体育馆,那如果是'爱因斯坦'我们让他在图书馆,如果是'居里夫人',那就让她在化学实验室,如果是'巴顿将军',这个打仗的将军一一我们可以让他到网吧。总之,不管什么记录,我们都需要用同一个散列函数计算出地址再存储。

(2)当查找记录时,我们通过同样的散列函数计算记录的散列地址,按此散列地址访问该记录 。说起来很简单,在哪存的,上哪去找,由于存取用的是同一个散列函数,因此结果当然也是相同的。

所以说,散列技术既是一种存储方法,也是一种查找方法。

散列函数的构造方法

直接定址法

如果我们现在要对0~100 岁的人口数字统计表,如表所示,那么我们对年龄这个关键字就可以直接用年龄的数字作为地址。此时f(key)=key。

如果我们现在要统计的是80 后出生年份的人口数 ,如表所示,那么我们对出生年份这个关键字可以用年份减去1980来作为地址。此f(key)=key-1980。

也就是说,我们可以取关键字的某个线性函数值为散列地址 ,即f(key)=a×key+b (a、b为常数)

数字分析法

如果我们的关键字是位数较多的数字,比如我们的 11 位手机号 "130xxxx1234", 其中前三位是接入号,一般对应不同运营商公司的子品牌,如 130是联通如意通、136 是移动神州行、153 是电信等;中间四位是HLR 识别号,表示用户号的归属地;后四位才是真正的用户号,如表所示。

这里我们提到了一个关键词------抽取。抽取方法是使用关键字的一部分来计算散列存储位置的方法,这在散列函数中是常常用到的手段。

数字分析法通常适合处理关键字位数比较大的情况,如果事先知道关键字的分布且关键字的若干位分布较均匀,就可以考虑用这个方法。

除留余数法

此方法为最常用的构造散列函数方法。对于散列表长为m的散列函数公式为:
f(key)=key mod p(p≤m)

mod 是取模(求余数)的意思。事实上,这方法不仅可以对关键字直接取模,也可在折叠、平方取中后再取模。

很显然,本方法的关键就在于选择合适的p,p 如果选得不好,就可能会容易产生同义词。

例如表,我们对于有 12 个记录的关键字构造散列表时,就用了f(key) =key.mod 12的方法。比如 29 mod12=5,所以它存储在下标为5的位置。

我们不选用p=12来做除留余数法,而选用p=11,如表所示。

此就只有12和144有冲突,相对来说,就要好很多。

例子:

这里我们写了一个例子,看哈希表如何应用。

hash.h头文件

c 复制代码
#ifndef _HASH_H_
#define _HASH_H_


#define HASN_SIZE 27

typedef struct per		//创建数据内容
{
	char name[64];
	char tel[31];
	char addr[64];
	int age;
}DATA_TYPE;


typedef struct hsnode
{
	DATA_TYPE data;
	struct hsnode *pnext;
}HASH_NODE;		



extern int insert_hash_table(DATA_TYPE data);
extern void hash_for_each();
extern HASH_NODE *find_hash_table(char *name);
extern void destroy_hash_table();

#endif

hash.c

c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "hash.h"


HASH_NODE *hash_table[HASN_SIZE] = {NULL};


int hash_fun(char ch)	//判断,因为是通过首字母排序
{
	if (ch >= 'a' && ch <= 'z')
	{
		return ch-'a';
	}
	else if (ch >= 'A' && ch <= 'Z')
	{
		return ch-'A';
	}
	else
	{
		return HASN_SIZE-1;
	}
}


int insert_hash_table(DATA_TYPE data)	//插入数据
{
	HASH_NODE *pnode = malloc(sizeof(HASH_NODE));
	if (NULL == pnode)
	{
		perror("fail malloc");
		return -1;
	}
	pnode->data = data;
	pnode->pnext = NULL;

	
	int addr = hash_fun(data.name[0]);
	
	//hash_table[addr] ========>phead

	pnode->pnext = hash_table[addr];
	hash_table[addr] = pnode;

	return 0;
}


void hash_for_each()	//遍历哈希表
{
	for (int i = 0; i < HASN_SIZE; i++)
	{
		HASH_NODE *ptmp = hash_table[i];
		while (ptmp != NULL)
		{
			printf("%s %s %s %d\n", ptmp->data.name, ptmp->data.tel, ptmp->data.addr, ptmp->data.age);
			ptmp = ptmp->pnext;
		}
		printf("\n");
	}
}


HASH_NODE *find_hash_table(char *name)	//通过名字查找其其余信息
{
	int addr = hash_fun(name[0]);

	HASH_NODE *ptmp = hash_table[addr];
	
	while (ptmp != NULL)
	{
		if (!strcmp(name, ptmp->data.name))
		{
			return ptmp;
		}
		ptmp = ptmp->pnext;
	}
	
	return NULL;
}

void destroy_hash_table()	//删除哈希表
{
	HASH_NODE *ptmp = NULL;
	for (int i = 0; i < HASN_SIZE; i++)
	{
		while(hash_table[i] != NULL)
		{
			ptmp = hash_table[i];
			hash_table[i] = ptmp->pnext;
			free(ptmp);
		}
	}
	
}
相关推荐
@Java小牛马3 分钟前
排序算法原理及其实现
java·数据结构·算法·排序算法
程序员老冯头1 小时前
第二十三章 C++ 继承
开发语言·数据结构·c++·算法·继承
孙尚香蕉3 小时前
深入探索哈夫曼编码与二叉树的遍历
数据结构·算法
济宁雪人3 小时前
数据结构之栈和队列
数据结构
andyweike3 小时前
数据结构-排序思想
数据结构·算法·排序算法
权^3 小时前
list的介绍(详解)
数据结构·list
myloveasuka4 小时前
list详解
数据结构·list
TANGLONG2224 小时前
【初阶数据结构与算法】八大排序之非递归系列( 快排(使用栈或队列实现)、归并排序)
java·c语言·数据结构·c++·算法·蓝桥杯·排序算法
小白—人工智能5 小时前
有一个4*5的矩阵如下,要求编写程序计算总和与平均值,并找出其中值最大的那个元素输出,以及其所在的行号和列号。
数据结构·python·算法·矩阵
轩源源5 小时前
C++草原三剑客之一:继承
开发语言·数据结构·c++·算法·青少年编程·继承·组合