哈希之闭散列

给出哈希之闭散列的定义:

#define HashMaxSize 1000

typedef enum Stat
{
	Empty,
	Valid,
	Invalid //当前元素被删除了
}Stat;

typedef int KeyType;
typedef int ValType;

typedef size_t(*HashFunc)(KeyType key);

typedef struct HashElem
{
	KeyType key;
	ValType value;
	Stat stat; // 引入一个 stat 标记来作为是否有效的标记
}HashElem;

typedef struct HashTable
{
	HashElem data[HashMaxSize];
	size_t size;
	HashFunc hash_func;
}HashTable;

将函数的声明放在head.h的头文件里面:

// 现在使用闭散列的方式实现hash表(通用的hash表)
//
// 下面的问题是我们要解决的问题之一
// 统计一个数组中每一个元素出现的个数
// 这个时候就可以用 value 来表示次数了  
// 给定一个数组 {1,1,1,2,2,3}
// 遍历数组
// 1)先将hash表中查找(得到了当前元素的次数)
// 2)如果元素在hash表中存在,将这个次数(value)+ 1
// 3) 如果元素在hash表中不存在,插入一个新的元素,初始value为1
#pragma once

#include <stddef.h>
#include <windows.h>
#include <stdio.h>

#define HashMaxSize 1000

typedef enum Stat
{
	Empty,
	Valid,
	Invalid //当前元素被删除了
}Stat;

typedef int KeyType;
typedef int ValType;

typedef size_t(*HashFunc)(KeyType key);

typedef struct HashElem
{
	KeyType key;
	ValType value;
	Stat stat; // 引入一个 stat 标记来作为是否有效的标记
}HashElem;

typedef struct HashTable
{
	HashElem data[HashMaxSize];
	size_t size;
	HashFunc hash_func;
}HashTable;

/

// 哈希函数
size_t HashFuncDefault(KeyType key);

// 初始化
void HashInit(HashTable* ht, HashFunc hash_func);

// 插入
int HashInsert(HashTable* ht, KeyType key, ValType value);

// 输入key,查找对应key的value 
int HashFind(HashTable* ht, KeyType key, ValType* value);

 // 删除
void HashRemove(HashTable* ht, KeyType key);

// 判空
int HashEmpty(HashTable* ht);

// 大小
size_t HashSize(HashTable* ht);

// 销毁
void HashDestroy(HashTable* ht);

// 打印
void HashPrintInt(HashTable* ht, const char* msg);

函数的定义(具体实现):

#include "Hash.h"

// 默认的取哈希值的方式
// 比较简单,但是冲突的可能性也比较大
size_t HashFuncDefault(KeyType key)
{
	return key % HashMaxSize;
}

void HashInit(HashTable* ht, HashFunc hash_func)
{
	if (ht == NULL)
	{
		// 非法输入
		return;
	}
	ht->hash_func = hash_func;
	ht->size = 0;
	size_t i = 0;
	for (; i < HashMaxSize; ++i)
	{
		ht->data[i].stat = Empty;
	}
	return;
}

void HashPrintInt(HashTable* ht, const char* msg)
{
	printf("[%s]\n", msg);
	size_t i = 0;
	for (; i < HashMaxSize; ++i)
	{
		if (ht->data[i].stat != Empty)
		{
			printf("[%lu] key = %d, value = %d stat = %d\n", i, ht->data[i].key, ht->data[i].value, ht->data[i].stat);
		}
	}
}

int HashInsert(HashTable* ht, KeyType key, ValType value)
{
	if (ht == NULL)
	{
		// 非法输入
		return 0;
	}

	if (ht->size >= HashMaxSize * 0.8) // 设定负载因子为 0.8
	{
		// 这里认为hash表已经满了,不能继续插入
		return 0;
	}

	// 先找到 key 对应的下标
	size_t offset = ht->hash_func(key);
	while (1)
	{
		if (ht->data[offset].stat == Valid)
		{
			if (ht->data[offset].key == key)
			{
				// 发现在hash表中已经存在了相同的元素
				// 策略1:认为插入失败
				// 策略2:更新对应的value
				// ht->data[offset].value = value;
				return 0;
			}

			// 出现了hash冲突,使用线性探测的方式查找下一个元素
			++offset;
			if (offset >= HashMaxSize) // 判断越界和处理
			{
				offset -= HashMaxSize;
			}
		}
		else
		{
			// 由于我们的负载因子是 80%,一定能找到一个空位
			ht->data[offset].key = key;
			ht->data[offset].value = value;
			ht->data[offset].stat = Valid;
			++ht->size;
			return 1;
		}
	}
	return 0;
}

int HashFind(HashTable* ht, KeyType key, ValType* value)
{
	if (ht == NULL || value == NULL)
	{
		// 非法输入
		return 0;
	}

	// 1. 通过hash函数计算数组下标
	size_t offset = ht->hash_func(key);

	// 2. 从这个数组下标开始,线性的向后查找
	while (1)
	{
		//  a) 如果找到了某个元素的 key 是想要的值,并且状态为 Valid,就认为找到了,就将 value 赋值给输出参数
		if (ht->data[offset].key == key && ht->data[offset].stat == Valid)
		{
			// 找到了想要找的元素
			*value = ht->data[offset].value;
			return 1;
		}

		//  b) 如果发现当前位置的状态是 Empty,就认为没有找到
		else if (ht->data[offset].stat == Empty)
		{
			return 0;
		}

		//  c)继续尝试查找下一个位置
		else
		{
			offset++;
			if (offset >= HashMaxSize) // 判断越界和处理
			{
				offset -= HashMaxSize;
			}
		}
	}
	return 0;
}

void HashRemove(HashTable* ht, KeyType key)
{
	if (ht == NULL)
	{
		return;
	}

	// 1. 根据 key 找到对应的数组下标
	size_t offset = ht->hash_func(key);

	// 2. 从找到的下标开始依次遍历
	while (1)
	{
		// 6种情况:
		// 1. key 相同:
		//  a) 状态为 Valid
		//  b) 状态为 Empty
		//  c) 状态为 Invalid
		// 2. key 不相同:
		//  a) 状态为 Valid
		//  b) 状态为 Empty
		//  c) 状态为 Invalid

		//  a) 如果当前元素的 key 为要删除的 key,并且状态为 Valid,就将状态置为 Invalid,并且要 -- size
		if (ht->data[offset].key == key && ht->data[offset].stat == Valid)
		{
			// 找到了想要找的元素
			ht->data[offset].stat = Invalid;
			--ht->size;
			return;
		}
	
		//  b) 如果当前元素的状态为 Empty,要删除的 key 没有找到,不需要进行任何删除了
		else if (ht->data[offset].stat == Empty)
		{
			return;
		}

		//  c) 剩下的情况,就依次的取下一个元素就行了
		else
		{
			offset++;
			if (offset >= HashMaxSize) // 判断越界和处理
			{
				offset -= HashMaxSize;
			}
		}
	}
	return;
}

int HashEmpty(HashTable* ht)
{
	if (ht == NULL)
	{
		return 1;
	}
	return ht->size == 0 ? 1 : 0;
}

size_t HashSize(HashTable* ht)
{
	if (ht == NULL)
	{
		return 0;
	}
	return ht->size;
}

void HashDestroy(HashTable* ht) // 清理掉所有状态
{
	size_t i = 0;
	for (; i < HashMaxSize; ++i)
	{
		ht->data[i].stat = Empty;
	}
	ht->size = 0;
	ht->hash_func = NULL;
	return;
}

测试代码:

#include "Hash.h"

void TestInit()
{
	HashTable ht;
	HashInit(&ht, HashFuncDefault);
	printf("ht->size expect 0, actual %lu\n", ht.size);
	printf("ht->hash_func expect %p, actual %p\n", HashFuncDefault, ht.hash_func);
	size_t i = 0;
	for (; i < HashMaxSize; ++i)
	{
		if (ht.data[i].stat != Empty)
		{
			printf("pos [%lu] elem error!\n", i);
		}
	}
}

void TestInsert()
{
	HashTable ht;
	HashInit(&ht, HashFuncDefault);

	HashInsert(&ht, 1, 100);
	HashInsert(&ht, 2, 200);
	HashInsert(&ht, 1001, 300);
	HashInsert(&ht, 1002, 400);

	HashPrintInt(&ht, "插入四个元素");
}

void TestFind()
{
	HashTable ht;
	HashInit(&ht, HashFuncDefault);

	HashInsert(&ht, 1, 100);
	HashInsert(&ht, 2, 200);
	HashInsert(&ht, 1001, 300);
	HashInsert(&ht, 1002, 400);

	int value = 0;
	int ret = HashFind(&ht, 1, &value);
	printf("ret expect 1, actual %d\n", ret);
	printf("vaule expect 100, actual %d\n", value);

	ret = HashFind(&ht, 1001, &value);
	printf("ret expect 1, actual %d\n", ret);
	printf("value expect 300, actual %d\n", value);

	ret = HashFind(&ht, 1005, &value);
	printf("ret expect 0, actual %d\n", ret);
}

void TestRemove()
{
	HashTable ht;
	HashInit(&ht, HashFuncDefault);

	HashInsert(&ht, 1, 100);
	HashInsert(&ht, 2, 200);
	HashInsert(&ht, 1001, 300);
	HashInsert(&ht, 1002, 400);

	HashRemove(&ht, 1);
	HashPrintInt(&ht, "删除 key 为 1 的元素:");

	int value = 0;
	int ret = HashFind(&ht, 1, &value);
	printf("ret expect 0, actual %d\n", ret);

	ret = HashFind(&ht, 1001, &value);
	printf("ret expect 1, actual %d\n", ret);
	printf("value expect 300, actual %d\n", value);
}

// 哈希桶(统计元素个数)
void CountNum()
{
	int array[] = { 1, 1, 1, 2, 2, 3 };
	HashTable ht;
	HashInit(&ht, HashFuncDefault);
	size_t i = 0;
	for (; i < sizeof(array) / sizeof(array[0]); ++i)
	{
		int value = 0;
		int ret = HashFind(&ht, array[i], &value);
		if (ret == 0)
		{
			// 没有找到
			HashInsert(&ht, array[i], 1);
		}
		else
		{
			// 该元素已经存在
			HashRemove(&ht, array[i]);
			HashInsert(&ht, array[i], value + 1);
		}
	}
	HashPrintInt(&ht, "统计的最终结果为:");
}

 c++ 中的hash,做了这样的事情
 重载了 [] 运算符

 如果1已经存在,就取出对应的value
 如果1已经不存在,插入一个默认的值
 int value = ht[1];  
 ht[1] = value;
//#include <unordered_map>
//void CountNum_CPP()
//{
//	int array[] = { 1, 1, 1, 2, 2, 3 };
//	std::unordered_map<int, int>ht;
//	size_t i = 0;
//	for (; i < sizeof(array) / sizeof(array[0]); ++i)
//	{
//		++ht[i];
//	}
//	// 遍历哈希表,打印结果就可以了
//	for (auto item : ht)
//	{
//		printf("%d,%d\n", item.fist, item.second);
//	}
//}

int main()
{
	TestInit();
	TestInsert();
	TestFind();
	TestRemove();
	CountNum();
	//CountNum_CPP();
	system("pause");
	return 0;
}

结果如下:


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值