> 开散列法又叫链地址法(开链法)。
开散列法:首先对关键码集合用散列函数计算散列地址,具有相同地址的关键码归于同一子集合,每一个子集合称为一个桶,各个
桶中的元素通过一个单链表链接起来,各链表的头结点存储在哈希表中。
设元素的关键码为37, 25, 14, 36, 49, 68, 57, 11, 散列表为HT[12],表的大小为12,散列函数为Hash(x) = x % 11
Hash(37)=4
Hash(25)=3
Hash(36)=3
Hash(49)=5
Hash(68)=2
Hash(11)=0
使用哈希函数计算出每个元素所在的桶号,同一个桶的链表中存放哈希冲突的元素。
通常,每个桶对应的链表结点都很少,将n个关键码通过某一个散列函数,存放到散列表中的m个桶中,那么每一个桶中链表的平均 长度为 。以搜索平均长度为 的链表代替了搜索长度为 n 的顺序表,搜索效率快的多。
应用链地址法处理溢出,需要增设链接指针,似乎增加了存储开销。事实上: 由于开地址法必须保持大量的空闲空间以确保搜索效率,如二次探查法要求装载因子a <= 0.7,而表项所占空间又比指针大的多, 所以使用链地址法反而比开地址法节省存储空间。
开散列的实现(拉链法)
HashNode.h
#pragma once
#include <stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef int KeyType;
typedef int valueType;
typedef struct HashNode
{
KeyType _key;
valueType _value;
struct HashNode*_next;
}HashNode;
typedef struct HashTable
{
HashNode** table;
size_t _size;
size_t _N;
}HashTable;
HashNode*BuyHashNode(KeyType key, valueType value);
size_t GetHashTablePrime(size_t N);
void HashTableInit(HashTable*ht,int size);//初始化
void HashTablePrint(HashTable*ht);
int HashTableInsert(HashTable*ht, KeyType key, valueTypevalue);//
HashNode* HashTableFind(HashTable*ht, KeyType key);//查找
size_t HashTableErase(HashTable*ht, KeyType key);//删除
void HashTableDesTroy(HashTable*ht);//销毁
HashNode.c
#include"HashNode.h"
HashNode* BuyHashNode(KeyType key, valueType value)
{
HashNode*Node = (HashNode*)malloc(sizeof(HashNode));
assert(Node);
Node->_key = key;
Node->_value = value;
Node->_next = NULL;
return Node;
}
void HashTableInit(HashTable*ht,int size)//初始化
{
assert(ht);
ht->_size = 0;
ht->_N = size;
//这里要注意开辟空间是给指针数组
ht->table = (HashTable**)malloc(sizeof(HashTable*)*ht->_N);
assert(ht->table);
memset(ht->table, 0, sizeof(HashTable*)*ht->_N);
}
size_t GetHashTablePrime(size_t N)
{
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 (int i = 0; i < 28; i++)
{
if (N < _PrimeList[i])
{
N = _PrimeList[i];
return N;
}
}
return _PrimeList[27];
}
size_t HashFunc(KeyType key, size_t N)
{
return key%N;
}
int HashTableInsert(HashTable*ht, KeyType key, valueType value)//插入
{
assert(ht);
HashNode* node = BuyHashNode(key, value);
if (ht->_size * 10 / ht->_N > 7)
{
//扩容
size_t newN = GetHashTablePrime(ht->_N);
HashTable newht;
HashTableInit(&newht, newN);
for (size_t i = 0; i < ht->_N; i++)
{
HashNode* cur = ht->table[i];//得到链表表头节点的地址
if (ht->table[i])
{
size_t index = HashFunc(cur->_key, newht._N);//重新计算原表中的值在新表中的位置
while (cur)
{
cur->_next = newht.table[index];//将原表头节点的下一个指向新表头结点
newht.table[index] = cur;//原表头结点赋给新表头结点实现原表值映射到新表中
cur = cur->_next;
}
}
}
free(ht->table);
ht->_N = newN;//将新表中的扩容给旧表
ht->table = newht.table;//新表赋给原表
}
size_t index = HashFunc(key, ht->_N);
if (ht->table[index])
{
HashNode*cur = ht->table[index];
while (cur)
{
if (cur->_key == key)
{
return -1;
}
cur = cur->_next;
}
}
node->_next = ht->table[index];
ht->table[index] = node;
ht->_size++;
return 0;
}
void HashTablePrint(HashTable*ht)
{
for (size_t i = 0; i < ht->_N; i++)
{
if (ht->table[i])
{
HashNode*cur = ht->table[i];
while (cur)
{
printf("[%d]->%d\n ",i, cur->_key);
cur = cur->_next;
}
}
}
}
HashNode *HashTableFind(HashTable*ht, KeyType key)
{
assert(ht);
size_t index = HashFunc(key, ht->_N);
if (ht->table[index])
{
if (ht->table[index]->_key=key)
{
return ht->table[index];
}
else
{
HashNode*cur = ht->table[index];
while (cur)
{
if (cur->_key==key)
{
return cur;
}
cur = cur->_next;
}
return NULL;
}
}
}
size_t HashTableErase(HashTable*ht, KeyType key)
{
assert(ht);
size_t index = HashFunc(key, ht->_N);
if (ht->table[index])
{
HashNode* cur = ht->table[index];
HashNode* prev = ht->table[index];
while (cur)
{
if (cur == prev&&cur->_key == key)//只有一个节点
{
ht->table[index] = cur->_next;
free(cur);
cur = NULL;
ht->_size--;
return 0;
}
else if (cur->_key == key)//多个节点
{
prev = cur->_next;
free(cur);
cur = NULL;
ht->_size--;
return 0;
}
prev = cur;
cur = cur->_next;
}
return -1;
}
else
{
return -1;
}
}
void HashTableDesTroy(HashTable*ht)
{
assert(ht);
for (size_t i = 0; i < ht->_N; i++)
{
if (ht->table[i])
{
HashNode*cur = ht->table[i];
while (cur)
{
HashNode*tmp = cur;
cur = cur->_next;
free(tmp);
tmp = NULL;
}
}
}
free(ht->table);
ht->table = NULL;
ht->_N = 0;
ht->_size = 0;
}
test.c
#include"HashNode.h"
void test()
{
HashTable ht;
HashTableInit(&ht,5);
HashTableInsert(&ht, 8,0);
HashTableInsert(&ht, 11, 0);
HashTableInsert(&ht, 10, 0);
HashTableInsert(&ht, 12, 0);
HashTableInsert(&ht, 2, 0);
HashTableInsert(&ht, 5, 0);
HashTableInsert(&ht, 20, 0);
HashTablePrint(&ht);
printf("\n");
printf("%d\n ", HashTableFind(&ht, 10)->_key);
printf("%d\n ", HashTableFind(&ht, 12)->_key);
printf("%d\n ",HashTableFind(&ht, 20)->_key);
printf("%d\n", HashTableFind(&ht, 8)->_key);
printf("%p\n ", HashTableFind(&ht, 1)->_key);
printf("\n");
HashTableErase(&ht, 8);
HashTableErase(&ht, 2);
HashTableErase(&ht, 5);
HashTableErase(&ht, 20);
HashTablePrint(&ht);
printf("\n");
HashTableDesTroy(&ht);
HashTablePrint(&ht);
}
int main()
{
test();
system("pause");
return 0;
}