数据结构 --- c语言数组实现哈希结构

本文介绍了哈希地址的概念,强调它是通过哈希函数得到的逻辑地址,并非实际存储地址。讨论了多种哈希函数的选择,如取余法、直接定址法、平方取中法和折叠法。哈希冲突是不可避免的问题,文中提到了开放地址法和邻接表法两种解决冲突的方法。此外,还展示了如何使用C语言实现哈希表,包括插入数据、哈希函数和查找冲突位置的函数。最后,给出了一个简单的测试案例来验证哈希表的创建和操作。
摘要由CSDN通过智能技术生成

哈希地址

  • 哈希地址是一个逻辑地址,不是实际存在的地址,通过哈希函数得到哈希地址

哈希函数

  • 哈希函数由自己决定,选什么函数都可以,假设用数组存储哈希的数据,在数组中用下标去描述位置,通过数据去构造哈希地址,地址就是数组下标

  • 图中哈希结构的最大容量为 7

  • 一般通过哈希构造函数把数据和下标建立联系

  • 取余法得到哈希地址:数据的哈希地址 == 数据 % p(最大容量),通过 % 7得到哈希地址

  • 通过哈希构造函数,得到哈希地址 88 % 7 == 4 ,余4把它放在数组下标是4的位置 (把当前元素放到以当前哈希地址为下标的容量的位置)

  • 直接定址法:数据是多少,构建出来的哈希地址就是多少,元素是 88,构建出来的哈希地址就是 88,数据就是地址,地址就是数据,图中的元素最小需要的数组长度为 89,浪费空间

  • 平方取中法:把原来的数据做平方,取中间一部分

  • 折叠法:把字符串转为ASCII码,是一个很大的数字,把数字折叠

哈希冲突

  • 通过1个哈希函数所得到的地址可能存在重复(相同的元素)

处理哈希冲突

  • 开放地址法:如果要把具有哈希冲突的元素 4 存到哈希结构中,要去数组后面找空的位置,假设数组下标为6的位置有空缺,就把 4 放在这个位置,开放地址法就是把其他空的位置去存储具有哈希冲突的元素

  • 邻接表法:图的方式存储采用的就是邻接表法,不开放后面的元素,以当前位置(存在冲突的位置)去创建一个链表出来,把具有冲突的元素放在当前链表中--->以当前元素当做链表的表头去存储元素

构建数据类型

  • 如果要处理的是字符串类型的数据,构造一个键出来充当哈希结构的哈希地址的求解--->关联

  • 存字符串,没有整型数据,构建一个整型数据处理充当哈希结构的哈希地址的求解

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
typedef struct pair 
{
	int key;			//键
	char element[20];	//数据类型--->字符串类型
}DATA,*LPDATA;

 哈希表的创建--->采用取余法

  • 得到的哈希地址是用取余法来做的--->数据%p

  • 通过取余法所求的哈希地址,所产生的地址个数的范围: [0,p-1]

  • divisior的作用:限定哈希地址的数目 & 存储哈希元素

  • 如果用结构体数组充当哈希地址,不好判断空的情况

typedef struct hashTable 
{
	LPDATA* table;	   //数据用二级指针表示 便于初始化,以及判断当前hash地址是否存在冲突
	int divisor;       //H(key)=key%p -->就是限定hash地址的数目 数据要%p需要传入p
	int curSize;	   //当前元素个数
}HASH,*LPHASH;

 哈希结构的结构体描述--->用结构体指针表示哈希结构 

LPHASH createHashTable(int p) 
{
	LPHASH hash = (LPHASH)malloc(sizeof(HASH)); //动态内存申请
	assert(hash);
    //给数据做初始化
	hash->curSize = 0;
	hash->divisor = p;
	hash->table = (LPDATA*)malloc(sizeof(LPDATA)* hash->divisor); //容量由取余数决定
	assert(hash->table);
	for (int i = 0; i < hash->divisor; i++)    //多个一级指针
	{
		hash->table[i] = NULL;                 //二级指针申请内存后给每个一级指针做初始化
	}
	return hash;
}

 插入数据

  • 注意先申请内存再插入元素

  • 用的是二级指针,刚刚只申请了一级指针的内存

//要插入的表 要插入的数据
void insertData(LPHASH hash, DATA data) 
{
	//做插入前先求hash地址--->调用哈希函数                  //不存在哈希冲突
	int pos = search(hash, data.key);                    //找到要存储数据的哈希地址
	if (hash->table[pos] == NULL)                        //当前下标没有数据 直接插入
	{
		hash->table[pos] = (LPDATA)malloc(sizeof(DATA)); //注意先申请内存再拷贝元素
		memcpy(hash->table[pos], &data, sizeof(DATA));   //内存拷贝
		hash->curSize++;
	}
	else                                                 //存在哈希冲突
	{
		if (hash->table[pos]->key == data.key)           //键相同回到原来位置 
		{
			strcpy(hash->table[pos]->element, data.element); //用覆盖元素的方式
		}
		else                                             //键不同回到原来位置说明满了
		{
			printf("hash表满了,无法插入!\n");             
			return;
		}
	}
}

 哈希函数 

  • 可能会存在哈希冲突,需要找合适的位置去存放元素,不一定是直接取余,直接取余得到的地址不一定能用

  • 传入键,通过键去找:因为是通过键去生成哈希地址

  • 找完一圈后回到原来的位置(通过取余法),说明没有合适的位置

  • 如果 curPos + 1中没有元素,把元素放在当前位置即可

//要找的表 传入键
int search(LPHASH hash, int key) 
{
	int pos = key % hash->divisor;  //不存在冲突的hash地址 正常情况
	int curPos = pos;               //存在冲突 开放地址法做哈希地址的查找
	do 
	{
		//key相同,采用覆盖的数据方式 不当做哈希冲突来做
		if (hash->table[curPos] == NULL||hash->table[curPos]->key==key)
			return curPos;          //判断当前位置能不能用 ==NUll说明可以用
		curPos=(curPos + 1)%hash->divisor; //不为NULL往后走 如果后面为NULL就插到当前位置
	} while (curPos != pos);        //当前Pos不等于原来Pos就一直去做查找
	return curPos;                  
}
//!= 原来地址一直做查找 
//== 原来地址:找完一圈后回到原来的位置说明它没有合适的位置 直接返回

打印哈希表--->打印数组

void printHash(LPHASH hash) 
{
	for (int i = 0; i < hash->divisor; i++) //用最大容量长度做打印
	{
		if (hash->table[i] == NULL) 
		{
			printf("NULL\n");
		}
		else                                //不为空打印元素
		{
			printf("%d:%s\n", hash->table[i]->key, hash->table[i]->element);
		}
	}
}

 测试代码

int main() 
{
	LPHASH hash = createHashTable(10);     //创建哈希表
	DATA array[5] = { 1,"雷电",11,"春天",23,"四月",44,"baby",56,"virtual" };
	for (int i = 0; i < 5; i++) 
	{
		insertData(hash, array[i]);        //把数据插到哈希表中
	}
	printHash(hash);                       //打印哈希表
	return 0;
}
//测试代码

NULL
1:雷电
11:春天
23:四月
44:baby
NULL
56:virtual
NULL
NULL
NULL
  • 8
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

qiuqiuyaq

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值