一、哈希桶:
1、通过哈希函数计算桶号
(1)哈希桶的最佳状态,就是每个桶下面挂一个元素(动态的可以增容:表格中的元素和总的个数相等):解决了一个哈希桶下面挂多个元素的情况
(2)素数表泄露,则别人知道了增容方式,受到攻击。继续增容,导致插入相同元素的概率增加,一条表格下挂的元素增多,使性能下降。解决方法:
在下面挂一个红黑树,效率达到lgN
2、每个桶下面挂一个链表,链表采用头插,效率高(尾插效率低)
二、哈希桶的实现
HashBucket.h
1、通过哈希函数计算桶号
(1)哈希桶的最佳状态,就是每个桶下面挂一个元素(动态的可以增容:表格中的元素和总的个数相等):解决了一个哈希桶下面挂多个元素的情况
(2)素数表泄露,则别人知道了增容方式,受到攻击。继续增容,导致插入相同元素的概率增加,一条表格下挂的元素增多,使性能下降。解决方法:
在下面挂一个红黑树,效率达到lgN
2、每个桶下面挂一个链表,链表采用头插,效率高(尾插效率低)
二、哈希桶的实现
HashBucket.h
# pragma once
# include<stdio.h>
# include<assert.h>
# include<stdlib.h>
# include<malloc.h>
# include"Common.h"
typedef int DataType;
typedef struct HashNode
{
struct HashNode *_pNext;
DataType _data;//哈希表中存放的数据
}HashNode;
typedef struct HashBucket//哈希桶
{
HashNode **_table;//二级指针,表示哈希表格,指向哈希桶的空间(桶中存放指向链表的指针)
int _capacity;//容量
int _size;//哈希表格中有效元素的个数
}HashBucket;
//初始化
void HashBucketInit(HashBucket *ht,int capacity);
//元素不可以重复的插入
void HashBucketInsertUnique(HashBucket *ht, DataType data);
//元素可以重复的插入
void HashBucketInsertEqual(HashBucket *ht, DataType data);
//删除不可以重复的元素
void HashBucketDeleteUnique(HashBucket *ht, DataType data);
//删除可以重复的元素
void HashBucketDeleteEqual(HashBucket *ht, DataType data);
//查找
HashNode* HashBucketFind(HashBucket *ht, DataType data);
//哈系桶中有多少元素
int HashBucketSize(HashBucket *ht);
//哈希桶是不是空的
int HashBucketEmpty(HashBucket *ht);
//销毁哈希桶
void HashBucketDestory(HashBucket *ht);
HashBucket.c

# include"HashBucket.h"
# include"Common.h"
//哈希函数
int HashFunc(HashBucket *ht, DataType data)
{
return data%ht->_capacity;
}
void HashBucketInit(HashBucket *ht, int capacity, int IsLineDetetive)
{
int i = 0;
assert(ht);
//获取下一个元素,防止下一个元素不为素数
capacity = GetNextPrime(capacity);
//开辟哈希桶的空间
ht->_table = (HashNode**)malloc(sizeof(HashNode*)*capacity);
if (NULL == ht->_table)//如果开辟空间失败,返回空
{
assert(0);
return;
}
for (; i < capacity; ++i)//初始化空间的每一个位置为空
ht->_table[i] = NULL;
ht->_capacity = capacity;
ht->_size = 0;
}
void HashBucketInsertUnique(HashBucket *ht, DataType data)
{
HashNode *pNewNode = NULL;
//计算元素的桶号
int bucketNo = HashFunc(ht, data);
//检测元素是否在当前桶中(元素须唯一),遍历一遍
HashNode *pCur = ht->_table[bucketNo];//从bucketNo位置开始遍历
while (pCur)
{
if (data == pCur->_data)//若果存在则返回
return;
pCur = pCur->_pNext;//不存在,则继续往下走
}
//创建新节点,在哈希桶下的链表中插入元素
pNewNode = BuyHashNode(data);
//头插法
pNewNode->_pNext = ht->_table[bucketNo];
ht->_table[bucketNo] = pNewNode;
ht->_size++;
}
HashNode *BuyHashNode(DataType data)
{
HashNode *pNewNode = (HashNode*)malloc(sizeof(HashNode));
if (NULL == pNewNode)//新空间申请失败
{
assert(0);
return NULL;
}
//节点创建成功
pNewNode->_data = data;
pNewNode->_pNext = NULL;
return pNewNode;
}


void HashBucketDeleteUnique(HashBucket *ht, DataType data)
{
//计算元素的桶号
int bucketNo = HashFunc(ht, data);
//检测元素是否在当前桶中
HashNode *pPre = NULL;
HashNode *pCur = ht->_table[bucketNo];//找到了桶的位置
while (pCur)
{
if (data == pCur->_data)//等于此位置
{
//删除链表中的第一个节点
if (pCur == ht->_table[bucketNo])//cur是链表中的第一个节点,
{
ht->_table[bucketNo] = pCur->_pNext;//哈希桶里直接放cur的下一个节点
}
//不是链表中的第一个节点
else
{
pPre->_pNext = pCur->_pNext;//前一个节点指向当前节点的下一个节点
}
free(pCur);
ht->_size--;
return;
}
pPre = pCur;//如果不是pPre记录下pCur
pCur = pCur->_pNext;//cur继续向后走
}
}
void HashBucketInsertEqual(HashBucket *ht, DataType data)
{
int bucketNo = HashFunc(ht, data);//计算桶号,通过哈希函数计算
//可以重复,不用检测链表中是否已经存在此元素,直接向链表中插入此元素即可
HashNode *pNewNode = BuyHashNode(data);//给定新节点
//头插
pNewNode->_pNext = ht->_table[bucketNo];//新节点连接原节点,原节点为桶位置
ht->_table[bucketNo] = pNewNode;//此时桶的位置为新节点的位置
ht->_size++;
}

void HashBucketDeleteEqual(HashBucket *ht, DataType data)
{
//在当前哈系桶中查找桶位置
int bucketNo = HashFunc(ht, data);
HashNode *pPre = NULL;
HashNode *pCur = ht->_table[bucketNo];
while (pCur)
{
if (data == pCur->_data)
{
if (pCur == ht->_table[bucketNo])//当前要删除元素元素是桶位置
{
ht->_table[bucketNo] = pCur->_pNext;
free(pCur);
pCur = ht->_table[bucketNo];//继续朝后走,后面有可能还有要删除位置
}
else//是要删除的节点,且在链表处,不在桶位置
{
pPre->_pNext = pCur->_pNext;
free(pCur);
pCur = pPre->_pNext;
}
}
else//不相等
{
pPre = pCur;
pCur = pCur->_pNext;
}
}
}
int HashBucketSize(HashBucket *ht)
{
return ht->_size;
}
int HashBucketEmpty(HashBucket *ht)
{
return 0 == ht->_size;
}
void HashBucketDestory(HashBucket *ht)
{
int i = 0;
for (; i < ht->_capacity; ++i)//i表示要删除的桶
{
HashNode *pCur = ht->_table[i];
while (pCur)//只要桶里面有节点
{
//删除链表中第一个节点的位置
ht->_table[i] = pCur->_pNext;
free(pCur);
pCur = ht->_table[i];//把当前节点放到链表中第一个节点的位置
}
}
free(ht->_table);
ht->_capacity = 0;
ht->_size = 0;
}
common.c
//闭散列用的少,因为浪费空间
# define _CRT_SECURE_NO_WARNINGS 1
# include"Common.h"
#define _PrimeSize 28//enum{_PrimeSize =28};
// 使用素数表对齐做哈希表的容量,降低哈希冲突
const unsigned long _PrimeList[_PrimeSize]=
{//可将long换为long long获取更大的素数
53ul, 97ul, 193ul, 389ul, 769ul,
1543ul, 3079ul, 6151ul, 12289ul, 24593ul,
49157ul, 98317ul, 196613ul, 393241ul, 786433ul,
1572869ul, 3145739ul, 6291469ul, 12582917ul, 25165843ul,
50331653ul, 100663319ul, 201326611ul, 402653189ul, 805306457u,
1610612711ul, 3221225473ul, 4294967291ul
};
//获取比容量大的第一个素数
size_t GetNextPrime(size_t capacity)
{
int i = 0;
for (; i < _PrimeSize; ++i)
{
if (_PrimeList[i]>capacity)//容量小于素数,直接返回该素数
return _PrimeList[i];
}
return _PrimeList[_PrimeSize - 1];//容量太大了,返回最后一个素数
}
unsigned int StrToInt(const char * str)
{
unsigned int seed = 131; // 31 131 1313 13131 131313
unsigned int hash = 0;
while (*str)
{
hash = hash * seed + (*str++);
}
return (hash & 0x7FFFFFFF);
}