哈希表--数据结构

此前学习了链表与数组两种存储数据的结构,但是二者都有其缺点,数组因为其存储空间连续的特性可以直接访问到其中任意元素,但是此特性也导致其存储空间不能随意的增加或减少(连续空间有可能被其他程序调用)。而链表因其不依赖连续空间的关系可以随意增删,但是想要直接访问其中的元素并不容易,需要借助头节点依次访问。

于是这里将两种数据结构组合,用来制作哈希表(链地址法)

哈希表的基础思想在于存储位置与其实际值的关系,一般是通过一个函数(hash函数)得到其存储位置(数组下标)

比较常用的是除留余数法,这里以此为例写代码

由于不同值经过hash函数后可能得到相同的结果,对此问题,有两种解决方法

链地址法:哈希表的每个数组元素不再只存一个数据,而是一个链表(链)的头指针(也可以是一个节点)。当不同的 key 计算出相同的哈希地址(冲突)时,它们被"串"在同一个链表的节点中。

开放地址法:在开放地址法中,哈希表数组的大小(m)必须大于元素个数(n),当插入时发现下标 H(key) 已被占用,就按照某种规则去探测下一个空闲位置,直到找到空位为止。

哈希表创建函数

这里直接用指针作为数组元素,宏定义数组大小

复制代码
#include <stdio.h>
#include <stdlib.h>
#define SIZE 10
typedef int data_t;
typedef struct node
{
	data_t data;
	struct node *next;  
}node_t;

node_t *hash_table[SIZE]; //NULL
int hash_create(data_t *a,int len)
{

	int i = 0;
	for (i = 0; i <len; ++i)
	{
		int addr = a[i] % SIZE;
		node_t *p_new = malloc(sizeof(node_t));
		if (p_new == NULL)
		{
			return -1;
		}
		p_new->data = a[i];
		p_new->next = NULL;
		if (hash_table[addr] == NULL)
		{
			hash_table[addr] = p_new;
		}else 
		{
			p_new->next = hash_table[addr];
			hash_table[addr] = p_new;
		}
	}

	return 0;
}

tips:取余时并不局限在10(结果在0~9),可以用更大或更小的数,关键在于让其分散存储,要避免数据聚集,数据聚集会导致查找效率下降(链表需要遍历)。

哈希表遍历函数

复制代码
void hash_show()
{
 // 拿出hash_table中的元素
 // 看是否为NULL
 // 不为NULL
 // 则遍历链表
	int i = 0;
	for (i = 0; i < SIZE; ++i)
	{
		printf("%d[%p]->",i,hash_table[i]);
		if (hash_table[i] != NULL)
		{
			node_t *p = hash_table[i];
			while (p)
			{
				if (p->next!=NULL)
					printf("%d->",p->data);
				else 
					printf("%d|NULL",p->data);
				p = p->next;
			}
		} 
		putchar('\n');
	}
}

哈希表查找函数

复制代码
node_t *hash_find(data_t data)
{
	int addr = data%SIZE;

	if (hash_table[addr] == NULL)
	{
		return NULL;
	}else 
	{
		node_t *p = hash_table[addr];

		while (p)
		{
			if (p->data == data)
			{
				return p;
			}
			p = p->next;
		}
	}

	return NULL;

}

哈希表修改函数

复制代码
node_t *hash_update(data_t old,data_t new)
{
	//   1.先求出addr
	//   data % SIZE
	//   2.找到位置
	//     遍历链表 找到对应的值
	//   3.找到了返回 对应节点的地址
	//     找不到则 返回NULL
   
	int addr = old%SIZE;

	if (hash_table[addr] == NULL)
	{
		return NULL;
	}else 
	{
		node_t *p = hash_table[addr];

		while (p)
		{
			if (p->data == old)
			{
				p->data = new; //修改 
				return p;
			}
			p = p->next;
		}
	}

	return NULL;
}

哈希表删除函数

复制代码
int hash_delete_key(data_t data)
{
    //思路:
    //1.表中如果没有这个值
    // 没法删
    //2.有该数据
    //
    // 链表可能就只有一个节点
    // 不止一个节点
    // 找到该节点的前一个节点 删除

	int addr = data%SIZE;
	if (hash_table[addr] == NULL)
	{
		return -1;
	}else 
	{
		node_t *p = hash_table[addr];
		
		if(p->next == NULL)
		{
			if (p->data == data)
			{
				free(p);
				hash_table[addr] = NULL;
			}
		}else 
		{
			if (p->data == data)
			{
				hash_table[addr] = p->next;
				free(p);
			}else 
			{
				while (p->next && p->next->data != data)  //当p的下一节点为空,或者下一节点
				{                                         //查找的节点时停下
					p=p->next;
				}
				if (p->next == NULL)
				{
					return -1;
				}
				node_t *p_temp = p->next;        //删除目标节点
				p->next = p_temp->next;
				free(p_temp);
			}
		}
	}

	return 0;
}

哈希表销毁函数

复制代码
void hash_destroy(void)
{
	int i = 0;
	for (i = 0; i < SIZE; ++i)
	{
		if (hash_table[i] !=NULL)
		{
			node_t *p = hash_table[i];
			hash_table[i] = NULL;
			
			while (p)
			{
				node_t *p_temp = p;
				p = p->next;
				free(p_temp);
			}
		}
	}
}
相关推荐
Herbert_hwt15 小时前
C语言零基础入门:循环控制与数据类型详解
c语言·数据结构·算法
晚风醉蝶16 小时前
1-6-插入排序-InsertionSort
java·数据结构·排序算法
Tyler_TXZ17 小时前
C++C语言之——二叉树
c语言·开发语言·数据结构·c++·二叉树
有点。17 小时前
C++二叉树二(练习题)
数据结构·c++·算法·图论
LuminousCPP17 小时前
栈和队列专题(一):LeetCode 20. 有效的括号
数据结构·经验分享·笔记·leetcode·手写栈
白狐_79819 小时前
408 数据结构|线索二叉树两题详解:先序线索化后的空链域 + 中序前驱/后继判断
c语言·数据结构·链表
疯狂打码的少年20 小时前
【数据结构】二叉排序树(BST)的定义与操作
数据结构·笔记·算法
wabs66620 小时前
关于字符串【力扣541.反转字符串II的思考】
数据结构·算法·leetcode·字符串
旖旎夜光1 天前
LeetCode 30:串联所有单词的子串(滑动窗口) —— 题解
数据结构·c++·算法·leetcode·滑动窗口