学习《算法导论》第十一章 散列表 总结二
本节学习散列表的两种实现方法:链接法和开放寻址法的代码实现.
链接法
hashsep.h
#ifndef _HashSep_H
typedef int ElementType;
typedef unsigned int Index;
struct ListNode;
typedef struct ListNode* Position;
struct HashTbl;
typedef struct HashTbl* HashTable;
Index Hash(int Key, int TableSize);
HashTable InitializeTable(int TableSize);
void DestroyTable(HashTable H);
Position Find(ElementType Key, HashTable H);
void Insert(ElementType Key, HashTable H);
ElementType Retrieve(Position P);
#endif
hashsep.c
#include "hashsep.h"
#include <stdio.h>
#include <stdlib.h>
#define MinTableSize (10)
typedef Position List;
// 链表结点结构
struct ListNode
{
ElementType Element;
Position Next;
};
// 散列表结构
// 这里用的是链接法解决碰撞,所以散列表里存的是指针:指向链表
// 注意数据结构的定义
struct HashTbl
{
int TableSize;
List* TheLists;
};
// 入参:卫星数据的关键字, HASH表的大小
// 出参:映射到HASH表中的槽的位置
// 功能:散列函数,这里用的散列法是除法散列法,求余
Index Hash(int Key, int TableSize)
{
return Key % TableSize;
}
// 返回大于N的最小素数
static int NexPrime(int N)
{
int i;
if (N % 2 == 0)
N++;
for ( ; ; N += 2)
{
for (i = 3; i * i <= N; i+= 2)
{
if (N % i == 0)
break;
}
return N;
}
}
// 功能:散列表初始化
HashTable InitializeTable(int TableSize)
{
HashTable H;
int i;
// 合法性检查
if (TableSize < MinTableSize)
{
printf("Table size too small");
return NULL;
}
// 分配散列表的空间
H = malloc(sizeof(struct HashTbl));
if (NULL == H)
{
printf("H is NULL");
return NULL;
}
H->TableSize = NexPrime(TableSize);
// 分配链表的总空间大小
H->TheLists = malloc(sizeof(List) * H->TableSize);
if (NULL == H)
{
printf("H is NULL");
return NULL;
}
// 初始化散列表中链表头的大小
for (i = 0; i < H->TableSize; i++)
{
H->TheLists[i] = malloc(sizeof(struct ListNode));
if (NULL == H->TheLists[i])
{
printf("H is NULL");
return NULL;
}
else
{
H->TheLists[i]->Next = NULL;
}
}
return H;
}
// 查找操作
// 返回关键字Key所在的位置,而不是槽的位置
Position Find(ElementType Key, HashTable H)
{
Position P;
List L;
// 通过散列函数查找到关键字Key所在的槽的位置
L = H->TheLists[Hash(Key, H->TableSize)];
P = L->Next;
// 链表中的查找
while(P != NULL && P->Element != Key)
{
P = P->Next;
}
return P;
}
// 插入操作
void Insert(ElementType Key, HashTable H)
{
Position Pos, NewCell;
List L;
// 查找关键字Key是否在散列在散列表中
Pos = Find(Key, H);
if (NULL == Pos) // Key不在散列表中
{
NewCell = malloc(sizeof(struct ListNode));
if (NULL == NewCell)
{
printf("Distribute failed!");
return;
}
else
{
// 确定关键字Key要散列到的槽位
L = H->TheLists[Hash(Key, H->TableSize)];
// 插入该链表的最前面
NewCell->Next = L->Next;
NewCell->Element = Key;
L->Next = NewCell;
}
}
}
// 释放散列表
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);
}
// 返回存放在位置P处的元素
ElementType Retrieve(Position P)
{
return P->Element;
}
开放寻址法
hashquad.h
#ifndef _HashSep_H
typedef int ElementType;
typedef unsigned int Index;
struct ListNode;
typedef struct ListNode* Position;
struct HashTbl;
typedef struct HashTbl* HashTable;
Index Hash(int Key, int TableSize);
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 Rehash(HashTable H);
#endif
hashquad.c
#include <stdlib.h>
#include <stdio.h>
#include "hashquad.h"
#define MinTableSize 10
enum KindOfEntry {Legitimate, Empty, Deleted};
// 解决开放寻址法中删除操作的困难
// 将散列表中的每个槽位都进行标记:legitimate empty deleted
struct HashEntry
{
ElementType Element;
enum KindOfEntry Info;
};
typedef struct HashEntry Cell;
// 散列表结构
struct HashTbl
{
int TableSize;
Cell* TheCells;
};
// 返回大于N的最小素数
static int NexPrime(int N)
{
int i;
if (N % 2 == 0)
N++;
for ( ; ; N += 2)
{
for (i = 3; i * i <= N; i+= 2)
{
if (N % i == 0)
break;
}
return N;
}
}
// 散列函数
Index Hash(ElementType Key, int TableSize)
{
return Key % TableSize;
}
// 散列表的初始化
HashTable InitializeTable(int TableSize)
{
HashTable H;
int i;
if (TableSize < MinTableSize)
{
printf("Table size too small!");
return NULL;
}
// 分配散列表的空间
H = malloc(sizeof(struct HashTbl));
if (NULL == H)
{
printf("H is NULL");
return NULL;
}
H->TableSize = NextPrime(TableSize);
H->TheCells = malloc(sizeof(Cell) * H->TableSize);
if (NULL == H->TheCells)
{
printf("H->TheCells is NULL");
return NULL;
}
// 将散列表中每个槽的状态标记为empty
for(i = 0; i < H->TableSize; i++)
{
H->TheCells[i].Info = Empty;
}
return H;
}
// 查找操作
Position Find( ElementType Key, HashTable H )
{
Position CurrentPos;
int CollisionNum;
CollisionNum = 0;
CurrentPos = Hash( Key, H->TableSize );
while ( H->TheCells[ CurrentPos ].Info != Empty &&
H->TheCells[ CurrentPos ].Element != Key )
/* Probably need strcpy! */
{
CurrentPos += 2 * ++CollisionNum - 1; // 若该位置不是Empty且存放的关键字不是key,查找下一个
if ( CurrentPos >= H->TableSize )
CurrentPos -= H->TableSize;
}
return CurrentPos;
}
// 插入操作
void Insert( ElementType Key, HashTable H )
{
Position Pos;
Pos = Find( Key, H );
if ( H->TheCells[ Pos ].Info != Legitimate ) // 只要该位置不是Legitimate,就可以插入
{
/* Ok to insert here */
H->TheCells[ Pos ].Info = Legitimate;
H->TheCells[ Pos ].Element = Key; /* Probably need strcpy! */
}
}
// 更新
HashTable Rehash( HashTable H )
{
int i, OldSize;
Cell *OldCells;
OldCells = H->TheCells;
OldSize = H->TableSize;
/* Get a new, empty table */
H = InitializeTable( 2 * OldSize );
/* Scan through old table, reinserting into new */
for( i = 0; i < OldSize; i++ )
if ( OldCells[ i ].Info == Legitimate )
Insert( OldCells[ i ].Element, H );
free( OldCells );
return H;
}
ElementType Retrieve( Position P, HashTable H )
{
return H->TheCells[ P ].Element;
}
void
DestroyTable( HashTable H )
{
free( H->TheCells );
free( H );
}
但是以上两种方法都没实现删除操作,开放寻址法的关键字删除后,要将该槽位标记为deleted.