搜索结构之哈希-----开散列
开散列法又叫链地址法(开链法)。
开散列法:首先对关键码集合用散列函数计算散列地址,具有相同地址的关键码归于同一子集合,每一个子集合称为一个桶,各个桶中的元素通过一个单链表链接起来,各链表的头结点存储在哈希表中。
设元素的关键码为37, 25, 14, 36, 49, 68, 57, 11, 散列表为HT[12],表的大小为12,散列函数为Hash(x) = x % 11
Hash(37)=4
Hash(25)=3
Hash(14)=3
Hash(36)=3
Hash(49)=5
Hash(68)=2
Hash(57)=2
Hash(11)=0
使用哈希函数计算出每个元素所在的桶号,同一个桶的链表中存放哈希冲突的元素。
通常,每个桶对应的链表结点都很少,将n个关键码通过某一个散列函数,存放到散列表中的m个桶中,那么每一个桶中链表的平均长度为n/m。以搜索平均长度为的链表代替了搜索长度为 n 的顺序表,搜索效率快的多。
应用链地址法处理溢出,需要增设链接指针,似乎增加了存储开销。事实上:
由于开地址法必须保持大量的空闲空间以确保搜索效率,如二次探查法要求装载因子a <= 0.7,而表项所占空间又比指针大的多,所以使用链地址法反而比开地址法节省存储空间。
哈希表(开散列)
Hash_Bucket.h
typedef int DataType;
typedef struct Hash_Node{
struct Hash_Node* _pNext;
DataType _data;
}Hash_Node,*pHash_Node;
typedef int (*pElemToInt)(DataType data);
typedef struct Hash_Bucket{
pHash_Node* _HashTable;
int _size;
int _capacity;
pElemToInt _pElemToInt;
}Hash_Bucket;
int ElemToint(int data);
int Strtoint(const char * str);
int GetNextPrimeNum(int Old_Capacity);
//初始化
void HashBucketInit(Hash_Bucket *hb, pElemToInt ElemToInt);
//扩容
void HashBucketAddCapacity(Hash_Bucket *hb);
//插入(元素不重复)
void HashBucketInsertUnique(Hash_Bucket*hb, DataType data);
//插入(元素可以重复)
void HashBucketInsertEqual(Hash_Bucket*hb, DataType data);
//删除
void HashBucketDeleteUnique(Hash_Bucket*hb, DataType data);
//删除所有指定元素
void HashBucketDeleteEquel(Hash_Bucket*hb, DataType data);
//找到指定元素
Hash_Node* HashBucketFind(Hash_Bucket*hb, DataType data);
//桶的大小
int HashBucketSize(Hash_Bucket*hb);
//桶是否为空
int HashBucketEmpty(Hash_Bucket*hb);
//销毁哈希桶
void HashBucketDestory(Hash_Bucket*hb);
//打印哈希桶
void HashBucketPrint(Hash_Bucket*hb);
Hash_Bucket.c
#include "HashBucket.h"
#include<malloc.h>
#include<stdlib.h>
#include<stdio.h>
#include<assert.h>
int HashFun(Hash_Bucket *hb,DataType data){
assert(hb);
return (hb->_pElemToInt)(data) % (hb->_capacity);
}
//哈希字符串转换函数
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);
}
//默认处理方式
int ElemToint(int data){
return data;
}
//素数表
int GetNextPrimeNum(int Old_Capacity){
int i = 0;
const int _PrimeSize = 28;
static const unsigned long _PrimeList[28] =
{
53ul, 97ul, 193ul, 389ul, 769ul,
1543ul, 3079ul, 6151ul, 12289ul, 24593ul,
49157ul, 98317ul, 196613ul, 393241ul, 786433ul,
1572869ul, 3145739ul, 6291469ul, 12582917ul, 25165843ul,
50331653ul, 100663319ul, 201326611ul, 402653189ul, 805306457ul,
1610612741ul, 3221225473ul, 4294967291ul
};
for (i = 0; i < _PrimeSize; i++)
{
if (_PrimeList[i]>Old_Capacity)
return _PrimeList[i];
}
return -1;
}
//扩容
void HashBucketAddCapacity(Hash_Bucket *hb){
pHash_Node pcur = NULL;
int i = 0;
assert(hb);
int new_capacity = GetNextPrimeNum(hb->_capacity);
pHash_Node * _newTable = (pHash_Node *)malloc(sizeof(pHash_Node)*new_capacity);
if (_newTable==NULL)
{
assert(0);
return;
}
for ( i = 0; i < new_capacity; i++)
{
_newTable[i]->_pNext = NULL;
}
for (i=0; i < hb->_capacity;i++){
pcur = hb->_HashTable[i];
int new_bucketNober = -1;
while (pcur)
{
hb->_HashTable[i] = pcur->_pNext;
//插入pcur节点到_newTable中
new_bucketNober = hb->_pElemToInt(pcur->_data) % new_capacity;
pcur->_pNext = _newTable[new_bucketNober];
_newTable[new_bucketNober] = pcur;
pcur = hb->_HashTable[i];
}
}
free(hb->_HashTable);
hb->_HashTable = _newTable;
hb->_capacity = new_capacity;
}
//初始化
void HashBucketInit(Hash_Bucket *hb, pElemToInt ElemToInt){
int i = 0;
assert(hb);
hb->_capacity = 3;
hb->_HashTable = (pHash_Node *)malloc(sizeof(pHash_Node)*(hb->_capacity));
if (hb->_HashTable==NULL)
{
assert(0);
return;
}
for ( i = 0; i < hb->_capacity; i++)
{
hb->_HashTable[i] = NULL;
}
hb->_size = 0;
hb->_pElemToInt = ElemToInt;
}
Hash_Node* Buy_Hash_Node(DataType data){
Hash_Node *ret = (Hash_Node *)malloc(sizeof(Hash_Node));
if (ret==NULL)
{
assert(0);
return;
}
ret->_data = data;
ret->_pNext = NULL;
return ret;
}
//插入(元素不重复)
void HashBucketInsertUnique(Hash_Bucket*hb, DataType data){
Hash_Node*pcur = NULL;
Hash_Node*NewNode = NULL;
assert(hb);
if (hb->_capacity==hb->_size)
{
HashBucketAddCapacity(hb);
}
int Hash_Addr = HashFun(hb, data);
pcur = hb->_HashTable[Hash_Addr];
while (pcur)
{
if (pcur->_data==data)
return;
else
pcur = pcur->_pNext;
}
NewNode = Buy_Hash_Node(data);
NewNode->_pNext = hb->_HashTable[Hash_Addr];
hb->_HashTable[Hash_Addr] = NewNode;
}
//插入(元素可以重复)
void HashBucketInsertEqual(Hash_Bucket*hb, DataType data){
Hash_Node*NewNode = NULL;
assert(hb);
int Hash_Addr = HashFun(hb, data);
NewNode = Buy_Hash_Node(data);
NewNode->_pNext = hb->_HashTable[Hash_Addr];
hb->_HashTable[Hash_Addr] = NewNode;
}
//删除(元素不重复)
void HashBucketDeleteUnique(Hash_Bucket*hb, DataType data){
Hash_Node*pcur = NULL;
Hash_Node*pre = NULL;
assert(hb);
int Hash_Addr = HashFun(hb, data);
pcur = hb->_HashTable[Hash_Addr];
while (pcur)
{
if (pcur->_data == data)
{
if (pcur == hb->_HashTable[Hash_Addr])
hb->_HashTable[Hash_Addr] = pcur->_pNext;
else
pre->_pNext = pcur->_pNext;
free(pcur);
hb->_size--;
return;
}
else
{
pre = pcur;
pcur = pcur->_pNext;
}
}
}
//删除(元素有可能重复)
void HashBucketDeleteEquel(Hash_Bucket*hb, DataType data){
Hash_Node*pcur = NULL;
Hash_Node*pre = NULL;
assert(hb);
int Hash_Addr = HashFun(hb, data);
pcur = hb->_HashTable[Hash_Addr];
while (pcur)
{
if (pcur->_data == data)
{
if (pcur == hb->_HashTable[Hash_Addr])
{
hb->_HashTable[Hash_Addr] = pcur->_pNext;
free(pcur);
pcur = hb->_HashTable[Hash_Addr];
}
else
{
pre->_pNext = pcur->_pNext;
free(pcur);
pcur = pre->_pNext;
}
hb->_size--;
}
else
{
pre = pcur;
pcur = pcur->_pNext;
}
}
}
//找到指定元素
Hash_Node* HashBucketFind(Hash_Bucket*hb, DataType data){
Hash_Node*pcur = NULL;
assert(hb);
int Hash_Addr = HashFun(hb, data);
pcur = hb->_HashTable[Hash_Addr];
while (pcur)
{
if (pcur->_data == data)
return pcur;
else
pcur = pcur->_pNext;
}
return NULL;
}
//桶的大小
int HashBucketSize(Hash_Bucket*hb){
return hb->_size;
}
//桶是否为空
int HashBucketEmpty(Hash_Bucket*hb){
return 0 == hb->_size;
}
//销毁哈希桶
void HashBucketDestory(Hash_Bucket*hb){
int i = 0;
assert(hb);
for ( i = 0; i < hb->_capacity; i++)
{
free(hb->_HashTable[i]);
}
free(hb->_HashTable);
hb->_capacity = 0;
hb->_size = 0;
}
//打印哈希桶
void HashBucketPrint(Hash_Bucket*hb){
Hash_Node *pcur = NULL;
int i = 0;
assert(hb);
for (i = 0; i < hb->_capacity; i++)
{
pcur = hb->_HashTable[i];
while (pcur)
{
printf("%d---", pcur->_data);
pcur = pcur->_pNext;
}
printf("\n");
}
}
test.c
#include "HashBucket.h"
#include<stdio.h>
#include<stdlib.h>
int main(){
Hash_Node* ret;
Hash_Bucket hb;
HashBucketInit(&hb, ElemToint);
HashBucketInsertUnique(&hb,37);
HashBucketInsertUnique(&hb,25);
HashBucketInsertUnique(&hb,14);
HashBucketInsertUnique(&hb,36);
HashBucketInsertUnique(&hb,49);
HashBucketInsertUnique(&hb,68);
HashBucketInsertUnique(&hb,57);
HashBucketInsertUnique(&hb,11);
HashBucketInsertEqual(&hb, 14);
HashBucketInsertEqual(&hb, 68);
//HashBucketPrint(&hb);
//HashBucketDeleteEquel(&hb, 14);
printf("//");
HashBucketPrint(&hb);
ret = HashBucketFind(&hb, 11);
printf("%d\n",ret->_data);
HashBucketDeleteUnique(&hb, 49);
HashBucketPrint(&hb);
system("pause");
return 0;
}
总结开散列: 开散列最好的情况是:每个哈希地址存放一个元素
开散列最坏的情况是:所有的元素都同一个哈希地址中的链表上,如此下去,随着容量越来越大,就会导致链表的长度很长,而链表的查找时间复杂度为o(N),就会失去哈希原本的效率,所以我们可以将链表改为红黑树,时间复杂度为lg(N).