C數據結構與算法-基礎整理-查找-02:哈希表的理解

0x01.什麼是哈希表

散列表(Hash table,也叫哈希表),是根據關鍵碼值(Key value)而直接進行訪問的數據結構。也就是說,它通過把關鍵碼值映射到表中一個位置來訪問記錄,以加快查找的速度。這個映射函數叫做散列函數,存放記錄的數組叫做散列表

給定表M,存在函數f(key),對任意給定的關鍵字值key,代入函數後若能得到包含該關鍵字的記錄在表中的地址,則稱表M爲哈希(Hash)表,函數f(key)爲哈希(Hash) 函數。

存儲位置=F(關鍵字)

F稱爲散列函數或哈希函數。 

採用散列技術獎記錄存儲在一塊連續的存儲空間中,這塊連續的存儲空間稱爲哈希表(或散列表)。

散列技術是一種存儲方法,也是一種查找方法。

0x02.如何構造哈希函數

遵循的原則:

  • 計算簡單
  • 散列地址分佈均勻

常用方法:

  • 隨機數法:選擇一個隨機函數,取關鍵字的隨機函數值爲它的哈希地址。即H(key)=random(key),其中random爲隨機函數。適用於關鍵字長度不等時。
  • 除留餘數法:取關鍵字被某個不大於哈希表表長m的數p除後所得餘數爲哈希地址(p爲素數)
  • 摺疊法:將關鍵字分割成位數相同的幾部分(最後一部分的位數可不同),然後取這幾部分的疊加和(捨去進位)作爲哈希地址。適用於關鍵字位數比較多,且關鍵字中每一位上數字分佈大致均勻時。
  • 平方取中法:取關鍵字平方後的中間幾位爲哈希地址。(較常用的一種)
  • 直接定址法:取關鍵字或關鍵字的某個線性函數值爲哈希地址。即H(key)=key 或 H(key)=a*key+b   (a,b爲常數)。
  • 數字分析法:若關鍵字是以r爲基的數(如:以10爲基的十進制數),並且哈希表中可能出現的關鍵字都是事先知道的,則可取關鍵字的若干數位組成哈希地址。

考慮的因素:

  • 記錄的查找頻率
  • 哈希表的大小
  • 計算哈希函數所需時間
  • 關鍵字的分佈情況
  • 關鍵字的長度

0x03.處理哈希衝突

何時衝突:

  • 當關鍵字值域遠大於哈希表的長度,而且事先並不知道關鍵字的具體取值時。hash衝突就會發 生。
  • 當關鍵字的實際取值大於哈希表的長度時,而且表中已裝滿了記錄,如果插入一個新記錄,不僅發生衝突,而且還會發生溢出。

解決辦法:

  1. 開放定址法:基本思想是:當關鍵字key的哈希地址p=H(key)出現衝突時,以p爲基礎,產生另一個哈希地址p1,如果p1仍然衝突,再以p爲基礎,產生另一個哈希地址p2,…,直到找出一個不衝突的哈希地址pi ,將相應元素存入其中。 
  2. 鏈地址法:基本思想是將所有哈希地址爲 i 的元素構成一個稱爲同義詞鏈的單鏈表,並將單鏈表的頭指針存在哈希表的第i個單元中,因而查找、插入和刪除主要在同義詞鏈中進行。鏈地址法適用於經常進行插入和刪除的情況。
  3. 再哈希法:當哈希地址Hi=RH1(key)發生衝突時,再計算Hi=RH2(key)……,直到衝突不再產生。這種方法不易產生聚集,但增加了計算時間。

0x04.哈希表應用實例

結構:

#define MAXSIZE 1000
#define NULLADR -65535
typedef struct
{
	int* adr;//元素基址
	int size;//元素個數
}HashTable;
int TableSize = 0;

建表:

void IniHashTable(HashTable* H)
{
	int i;
	TableSize = MAXSIZE;
	H->size = TableSize;
	H->adr = (int*)malloc(TableSize * sizeof(int));
	for (i = 0; i < TableSize; i++)
	{
		H->adr[i] = NULLADR;
	}
}

哈希函數:

int Hash(int key)//哈希函數
{
	return key % TableSize;//除留取餘法
}

插入元素:

void InsertHash(HashTable* H, int key)
{
	int i = Hash(key);
	while (H->adr[i] != NULLADR)//開放定址法處理散列衝突
	{
		i = (i + 1) % TableSize;
	}
	H->adr[i] = key;
}

查找元素:

int Search(HashTable* H, int key)
{
	int i = Hash(key);//求散列地址
	while (H->adr[i] != key)//處理衝突
	{
		i = (i + 1) % TableSize;
	}
	if (H->adr[i] == NULLADR || i == Hash(key))//循環到了原點,查找失敗
	{
		return false;
	}
	return true;
}

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章