日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 >

哈希表(散列表)的介绍,代码实现

發布時間:2025/3/21 42 豆豆
生活随笔 收集整理的這篇文章主要介紹了 哈希表(散列表)的介绍,代码实现 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

百度百科介紹的哈希表:

哈希表Hash table,也叫散列表):是根據鍵(Key)而直接訪問在內存存儲位置的數據結構。也就是說,它通過計算一個關于鍵值的函數,將所需查詢的數據映射到表中一個位置來訪問記錄,這加快了查找速度。這個映射函數稱做散列函數,存放記錄的數組稱做散列表

-引入

? ? ? ? 假設數組a中有n個數據,要從中查找整數key,最樸素的辦法是通過循環遍歷查找。但是這種方法通常效率很低,例如在查找1到10是否在數組a[n]中,總共需要執行10*n次,時間復雜度為O(n),而通過哈希表,可以將整體效率優化到O(1)。

? ? ? ? 假設有一個長度為n的數組,里面數值的范圍為0到19,那么可以建立一個長度為20的數組,再遍歷原數組,用數組下標記錄每個元素出現的次數(如下)

void create_hash(int a[], int n, int table[]){int i;for(i = 0; i < n; i++){table[a[i]] ++;} }

建立的這個數組就是哈希表,將0到19轉換成數組下標的過程就是哈希函數。

? ? ? ? 再例如,在通訊錄中尋找李四的位置時,我們通常會根據李四姓氏的首字母L定位他的大概位置,接下來就好找了。這就是哈希表的思想,其中把目標李四映射成索引L的過程就是哈希函數的功能。

? ? ? ? 哈希函數的功能可以用一個公式表示:存儲位置 = f(關鍵字)。在上述這個例子中,L = f(李四),同樣的還可以有L = f(李梅),Z = f(張三)。

? ? ? ? 這樣的通過查找關鍵字不需要比較就可以獲得需要記錄的存儲位置就是一種新的存儲技術--散列技術。

? ? ? ? 散列技術是在記錄的存儲位置和它的關鍵字之間建立一個確定的對應關系f,使得每個關鍵字key對應一個存儲位置f(key)。

????????這種對應關系f成為哈希函數,采用散列技術將記錄存儲在一塊連續的存儲空間中,這塊存儲空間成為哈希表或散列表。

? ? ? ? 散列技術既是一種存儲方法,也是一種查找方法。它最適合的求解問題是查找與給定值相等的記錄。

由此我們可以得知哈希表的特點如下:

1)訪問速度很快;

2)需要額外的空間(用空間換取時間);

3)無序;

4)可能會產生碰撞(下面會講到);


-哈希沖突

? ? ? ? 理想情況下,每一個關鍵字通過哈希函數計算出來的地址都是不一樣的,但現實中基本沒有這種情況。日常生活中經常會出現多個關鍵字換算出來的地址相同,如上面通訊錄中,李四與李梅換算出來的存儲位置都是L,這種現象就叫做哈希沖突。

? ? ? ? 為解決哈希沖突,可以使用開放定址法和鏈地址法。

-開放定址法:

? ? ? ? 所謂開放定址法就是發生沖突時,向前順著哈希表尋找下一個空的地址存入。這里介紹開放定址中的線性探測法。

? ? ? ? 例如要將數組a[8]={10,21,34,45,58,39,51,38}存入哈希表中,哈希函數為對10取余,則前六個數據正常存入

下標 0 1 2 3 4 5 6 7 8 9 關鍵字 10 21 空 空 34 45 空 空 58 39

而要存如51時,發現1的位置已經存有21,此時使用開放定址法,51應存到下標為2的位置上

下標 0 1 2 3 4 5 6 7 8 9 關鍵字 10 21 51 空 34 45 空 空 58 39

同理,38存在下標為3的位置上

下標 0 1 2 3 4 5 6 7 8 9 關鍵字 10 21 51 38 34 45 空 空 58 39

????????這種方法的缺點是容易產生數據堆積,這樣無論是存入還是查找,效率都會降低。

? ? ? ? 開放定址法除了線性探測法外還有多種變形,如二次探測法,二次探測法是在線性探測的基礎上,將每次探測的步長改為了當前下標值?index + 12?、index + 22?、?index + 32?…… 直到找到空白位置插入元素為止。這種方法同樣有數據容易堆積的缺點。

-鏈地址法:

? ? ? ? 鏈地址法顧名思義是在哈希表中利用鏈表存儲數據,產生哈希沖突時,哈希表的每個下標都生成一條鏈表,這樣無論有多少個沖突,都只是在當前位置給單鏈表增加結點的問題。

? ? ? ? 鏈地址法對于可能會造成很多沖突的散列函數來說,提供了絕不會出現找不到地址的保障。當然,也帶來了查找時需要遍歷單鏈表的性能損耗。


-哈希函數的構造:

? ? ? ??要構造一個好的哈希函數,一般要根據兩個原則,一是計算簡單,二是散列地址分布均勻。

? ? ? ? 這里介紹直接定址法數字分析法除留余數法

-直接定址法:

? ? ? ? 例如文章開頭舉例的直接將數字存在哈希表中就是直接定址法。

? ? ? ? 直接定址法就是直接取關鍵字的某個線性函數值作為散列地址。f(key)= a * key + b? (a,b為常數)

? ? ? ? 再例如統計1980年后出生年份的人口數,我們可以用出生年份減去1980作為地址f(key)= key - 1980。

出生年份 1980 1981 1982 .... 2000 .... ↓哈希函數映射后 地址 00 01 02 .... 20 .... 出生人數 1500w 1600w 1300w .... 800w ....

-數字分析法:

? ? ? ? 如果關鍵字是較長的數字串,如學號,手機號,身份證號等,則可以取數字串的一部分來作為地址,如關鍵字為手機號時,取手機號后三位來作為地址就是數字分析法。當然為了減少哈希沖突,可以對取到的數字再做處理(翻轉,全部加1等)再作為地址。

-除留余數法:

? ? ? ? 這個方法是最常用的構造散列函數的方法。對于哈希表長為m的哈希函數公式為:f(key) = key mod?p (p <=m)? 其中mod是取余的意思。

? ? ? ? 這個方法的關鍵是選擇合適的p,通常來說p取小于或等于表長的最小質數或不包含小于20質因子的合數。


-代碼實現:

首先定義一個哈希表的結構及一些相關常數。其中HashTable就是哈希表結構。結構中的elem為一個動態數組。

#define SUCCESS 1 #define UNSUCCESS 0 #define HASHSIZE 12 #define NULLKEY -32768 typedef struct{int *elem;int count; }HashTable; int m = 0; //哈希表長,全局變量

初始化哈希表

Status InitHashTable(HashTable *H){ //status是一種函數類型,當函數返回值為函數結果狀態代碼int i; //時,函數定義為Status類型。函數結果狀態碼:TRUE 1、 m = HASHSIZE; //FALSE 0;OK 1、ERROR 0;INFEASIBLE -1、OVERFLOW -2H -> count = m;H -> elem = (int*)malloc(m*sizeof(int));for(i = 0;i < m; i++){H -> elem[i] = NULLKEY;} return OK; }

定義哈希函數(這里使用除留取余法)

int Hash(int key){return key % m; }

初始化完成后,接下來插入數據進哈希表,假設插入一串數字{12,67,56,16,25,37,22,29,15,47,48,34}

void InserHash(HashTable *H,int key){int addr = hash(key); //求散列地址 while(H -> elem[addr] != NULLKEY){addr = (addr + 1) % m; //開放定址法的線性探測法 }H -> elem[addr] = key; //直到有空位后插入關鍵字 }

哈希表存在后,構造搜索函數,通過散列表查找需要的記錄

Status SearchHash(HashTable H, int key, int *addr){*addr = Hash(key); //求散列地址 while(H.elem[*addr] != key){ //如果不為空則沖突 *addr = (*addr + 1) % m; //開放定址法的線性探測 if(H.elem[*addr] == NULLKEY || *addr == Hash(key)){return UNSUCCESS; //如果循環回到原點則說明關鍵字不存在}}return success; }

總結

以上是生活随笔為你收集整理的哈希表(散列表)的介绍,代码实现的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。