散列表定义
分离链表法
开放定址法
分离链接法实现
通过头文件 fatal.h
#include <stdio.h>
#include <stdlib.h>
#define Error( Str ) FatalError( Str )
#define FatalError( Str ) fprintf( stderr, "%s\n", Str ), exit( 1 )
声明 hashsep.h
/* Interface for separate chaining hash table */
typedef int ElementType;
/* START: fig5_2.txt */
typedef unsigned int Index;
/* END */
/* START: fig5_7.txt */
#ifndef _HashSep_H
#define _HashSep_H
struct ListNode;
typedef struct ListNode *Position;
struct HashTbl;
typedef struct HashTbl *HashTable;
HashTable InitializeTable(int TableSize);
void DestroyTable(HashTable H);
Position Find(ElementType Key, HashTable H);
void Insert(ElementType Key, HashTable H);
ElementType Retrieve(Position P);
/* Routines such as Delete are MakeEmpty are omitted */
#endif /* _HashSep_H */
/* END */
实现 hashsep.c
#include "fatal.h"
#include "hashsep.h"
#include <stdlib.h>
#define MinTableSize (10)
struct ListNode
{
ElementType Element;
Position Next;
};
typedef Position List;
struct HashTbl
{
int TableSize;
List *TheLists;
};
/* Return next prime; assume N >= 10 */
// 获取大于N的第一个素数
static int
NextPrime(int N)
{
int i;
// 确保N为奇数
if (N % 2 == 0)
N++;
// 递增后仍然为奇数
for (; ; N += 2)
{
for (i = 3; i * i <= N; i += 2)
// 从3开始到 N的平方跟之间所有的奇数 如果都不满足被N整除 说明这个N就是个素数
if (N % i == 0)
goto ContOuter; /* Sorry about this! */
return N;
ContOuter:;
}
}
/* Hash function for ints */
// 哈希映射函数 简单的取余 模运算一次即可
Index
Hash(ElementType Key, int TableSize)
{
return Key % TableSize;
}
// 初始化一个hash表
HashTable
InitializeTable(int TableSize)
{
HashTable H;
int i;
if (TableSize < MinTableSize)
{
Error("Table size too small");
return NULL;
}
/* Allocate table */
H = malloc(sizeof(struct HashTbl));
if (H == NULL)
FatalError("Out of space!!!");
H->TableSize = NextPrime(TableSize);
/* Allocate array of lists */
H->TheLists = malloc(sizeof(List) * H->TableSize);
if (H->TheLists == NULL)
FatalError("Out of space!!!");
/* Allocate list headers */
for (i = 0; i < H->TableSize; i++)
{
// 链表数组中每一个元素是一个头结点 它的next指向实际数据结点
H->TheLists[i] = malloc(sizeof(struct ListNode));
if (H->TheLists[i] == NULL)
FatalError("Out of space!!!");
else
H->TheLists[i]->Next = NULL;
}
return H;
}
// 找到对应key在hash表中的地址指针
Position
Find(ElementType Key, HashTable H)
{
Position P;
List L;
L = H->TheLists[Hash(Key, H->TableSize)];
P = L->Next;
while (P != NULL && P->Element != Key)
/* Probably need strcmp!! */
P = P->Next;
return P;
}
void
Insert(ElementType Key, HashTable H)
{
Position Pos, NewCell;
List L;
Pos = Find(Key, H);
if (Pos == NULL) /* Key is not found */
{
// key此时不存在
NewCell = malloc(sizeof(struct ListNode));
if (NewCell == NULL)
FatalError("Out of space!!!");
else
{
L = H->TheLists[Hash(Key, H->TableSize)];
// 插入这个新节点或者重复节点到链表的头节点后面 作为一个新节点 每次新来的重复节点都插入到头节点后面
NewCell->Next = L->Next;
NewCell->Element = Key; /* Probably need strcpy! */
L->Next = NewCell;
}
}
}
ElementType
Retrieve(Position P)
{
return P->Element;
}
void
DestroyTable(HashTable H)
{
int i;
for (i = 0; i < H->TableSize; i++)
{
Position P = H->TheLists[i];
Position Tmp;
while (P != NULL)
{
Tmp = P->Next;
free(P);
P = Tmp;
}
}
free(H->TheLists);
free(H);
}
开放定址法实现(平方探测法)
声明 hashquad.h
/* Interface for quadratic probing hash table */
typedef int ElementType;
/* START: fig5_14.txt */
#ifndef _HashQuad_H
#define _HashQuad_H
typedef unsigned int Index;
typedef Index Position;
struct HashTbl;
typedef struct HashTbl *HashTable;
HashTable InitializeTable(int TableSize);
void DestroyTable(HashTable H);
Position Find(ElementType Key, HashTable H);
void Insert(ElementType Key, HashTable H);
ElementType Retrieve(Position P, HashTable H);
HashTable Rehash(HashTable H);
/* Routines such as Delete are MakeEmpty are omitted */
#endif /* _HashQuad_H */
/* END */
实现 hashquad.c
#include "fatal.h"
#include "hashquad.h"
#include <stdlib.h>
#define MinTableSize (10)
// 正常存在 空的位置 被删除过的位置
enum KindOfEntry { Legitimate, Empty, Deleted };
struct HashEntry
{
ElementType Element;
enum KindOfEntry Info;
};
typedef struct HashEntry Cell;
struct HashTbl
{
int TableSize;
Cell *TheCells;
};
static int
NextPrime(int N)
{
int i;
if (N % 2 == 0)
N++;
for (; ; N += 2)
{
for (i = 3; i * i <= N; i += 2)
if (N % i == 0)
goto ContOuter; /* Sorry about this! */
return N;
ContOuter:;
}
}
Index
Hash(ElementType Key, int TableSize)
{
return Key % TableSize;
}
/* START: fig5_15.txt */
HashTable
InitializeTable(int TableSize)
{
HashTable H;
int i;
/* 1*/ if (TableSize < MinTableSize)
{
/* 2*/ Error("Table size too small");
/* 3*/ return NULL;
}
/* Allocate table */
/* 4*/ H = malloc(sizeof(struct HashTbl));
/* 5*/ if (H == NULL)
/* 6*/ FatalError("Out of space!!!");
/* 7*/ H->TableSize = NextPrime(TableSize);
/* Allocate array of Cells */
/* 8*/ H->TheCells = malloc(sizeof(Cell) * H->TableSize);
/* 9*/ if (H->TheCells == NULL)
/*10*/ FatalError("Out of space!!!");
/*11*/ for (i = 0; i < H->TableSize; i++)
// 默认每个位置的状态都是empty
/*12*/ H->TheCells[i].Info = Empty;
/*13*/ return H;
}
/* END */
/* START: fig5_16.txt */
Position
Find(ElementType Key, HashTable H)
{
Position CurrentPos;
int CollisionNum;
/* 1*/ CollisionNum = 0;
/* 2*/ CurrentPos = Hash(Key, H->TableSize);
// empty 状态节点可以直接返回作为一个可以插入的位置
// 非empty状态且值不等于key说明这是一个放置了其他值的位置 那就进行平方探测去找下一个可以放的位置
/* 3*/ while (H->TheCells[CurrentPos].Info != Empty &&
H->TheCells[CurrentPos].Element != Key)
/* Probably need strcmp!! */
{
// 采用的平方探测法 2个相邻的节点之间差 (i+1)平方 - i平方 = 2*i + 1 意味着下个测试位置是在当前位置上加 1 3 5 7 等等
/* 4*/ CurrentPos += 2 * ++CollisionNum - 1;
/* 5*/ if (CurrentPos >= H->TableSize)
// 模拟取余
/* 6*/ CurrentPos -= H->TableSize;
}
/* 7*/ return CurrentPos;
}
/* END */
void
Insert(ElementType Key, HashTable H)
{
Position Pos;
// Find返回的位置是一个可以直接插入的位置
Pos = Find(Key, H);
if (H->TheCells[Pos].Info != Legitimate)
{
/* OK to insert here */
H->TheCells[Pos].Info = Legitimate;
H->TheCells[Pos].Element = Key;
/* Probably need strcpy! */
}
}
/* START: fig5_22.txt */
// 扩展新的一倍大小的hash表 把原来的数据值插入到新hash表中
HashTable
Rehash(HashTable H)
{
int i, OldSize;
Cell *OldCells;
/* 1*/ OldCells = H->TheCells;
/* 2*/ OldSize = H->TableSize;
/* Get a new, empty table */
/* 3*/ H = InitializeTable(2 * OldSize);
/* Scan through old table, reinserting into new */
/* 4*/ for (i = 0; i < OldSize; i++)
/* 5*/ if (OldCells[i].Info == Legitimate)
/* 6*/ Insert(OldCells[i].Element, H);
/* 7*/ free(OldCells);
/* 8*/ return H;
}
/* END */
ElementType
Retrieve(Position P, HashTable H)
{
return H->TheCells[P].Element;
}
void
DestroyTable(HashTable H)
{
free(H->TheCells);
free(H);
}
测试代码 testhash.c
#define SepChain /* Define the appropriate hash algorithm */
#ifdef SepChain
#include "hashsep.h"
#endif
#ifdef QuadProb
#include "hashquad.h"
#endif
#include <stdio.h>
#define NumItems 400
main()
{
HashTable H;
Position P;
int i;
int j = 0;
int CurrentSize;
H = InitializeTable(CurrentSize = 13);
for (i = 0; i < NumItems; i++, j += 71)
{
#ifdef QuadProb
if (i > CurrentSize / 2)
{
H = Rehash(H);
printf("Rehashing...\n");
CurrentSize *= 2;
}
#endif
Insert(j, H);
}
for (i = 0, j = 0; i < NumItems; i++, j += 71)
#ifdef SepChain
if ((P = Find(j, H)) == NULL || Retrieve(P) != j)
#endif
#ifdef QuadProb
if (Retrieve((P = Find(j, H)), H) != j)
#endif
printf("Error at %d\n", j);
printf("End of program.\n");
return 0;
}