哈希表的链表实现

本文介绍了如何使用C语言实现哈希表,包括哈希表的结构定义、哈希函数、初始化、插入、查找和删除元素等基本操作。哈希表通过链表解决冲突,每个桶是一个链表,插入和删除元素时需考虑链表操作。在删除元素时,需检查链表是否为空以避免错误。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#define DEFAULT_SIZE 16
//哈希表元素定义
typedef struct _ListNode
{
	struct _ListNode* next;
	int key;//关键值
    void* data;//指向数据元素的指针,而不是创建一个数据元素,因为其可能很大
}ListNode;

typedef ListNode* List;//链表
typedef ListNode* Element;//元素,概念不一样而已

//哈希表结构定义
typedef struct _HashTable
{
	int TableSize;//桶的大小
	List* Thelists;//指针数组,不存储数据,实际指针是int类型的,指向LinkNode类型数据,数组下标为索引
}HashTable;

//哈希函数(映射)(求余多用)
//key可能是字符串 
//根据哈希桶数量,根据key的值,定位Hash桶的位置,即根据key值(数据的值)映射到哈希桶的索引号
int Hash(int key, int TableSize)
{
	return(key % TableSize);
}


//哈希表接口
//哈希表初始化
HashTable* InitHash(int TableSize)
{
	int i = 0;
	HashTable* hTable = NULL;//创建Hash表
	if (TableSize <= 0) {
		TableSize = DEFAULT_SIZE;
	}
	hTable = (HashTable*)malloc(sizeof(HashTable));//这里分配的Thelists只是一个int类型的变量
	if (hTable == NULL)
	{
		printf("HashTable alloc error.\n");
		return NULL;
	}
	//分配空间后赋值结构体元素
	hTable->TableSize = TableSize;
	//为Hash桶分配内存空间,其为一个指针数组
	hTable->Thelists = (List*)malloc(sizeof(List) * TableSize);//这里分配的是int类型指针变量Thelists指向的具体的空间,因为int类型指针变量Thelists指向的List类型的数据(指向ListNode的指针),因此这里分配的是一个指针数组,也就是好几个桶组成的数组
	if (hTable->Thelists == NULL)
	{
		printf("HashTable malloc error\n");
		free(hTable);
		return NULL;
	}
	//为Has桶对应的指针数组初始化链表节点
		for (i = 0; i < TableSize; i++)//二级指针Thelists用数组形式使用数据,Thelists[i]就是一个指针
		{
			hTable->Thelists[i] = (ListNode*)malloc(sizeof(ListNode));//Thelists[i]就是一个指针,指向ListNode结构体,每一个桶是一个链表,分配一个头结点
			if (hTable->Thelists[i] == NULL)
			{
				printf("HashTable malloc error\n");
				free(hTable->Thelists);
				free(hTable);
				return NULL;
			}
			else
			{
				memset(hTable->Thelists[i], 0, sizeof(ListNode));//初始化头结点,头结点的元素值全为0,头结点不放元素,只是用于指向首节点

/*			hTable->Thelists[i]->key=0;
			hTable->Thelists[i]->next=NULL;
			hTable->Thelists[i]->data=NULL;*/
			}
		}
	return hTable;
}

//从哈希表中根据键值key查找元素-找到桶号i-找到元素key
Element Find(HashTable* HashTable, int key)
{
	int i = 0;
	List L=NULL;				//用于指向找到的桶号
	Element e = NULL;	//遍历时暂指向当前元素,先指向首节点
	i = Hash(key, HashTable->TableSize);//找到桶号,即key值映射到指针数组的下标为索引
	L = HashTable->Thelists[i];
	e = L->next;			//指向头节点,桶的第一个元素
	while (e != NULL && e->key != key)//遍历该桶,找元素
		e = e->next;
	return e;//key存在就返回对应节点指针,不存在就返回NULL
}


//向哈希表中插入元素,插入的是键值对,即键值及其元素
void Insert(HashTable* HashTable, int key, void* value)//void*类型可以兼容很多数据
{
	Element e = NULL, temp = NULL;
	List L = NULL;
	e = Find(HashTable, key);//找节点,看是否存在
	if (e == NULL)			//不存在则插入
	{
		temp = (Element)malloc(sizeof(ListNode));//生成一个节点
		if (temp == NULL)
		{
			printf("malloc error\n");
			return ;
		}
		L = HashTable->Thelists[Hash(key, HashTable->TableSize)];//桶的指针
		temp->data = value;//对心生成的节点赋值
		temp->key = key;
		temp->next = L->next;//前插法   头指针
		L->next = temp;
		printf("insert success\n");
	}
	else                    //存在则不插入
		printf("the key already exist\n");
}

//哈希表删除元素,元素为键值对
void Delete(HashTable* HashTable, int key)
{
	Element e = NULL, last = NULL;			//e暂放当前节点,last暂放前一个结点
	List L = NULL;						//放头结点
	int i = Hash(key, HashTable->TableSize);//找到哈希桶头结点
	L = HashTable->Thelists[i];

	last = L;//哈希桶指针
	e = L->next;//首节点				//首节点才放元素,从头结点开始遍历
	while (e != NULL && e->key != key)//遍历查找元素
	{
		last = e;					//保留上一个节点指针,方便后续修改链表结构
		e = e->next;
	}
	if (e)//如果键值对存在
	{
		last->next = e->next;//修改链表结构
		free(e);//删除该元素
	}
}
/*
void Delete(HashTable* HashTable, int key) {
    Element e = NULL, last = NULL;//结构体指针e
    List L = NULL;//哈希桶指针
    int i = Hash(key, HashTable->TableSize);
    L = HashTable->Thelists[i];
    e = L->next;//首结点 存放数据
    while (e != NULL && e->key != key) {
        last = e;
        e = e->next;
    }
    if (e != NULL) {
        if (last != NULL) {
            last->next = e->next;
        }
        else {
            L->next = e->next;
        }
        free(e);
        printf("Delete success\n");
    }
    else {
        printf("The key is not exist\n");
    }
}
*/
//获取元素的值
	void* Retrieve(Element e)
	{
		return e ? e->data : NULL;
	}

int main()
{
	char *elems[] = { (char*)"翠花",(char*)"小芳",(char*)"老师" };
	int i = 0;

	HashTable* HashTable = NULL;//定义一个哈希表
	HashTable = InitHash(5);
	Insert(HashTable, 1, elems[0]);
	Insert(HashTable, 2, elems[1]);
	Insert(HashTable, 3, elems[2]);
	Delete(HashTable, 1);

	for (i = 0; i < 3; i++)
	{
		Element e = Find(HashTable, i);
		if(e){
			printf("key:%d,values:%s\n",e->key,(char*)e->data);//Retrieve返回的是void*类型,在这里打印的是字符串型
		}
		else {
			printf("Not found [key:%d]\n", i);
		}
	}
		system("pause");
		return 0;
}





#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define DEFAULT_SIZE 16
typedef struct _ListNode {
    struct _ListNode *next;
    int key;
    void *data;
} ListNode;

typedef ListNode *List;
typedef ListNode *Element;

typedef struct _HashTable {
    int TableSize;
    List *Thelists;
} HashTable;

int Hash(int key, int TableSize) {
    return key % TableSize;
}

HashTable *InitHash(int TableSize) {
    int i;
    HashTable *hTable = (HashTable *) malloc(sizeof(HashTable));
    if (hTable == NULL) {
        printf("HashTable alloc error.\n");
        return NULL;
    }
    if (TableSize <= 0) {
        TableSize = DEFAULT_SIZE;
    }
    hTable->TableSize = TableSize;
    hTable->Thelists = (List *) malloc(sizeof(List) * TableSize);
    if (hTable->Thelists == NULL) {
        printf("HashTable malloc error\n");
        free(hTable);
        return NULL;
    }
    for (i = 0; i < TableSize; i++) {
        hTable->Thelists[i] = (ListNode *) malloc(sizeof(ListNode));
        if (hTable->Thelists[i] == NULL) {
            printf("HashTable malloc error\n");
            for (int j = i - 1; j >= 0; j--) {
                free(hTable->Thelists[j]);
            }
            free(hTable->Thelists);
            free(hTable);
            return NULL;
        } else {
            hTable->Thelists[i]->key = 0;
            hTable->Thelists[i]->next = NULL;
            hTable->Thelists[i]->data = NULL;
        }
    }
    return hTable;
}

Element Find(HashTable *HashTable, int key) {
    int i = 0;
    List L = NULL;
    Element e = NULL;
    i = Hash(key, HashTable->TableSize);
    L = HashTable->Thelists[i];
    e = L->next;
    while (e != NULL && e->key != key) {
        e = e->next;
    }
    return e;
}

void Insert(HashTable *HashTable, int key, void *value) {
    Element e = NULL, temp = NULL;
    List L = NULL;
    e = Find(HashTable, key);
    if (e == NULL) {
        temp = (Element) malloc(sizeof(ListNode));
        if (temp == NULL) {
            printf("malloc error\n");
            return;
        }
        L = HashTable->Thelists[Hash(key, HashTable->TableSize)];
        temp->data = value;
        temp->key = key;
        temp->next = L->next;
        L->next = temp;
        printf("Insert success\n");
    } else {
        printf("The key already exists\n");
    }
}

void Delete(HashTable *HashTable, int key) {
    Element e = NULL, last = NULL;
    List L = NULL;
    int i = Hash(key, HashTable->TableSize);
    L = HashTable->Thelists[i];
    e = L->next;
    while (e != NULL && e->key != key) {
        last = e;
        e = e->next;
    }
    if (e != NULL) {
        if (last != NULL) {
            last->next = e->next;
        } else {
            L->next = e->next;
        }
        free(e);
        printf("Delete success\n");
    } else {
        printf("The key is not exist\n");
    }
}

void* Retrieve(Element e) {
    if (e != NULL) {
        return e->data;
    } else {
        return NULL;
    }
}
int main(){
    char *elems[] = { (char*)"翠花",(char*)"小芳",(char*)"老师" };
    HashTable *hTable = InitHash(3);
    Insert(hTable, 1, elems[0]);
    Insert(hTable, 2, elems[1]);
    Insert(hTable, 3, elems[2]);
    Element e = Find(hTable, 1);
    Delete(hTable, 1);
    for (int i = 0; i < 3; i++) {
        Element e = Find(hTable, i + 1);
        if (e != NULL) {
            printf("key:%d, value:%s", e->key, (char*)e->data);
    }
    else {
        printf("key:%d, value:NULL", i + 1);
    }
    }
    return 0;
}

总结:

①哈希表的结构

②在删除结点时,需要判断链表是否为空,避免链表为空时,对数据进行空操作而造成错误。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值