前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【数据结构】哈希表(C++)

【数据结构】哈希表(C++)

作者头像
半生瓜的blog
发布2023-05-13 13:34:04
4210
发布2023-05-13 13:34:04
举报
文章被收录于专栏:半生瓜のblog半生瓜のblog

哈希表

概念

哈希表-散列表, 它是基于快速存储的角度设计的,也是一种典型的“空间换时间”的做法。

(键值(编号)就代表了这个数据。)

在这里插入图片描述
在这里插入图片描述

链式存储实现

代码语言:javascript
复制
#include<iostream>
using namespace std;

#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* ThisList;
}HashTable;

//根据key,计算索引,定位Hash桶的位置
int Hash(int key, int TableSize)
{
	return (key % TableSize);
}

//初始化哈希表
HashTable* initHash(int TableSize)
{

	HashTable* hTable = NULL;
	if (TableSize <= 0)
	{
		TableSize = DEFAULT_SIZE;
	}
	hTable = new HashTable;
	hTable->TableSize = TableSize;
	hTable->ThisList = new List[TableSize];//哈希桶	
	if (!hTable->ThisList)	
	{
		return NULL;
	}
	
	//为Hash桶的对应指针数组初始化链表结点	
	for (int i = 0; i < TableSize; i++)
	{
		hTable->ThisList[i] = new ListNode;
		if (!hTable->ThisList[i])
		{
			return NULL;
		}
		else
		{
			memset(hTable->ThisList[i], 0, sizeof(ListNode));
		}
	}

	return hTable;
}

//哈希表中查找元素
Element findHash(HashTable* hashtable, int key)
{
	int i = 0;
	List L = NULL;
	Element e = NULL;
	i = Hash(key, hashtable->TableSize);
	L = hashtable->ThisList[i];
	e = L->next;
	while (e && e->key != key)
	{
		e = e->next;
	}
	return e;
}

//哈希表插入元素
void insertHash(HashTable* hashtable, int key, void* value)
{
	Element e = NULL, tmp = NULL;
	List L = NULL;
	e = findHash(hashtable, key);

	if (!e) 
	{
		tmp = new ListNode;
		if (!tmp)
		{
			return;
		}
		L = hashtable->ThisList[Hash(key, hashtable->TableSize)];//前插法 
		tmp->data = value;
		tmp->key = key;
		tmp->next = L->next;
		L->next = tmp;
	}
	else
	{
		cout << "这个键已经存在" << endl;
	}
}

//哈希表删除元素
void DeleteHash(HashTable* hashtable, int key)
{
	Element e = NULL, last = NULL;
	List L = NULL;
	int i = Hash(key, hashtable->TableSize);
	L = hashtable->ThisList[i];
	last = L;
	e = L->next;
	//key是所对应数据的数字代号,这个每个元素是不同的,i是对应数据所在的hash桶,也就是key % tablesize,多个元素可以是同一个
	while (e && e->key != key)
	{
		last = e;
		e = e->next;
	}
	if (e)
	{
		last->next = e->next;
		delete e;
	}
	else
	{
		cout << "该key不存在" << endl;
	}
}

//找到对应的ListNode提取元素
void* extract(Element e)
{
	return  e ? e->data : NULL;
}
int main(void)
{
	  char *elems1 ="王小花";
	  char *elems2 ="李小华";
	  char *elems3 ="张富贵";


	 HashTable* hashtable = NULL;
	 hashtable = initHash(31);
	 insertHash(hashtable, 1, elems1);
	 insertHash(hashtable, 2, elems2);
	 insertHash(hashtable, 3, elems3);
		

	 //查找方式1
	 Element findky = findHash(hashtable, 2);
	 if (findky)
	 {
		 cout << (const char*)findky->data << endl;//强转
	 }
	 else
	 {
		 cout << "Not Find this Key!" << endl;
	 }


	//查找方式2	
	Element e = findHash(hashtable, 1);
	if (e)
	{
		cout << (const char*)extract(e) << endl;
	}
	else
	{
		cout << "Not Find this Key!" << endl;
	}


	return 0;
}

顺序存储实现

代码语言:javascript
复制
#include<iostream>
using namespace	 std;

#define Hash_Size 50
#define Hash_Bucket 128

typedef struct _HashMem//哈希表存储的数据类型
{
	int key;
	void* data;
}HashMember;

typedef struct _hash
{
	HashMember Hash_Table[Hash_Bucket][Hash_Size];
	int _HashSize;//哈希桶的索引
}Hash_Table;


//多个计数器不能指向同一个计数器变量,并且不能是局部变量,所以在这里创建一个全局的计数器变量数组
int CountArry[Hash_Bucket];


//初始化
bool initHashtable(Hash_Table*	hashtable)
{
	if (!hashtable)
	{
		return false;
	}

	hashtable->_HashSize = Hash_Bucket;//哈希桶
	
	if (!hashtable->Hash_Table)
	{
		return false;
	}
	
	//为每个哈希桶在第[0]位置添加一个记录当前桶中元素个数的计数器

	for (int i = 0; i < Hash_Bucket; i++)
	{
		HashMember Count;
		int* count = &(CountArry[i]);
		Count.data = count;
		Count.key = -(i + 1);
		hashtable->Hash_Table[i][0] = Count;
	}
	return true;
}

//计算要存储元素的哈希桶索引
int Hash(int key,int hash_bucket)
{
	return (key % Hash_Bucket);
}
//查找元素
bool findHashtable(Hash_Table* hashtable, int key)
{
	//先找到对应的哈希桶
	int index = Hash(key, Hash_Bucket);
	int count = *((int*)(hashtable->Hash_Table[index][0].data));//对应桶中的元素个数
	for (int i = 1; i < count + 1; i++)
	{
		if (hashtable->Hash_Table[index][i].key == key)
		{
			return true;
		}
	}
	return false;
}

//插入元素
void insertHashtable(Hash_Table* hashtable, int key, void* data)
{
	if (!hashtable)
	{
		return;
	}
 
	int index = Hash(key, hashtable->_HashSize);

	HashMember newHashMember;
	newHashMember.data = data;
	newHashMember.key = key;
	
	bool isExistence = findHashtable(hashtable,key);//先找一下,如果没有就往对应的哈希桶中塞。


	if (!isExistence)
	{
		//找到每个哈希桶的计数器,在当前计数器数量所指向的位置的下一个位置放入元素,然后自增计数器。
		hashtable->Hash_Table[index][*((int*)(hashtable->Hash_Table[index][0].data))+1] = newHashMember;
		(*((int*)(hashtable->Hash_Table[index][0].data)))++;
	}
	else
	{
		cout << "该key已经存在了" << endl;
	}
}

//删除元素
bool deleteHashtable(Hash_Table* hashtable,int key)
{
	if (!hashtable)
	{
		return false;
	}
	
	if (findHashtable(hashtable, key))//找到了才能删除
	{
		//找到了,去那拿对应的哈希桶
		int index = Hash(key, hashtable->_HashSize);
		//拿到桶中的元素个数	
		int count = *((int*)hashtable->Hash_Table[index][0].data);
		int i = 1;
		for (i; i < count + 1; i++)
		{
			if (hashtable->Hash_Table[index][i].key == key)
			{
				break;
			}
		}
		//此时的i的位置就是对应key的位置
		for (int j = i; j < count - 1; j++)
		{
			hashtable->Hash_Table[index][j] = hashtable->Hash_Table[index][j + 1];
		}
		//计数器--
		(*((int*)hashtable->Hash_Table[index][0].data))--;
		return true;
	}
	else
	{
		return false;
	}

}

//清理哈希表
bool cleanHashtable(Hash_Table* hashtable)
{
	if (!hashtable)
	{
		return false;
	}	
	//将每个哈希桶的计数器置为0 
	for (int i = 0; i < Hash_Bucket; i++)
	{
		*((int*)hashtable->Hash_Table[i][0].data) = 0;
	}

	return true;
}

int main(void)
{
	Hash_Table* hashtable = new Hash_Table;
	//初始化
	initHashtable(hashtable);
	char elem1[] = "李小花";
	char elem2[] = "赵铁柱";
	char elem3[] = "张全蛋";
	char elem4[] = "新二";
	char elem5[] = "小明";
	//插入
	insertHashtable(hashtable, 1,elem1);
	insertHashtable(hashtable, 2,elem2);
	insertHashtable(hashtable, 3,elem3);
	insertHashtable(hashtable, 4,elem4);
	insertHashtable(hashtable, 5,elem5);	
	//删除
	bool ret1 = deleteHashtable(hashtable, 1);
	//查找
	bool ret = findHashtable(hashtable, 1);
	if (ret)
	{
		cout << "存在" << endl;
	}
	else
	{
		cout << "不存在" << endl;
	}
	
	//清理
	cleanHashtable(hashtable);
	//销毁
	delete hashtable;
	hashtable = NULL;

	return 0;
}

实际应用

字串匹配

给定几个字符串,判断一个字符串从第2位到第4位的的字符是否在这几个字符串中。

重点在于,这个哈希表的key和对应的value是同一个。 key是由value转化过去的。

hash_table.h
代码语言:javascript
复制
#include<iostream>
using namespace std;

#define BUCKET_SIZE 1024
#define compare(a,b) strcmp((const char*)a,(const char*) b)

#define hash_func SDBMHash


typedef struct	_ListNode
{
	struct _ListNode* next;
	void* key;
	void* data;
}ListNode;

//提高可读性和可维护性
typedef ListNode* List;
typedef ListNode* Element;


typedef struct _HashTable
{
	int TableSize;//哈希桶的大小
	List* ThisList;
}HashTable;



//把字符串对应内容转化为整数类型的key(不改变原来内容)
unsigned int SDBMHash(void* key)
{
	unsigned int hash = 0;
	char* str = (char*)key;
	while (*str)
	{
		hash = (*str++) + (hash << 6) + (hash << 16) - hash;//让映射到的整数尽可能均匀,不出现重叠。

	}
	return (hash & 0x7FFFFFFF);
}


//根据key,计算索引,定位Hash桶的位置
int Hash(void* key, int TableSize)
{
	return hash_func(key) % TableSize;
}



//初始化哈希表
HashTable* initHash(int TableSize)
{

	HashTable* hTable = NULL;
	if (TableSize <= 0)
	{
		TableSize = BUCKET_SIZE;
	}
	hTable = new HashTable;
	hTable->TableSize = TableSize;
	hTable->ThisList = new List[TableSize];//哈希桶	
	if (!hTable->ThisList)
	{
		return NULL;
	}

	//为Hash桶的对应指针数组初始化链表结点	
	for (int i = 0; i < TableSize; i++)
	{
		hTable->ThisList[i] = new ListNode;
		if (!hTable->ThisList[i])
		{
			return NULL;
		}
		else
		{
			memset(hTable->ThisList[i], 0, sizeof(ListNode));
		}
	}

	return hTable;
}

//哈希表中查找元素
Element findHash(HashTable* hashtable, void* key)
{
	int i = 0;
	List L = NULL;
	Element e = NULL;

	i = Hash(key, hashtable->TableSize);//将这个字符串类型的key转化为哈希桶索引,找到该元素要放置的桶


	L = hashtable->ThisList[i];//对应的哈希桶

	e = L->next;

	while (e && compare(e->key, key) != 0)
	{
		e = e->next;
	}
	return e;
}

/*
	不要被类型限制了,本质上和key是int类型的哈希表是一样的。
*/


//哈希表插入元素
void insertHash(HashTable* hashtable, void* key, void* value)
{
	Element e = NULL, tmp = NULL;
	List L = NULL;
	e = findHash(hashtable, key);

	if (!e)
	{
		tmp = new ListNode;
		if (!tmp)
		{
			return;
		}
        //L为其对应位置的哈希桶
        //将新插入的结点与桶链接起来
		L = hashtable->ThisList[Hash(key, hashtable->TableSize)];//前插法 
		tmp->data = value;
		tmp->key = key;
		tmp->next = L->next;
		L->next = tmp;
	}
	else
	{
		cout << "这个键已经存在" << endl;
	}
}

//哈希表删除元素
void DeleteHash(HashTable* hashtable, void* key)
{
	Element e = NULL, last = NULL;
	List L = NULL;
	int i = Hash(key, hashtable->TableSize);//找到对应的哈希桶,然后在对应的哈希桶里面一个一个地遍历
	L = hashtable->ThisList[i];
	last = L;
	e = L->next;
	//key是所对应数据的数字代号,这个每个元素是不同的,i是对应数据所在的hash桶,也就是key % tablesize,多个元素可以是同一个
	while (e && !compare(e->key, key))
	{
		last = e;
		e = e->next;
	}
	if (e)
	{
		last->next = e->next;
		delete e;
	}
	else
	{
		cout << "该key不存在" << endl;
	}
}

//找到对应的ListNode提取元素
void* extract(Element e)
{
	return  e ? e->data : NULL;
}

//销毁哈希表
void destoryHash(HashTable* hashtable)
{
	List L = NULL;
	Element cur = NULL, next = NULL;
	for (int i = 0; i < hashtable->TableSize; i++)
	{
		L = hashtable->ThisList[i];
		cur = L->next;
		while (cur)
		{
			next = cur->next;
			delete cur;
			cur = next;
		}

		delete(L);
	}
	delete (hashtable->ThisList);
	delete (hashtable);
}
test.cpp
代码语言:javascript
复制
#include<iostream>
#include"hash_table.h"
using namespace std;

int main(void)
{
	char elems1[] = "ADBB";
	char elems2[] = "BDDC";
	char elems3[] = "CDBC";
	char elems4[] = "BDBB";

	const char* tester = "ABDBBAC";
	char cur[5] = { '\0' };

	int i = 0;


	HashTable* hashtable = NULL;
	hashtable = initHash(BUCKET_SIZE);

	insertHash(hashtable, elems1, elems1);
	insertHash(hashtable, elems2, elems2);
	insertHash(hashtable, elems3, elems2);
	insertHash(hashtable, elems4, elems4);


	//将要进行检查的字符串的2-4个字符拿过来进行对比
	strncpy_s(cur, tester + 1, 4);

	Element e = findHash(hashtable, cur);
	if (e)
	{
		cout << "找到了" << endl;
	}
	else
	{
		cout << "没找到" << endl;
	}
	destoryHash(hashtable);
	return 0;
}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021-10-18,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 哈希表
    • 概念
      • 链式存储实现
        • 顺序存储实现
          • 实际应用
            • 字串匹配
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档