讀數據結構與算法分析
哈希表
一種用於以常數平均時間執行插入、刪除和查找操作的數據結構。
但是是無序的
一般想法
- 通常為一個包含關鍵字的具有固定大小的數組
- 每個關鍵字通過散列函數映射到數組中
- 沖突:兩個關鍵字映射到同一個值
散列函數
簡單的散列函數
不均勻,不夠好
typedef unsigned int Index ;
Index Hash(const char *key, int Tablesize)
{
unsigned int HashVal = 0;
while(*key != '\0')
HashVal += *key++ ;
return HashVal % Tablesize ;
}
一個好的散列函數
Index Hash(const char *key, int TableSize)
{
unsigned int HashVal = 0 ;
while(*key != '\0')
HashVal += (HashVal << 5) + *key++ ;
return HashVal %TableSize ;
}
解決沖突
分離鏈接法
將散列到同一個值的所有元素保存在一個表中
類型聲明
struct ListNode ;
typedef struct ListNode *Position ;
struct Hashbl ;
typedef struct HashTbl *HashTable ;
HashTable IntializeTable(int TableSize) ;
void DestroyTable(HashTable H) ;
Position Find(ElemenetType Key, HashTable H) ;
Void Insert(ElementType Key, HashTable H) ;
ElementType Retireve(Position P) ;
struct ListNode
{
ElementType Element ;
Position Next ;
} ;
typedef Position List ;
strcut HashTbl
{
int TableSize ;
List *TheLists ; //實則為指針數組
}
哈希表的初始化
HashTable IntializeTable(int TableSize)
{
HashTable H ;
TableSize = NextPrime(TableSize) ;//將表的大小設為它下一個素數
H->TheList = malloc(sizeof (List) * H->TableSize) ;
if(H->TheList == NULL)
FatalError("內存不足") ;
for(int i = 0,i < H->TableSize; i++)
{
H->TheLists[i] = malloc(struct ListNode) ;
if(H->TheLists[i] == NULL)
FatalError("內存不足") ;
else
H->TheLists[i]->Next = NULL ;
}
return H ;
}
Find函數
Position Find(ElementType Key, HashTable)
{
Position P ;
List L ;
L = H->TheLists[Hash(Key,H->TableSize)];
P = L->Next ;
while(P != NULL && P->Element != Key) //如果關鍵字為字符串,可能用到strcmp函數
P = P->Next ;
return P ;
}
Insert函數
void Insert(ElementType Key, HashTable H)
{
Position P ,TmpCell;
List L ;
TmpCell = malloc(ListNode) ;
if(TmpCell == NULL)
FatalError("內存不足") ;
else
{
L = H->TheLists[Hash(Key,H->TableSize)] ;
P = L->Next ;
TmpCell->Element = Key ;
TmpCell->Next = P ;
L->Next = TmpCell ;
free(TmpCell) ;
}
}
開放定址法
當發生沖突時,重新選擇地址
Hi(X) = (Hash(X) + F(i)) mod TableSize ;
- 線性探測法:會發生一次聚集
- 平方探測法:效果較好
F(i) = i^2
類型聲明
typedef unsigned int Index ;
typedef Index Position ;
struct Hashbl ;
typedef struct HashTbl *HashTable ;
HashTable IntializeTable(int TableSize) ;
void DestroyTable(HashTable H) ;
Position Find(ElemenetType Key, HashTable H) ;
Void Insert(ElementType Key, HashTable H) ;
ElementType Retireve(Position P) ;
enum KindOfEntry{Legitimate,Empty,Deleted} ;
struct HashEntry
{
ElementType Element ;
enum KingOfEntry Info ;
} ;
typedef struct HashEntry Cell ;
strcut HashTbl
{
int TableSize ;
Cell *TheCells ; //直接用結構數組
}
Find函數
Position Find(ElementType Key,HashTable H)
{
Position P ;
int Num = 0;
P = H->TheCells[Hash(Key,H->TableSize)] ;
while(H->TheCells[P].Info != Empty && H->TheCells[P].Element != Key) //如果關鍵字為字符串需要使用strcmp函數
{
P += 2 * ++Num - 1 ;
if(P >= H->TableSize)
P -= H->TableSize ;
}
return P ;
}
Insert函數
void Insert(ElementType Key, HashTable H)
{
Position P ;
P = Find(Key,H) ;
if(H->TheCells[P] != Legitimate)
{
H->TheCells[P].Info = Legitimate ;
H->TheCells[P].Element = Key ;//如果為字符串,則可能使用strcpy函數
}
}
總結
- 哈希表以常數平均時間執行Insert和Find操作是重要特點,但它是無序的
- 在解決沖突時使用不同的方法應該注意它的裝填因子導致的效率問題
- 應用:
- 編譯器使用哈希表追蹤代碼中聲明的變量
- 在圖論當中每個節點應當由實際的名字
- 游戲編程中的變化表
- 在線拼寫檢驗