一、哈希表概念
由于搜索二叉树中元素存储位置与元素各个关键码之间没有对应的关系,因此在查找一个元素时需要一个一个进行比较,搜索效率取决比较次数。如果构造一个存储结构使它存储位置与关键码能够一一对应,那么查找效率就会高效,即用一样的方法存储和查找关键码。哈希表有毕散列(开放定址法)和开散列(链地址法),本文主讲毕散列。
二、哈希冲突
- 首先毕散列容量是有限的,当满了时需要扩容,扩容时注意需要重新定址。
- α是负载因子,α=表中有效元素个数/散列表长,α应严格限制在0.7~0.8以下。
- 我们可以将元素值模表长,得到的值就是在哈希表存储位置的下标,但是如果该位置已存在元素,我们将在此位置往下探测直到找到可以存储的位置,查找时也通过此法查找。
例:依次插入9 11 23 19 54
三、源代码
1、hash.h
#ifndef _HASH__H_
#define _HASH__H_
#include "stdio.h"
#include "assert.h"
#include "malloc.h"
typedef int KeyType;
typedef int ValueType;
enum Status
{
EMPTY, //空
EXITS, //非空
DELETE, //删除
};
typedef struct HashNode
{
enum Status _status; //状态
KeyType _key; //值
ValueType _value; //冲突的次数
}HashNode;
typedef struct HashTable
{
HashNode *_tables;
int _size; //有效值
int _len; //表长
}HashTable;
void HashInit(HashTable* ht, int len);
int HashInsert(HashTable* ht, KeyType key, ValueType value);
HashNode* HashFind(HashTable* ht, KeyType key);
int HashRemove(HashTable* ht, KeyType key);
int HashDestory(HashTable* ht);
void HashPrint(HashTable* ht);
int HashSize(HashTable* ht);
int HashEmpty(HashTable* ht);
#endif
2、hash.c
#include "hash.h"
//初始化
void HashInit(HashTable* ht, int len)
{
assert(ht);
ht->_tables = (HashNode*)malloc(sizeof(HashNode)*len);
for (int i = 0; i < len; i++)
ht->_tables[i]._status = EMPTY;
ht->_size = 0;
ht->_len = len;
}
//插入
int HashInsert(HashTable* ht, KeyType key, ValueType value)
{
assert(ht);
int i;
if (((ht->_size * 10) / ht->_len) == 7)//扩容
{
HashTable hash;
HashInit(&hash, ht->_len * 2);
for (i = 0; i < ht->_len; i++)
{
if (ht->_tables[i]._status == EXITS)
HashInsert(&hash, ht->_tables[i]._key, 0);
}
HashInsert(&hash, key, value);
ht->_tables = hash._tables;
ht->_len = hash._len;
ht->_size = hash._size;
return 0;
}
else
{
i = key%ht->_len;
while (ht->_tables[i]._status == EXITS)//存在元素则向后探测
{
if (i == ht->_len)
i = 0;
else
{
ht->_tables[i]._value++;
i++;
}
}
ht->_tables[i]._key = key;
ht->_tables[i]._value = value;
ht->_size++;
ht->_tables[i]._status = EXITS;
return 0;
}
return -1;
}
//查找
HashNode* HashFind(HashTable* ht, KeyType key)
{
assert(ht);
int i = key%ht->_len;
while (ht->_tables[i]._status != EMPTY)
{
if (ht->_tables[i]._key == key)
{
if (ht->_tables[i]._status == DELETE)
return NULL;
else
return &ht->_tables[i];
}
else
{
if (i == ht->_len)
i = 0;
else
i++;
}
}
return NULL;
}
//删除
int HashRemove(HashTable* ht, KeyType key)
{
assert(ht);
HashNode* h = HashFind(ht, key);
if (h == NULL)
return -1;
h->_status = DELETE;
return 0;
}
//清除
int HashDestory(HashTable* ht)
{
assert(ht);
free(ht->_tables);
ht->_tables = NULL;
ht->_len = ht->_size = 0;
}
//打印
void HashPrint(HashTable* ht)
{
assert(ht);
for (int i = 0; i < ht->_len; i++)
{
if (ht->_tables[i]._status == EXITS)
printf("table[%d]:%d.%d\n", i, ht->_tables[i]._key, ht->_tables[i]._value);
else
printf("table[%d]:NULL.NULL\n",i);
}
}
//哈希表中有效元素个数
int HashSize(HashTable* ht)
{
return ht->_size;
}
//判断哈希表是否为空
int HashEmpty(HashTable* ht)
{
return ht->_size == 0 ? 0 : 1;
}