搜索结构之哈希-----开散列
开散列法又叫链地址法(开链法)。
开散列法:首先对关键码集合用散列函数计算散列地址,具有相同地址的关键码归于同一子集合,每一个子集合称为一个桶,各个桶中的元素通过一个单链表链接起来,各链表的头结点存储在哈希表中。
设元素的关键码为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).