哈希表

哈希表的结构是这样的


//HashTable.h

#ifndef _HASHTABLE_H_
#define _HASHTABLE_H_

#include <stdlib.h>
#include <stdbool.h>

#define BUCKET_SIZE		5					//基桶与溢出桶大小,此处的值表示可容纳5个元素
#define HASHTABLE_SIZE	17					//哈希表中基桶个数,为了使得出的哈希因子在所有可得的值中得出任一值的概率相同,所以使用素数作为基桶个数
#define INVALID_VALUE	-11111				//无效标记

typedef int EleType;
typedef int KeyType;

struct _ElementNode_						//元素节点类型,键值对
{
	KeyType Key;
	EleType Val;
};	

//存储键值对,Hash与之前的平衡二叉树都应该是键值对,如果只有值,查找函数返回值的地址,如果使用地址修改了值,会发生令人不愉快的事情。
typedef struct _HashBucket_
{
	_ElementNode_ Data[BUCKET_SIZE];   
	struct _HashBucket_ *Bucket;			//解决冲突的方式是链地址法
}HashBucket;								//桶节点类型

typedef HashBucket* HashTable;

bool HashTableInit(HashTable *Hash);									//初始化哈希表
bool HashTableInsert(HashTable Hash, KeyType Key, EleType Data);		//插入键值对
EleType *HashTableFind(HashTable Hash, KeyType Key);					//根据键查找值
bool HashTableDelete(HashTable Hash, KeyType Key);						//删除键值对
void HashTableDestroy(HashTable *Hash);									//摧毁哈希表


#endif //_HASHTABLE_H_



//HashTable.c

#include "HashTable.h"

static int HashFunc(KeyType Key)
{
	return Key % HASHTABLE_SIZE;				//除留取余法
}

static _ElementNode_ *HashTableFind_(HashTable Hash, KeyType Key)		
{
	int pos = 0, i = 0;
	HashBucket *p = NULL;

	if( NULL == Hash )
		return NULL;

	pos = HashFunc(Key);						//求Key所对应的基桶
	p = Hash + pos;								//指向该基桶

	for (i = 0; i < BUCKET_SIZE; ++i)			//在基桶内寻找位置
	{
		if( Key == p->Data[i].Key )
			return p->Data + i;					//返回找到的位置
	}
	//不在基桶中
	while (NULL != p->Bucket)					//到溢出桶中查找
	{	
		p = p->Bucket;
		for (i = 0; i < BUCKET_SIZE; ++i)
		{
			if( Key == p->Data[i].Key )
				return p->Data + i;			
		}
	}
	//也不在溢出桶中
	return NULL;
}

//更新一个桶内的元素,如果是以存在元素则用新值更新,如果是不存在元素则找空位置插入
static bool UpdataBucket(HashBucket *p, KeyType Key, EleType Data)
{
	int i = 0;
	for (i = 0; i < BUCKET_SIZE; ++i)			//遍历该基桶中的每个位置,查找是否有空位,或者是否可以更新现有元素
	{
		if (Key == p->Data[i].Key)				//Key已存在则更新Val,相当于是对已存在元素进行修改
		{
			p->Data[i].Val = Data;
			return true;
		}
		if (INVALID_VALUE == p->Data[i].Key)	//Key不存在则增加元素记录,即将新元素存入哈希表
		{
			p->Data[i].Key = Key;
			p->Data[i].Val = Data;
			return true;
		}
	}
	return false;
}

//创建一张含有HASHTABLE_SIZE个基桶的哈希表
bool HashTableInit(HashTable *Hash)
{
	int i = 0, j = 0;
	HashBucket * p = (HashBucket *)malloc(sizeof(HashBucket) * HASHTABLE_SIZE);
	if( NULL == p )
	{
		*Hash = NULL;
		return false;
	}
	for (i = 0; i < HASHTABLE_SIZE; ++i)		//初始化全部基桶
	{
		for (j = 0; j < BUCKET_SIZE; ++j)		//初始化桶内每个位置
		{
			p[i].Data[j].Key = INVALID_VALUE;
			p[i].Data[j].Val = INVALID_VALUE;
		}
		p[i].Bucket = NULL;							
	}

	*Hash = p;
	return true;
}

//插入操作函数,当Key已存在时,更新元素,否则在空位置中插入元素
bool HashTableInsert(HashTable Hash, KeyType Key, EleType Data)
{
	int pos = 0, i = 0;
	HashBucket *p = NULL, *s = NULL;
	if( NULL == Hash )
		return false;

	pos = HashFunc(Key);						//求Key所对应的基桶
	p = Hash + pos;								//指向该基桶

	if( true == UpdataBucket(p, Key, Data) )	//桶内元素更新成功
		return true;

	//基桶内已没有空位置
	while (NULL != p->Bucket)					//在溢出桶内寻找位置
	{
		p = p->Bucket;
		if (true == UpdataBucket(p, Key, Data))	//桶内元素更新成功
			return true;
	}

	//在已查找过的桶内都未能找到合适的位置,创建一个新的溢出桶来存储元素
	s = (HashBucket *)malloc(sizeof(HashBucket));
	if( NULL == s )
		return false;
	for (i = 0; i < BUCKET_SIZE; ++i)			//初始化桶
	{
		s->Data[i].Key = INVALID_VALUE;		
		s->Data[i].Val = INVALID_VALUE;
	}
	s->Bucket = NULL;							//初始化桶

	s->Data[0].Key = Key;						//将Key值对存储在溢出桶首位置中
	s->Data[0].Val = Data;
	p->Bucket = s;								//将新桶挂接上去

	return true;
}


EleType *HashTableFind(HashTable Hash, KeyType Key)
{
	_ElementNode_ *p = HashTableFind_(Hash, Key);

	if( NULL != p )
		return &(p->Val);						//查找到元素,返回元素的值成员地址
	
	return NULL;
}

bool HashTableDelete(HashTable Hash, KeyType Key)
{
	_ElementNode_ *p = HashTableFind_(Hash, Key);

	if( NULL != p )
	{
		p->Key = INVALID_VALUE;					//查找、插入都是根据Key值来的,因此只需覆盖Key即可
		return true;
	}
	else
		return false;
}

void HashTableDestroy(HashTable *Hash)
{
	HashBucket *p = NULL, *q = NULL, *s = NULL;
	int i = 0;
	if( NULL == Hash )
		return ;
	
	p = *Hash;									//指向首个基桶
	for (i = 0; i < HASHTABLE_SIZE; ++i)		//释放全部基桶
	{
		q = p->Bucket;							//指向某个基桶的溢出桶所构成的链表
		while (NULL != q)						//释放溢出桶所构成的链表
		{
			s = q->Bucket;						
			p->Bucket = s;
			free(q);							
			q = s;
		}
		++p;
	}
	free(*Hash);								//释放全部基桶
	*Hash = NULL;
}



//main.c

#include "HashTable.h"
#include <stdio.h>

int main(int argc, char **argv)
{
	HashTable hash = NULL;
	EleType *e = NULL;
//	KeyType KeyArray[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
//	EleType EleArray[] = { 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 };
	KeyType KeyArray[] ={ 1, 18, 35, 52, 69, 86, 103, 120, 137, 171, 171, 188, 205, 222,  239, 255, 273 };
	EleType EleArray[] ={ 10, 180, 350, 520, 690, 860, 1030, 1200, 1370, 1710, 0, 1880, 2050, 2220, 2390, 2550, 2730 };
	int size = sizeof(KeyArray) / sizeof(KeyType);
	int i = 0;
	bool ret = true;
	KeyType key = 0;

	HashTableInit(&hash);

	for (i = 0; i < size && ret; ++i)
		ret = HashTableInsert(hash, KeyArray[i], EleArray[i]);

	key = 156;
	e = HashTableFind(hash, key);
	if( NULL != e )
	{
		printf("值为%d\n", *e);
		*e = 123;

		printf("修改后\n");

		e = HashTableFind(hash, key);
		if (NULL != e)
			printf("值为%d\n", *e);
	}
	else
		printf("键%d不存在\n", key);

	key = 171;
	ret = HashTableDelete(hash, key);
	if( true == ret )
	{
		printf("键值对%d删除成功\n", key);
		e = HashTableFind(hash, key);
		if (NULL != e)
			printf("值为%d\n", *e);
		else
			printf("键%d不存在\n", key);
	}
	else
		printf("键%d不存在\n", key);

	HashTableDestroy(&hash);


	return 0;
}





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值