哈希表——数据结构——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头文件

#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

#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);
		}
	}
	
}

相关推荐

  1. 数据结构-

    2024-03-27 16:02:05       43 阅读
  2. 数据结构——

    2024-03-27 16:02:05       8 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-03-27 16:02:05       16 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-03-27 16:02:05       16 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-03-27 16:02:05       15 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-03-27 16:02:05       18 阅读

热门阅读

  1. Python GUI编程(Tkinter)

    2024-03-27 16:02:05       16 阅读
  2. 浅析机器学习的常用方法

    2024-03-27 16:02:05       17 阅读
  3. 一些常见的PostgreSQL问题和答案

    2024-03-27 16:02:05       15 阅读
  4. 代码随想录阅读笔记-二叉树【递归遍历】

    2024-03-27 16:02:05       18 阅读
  5. Mybatis在SpringBoot中是如何被加载执行

    2024-03-27 16:02:05       18 阅读