C語(yǔ)言寫(xiě)一個(gè)散列表
一、快速理解散列表
散列表,就是下標(biāo)可以為字母的數(shù)組。
假設(shè)現(xiàn)有一個(gè)數(shù)組int a[100],想查找其中第40個(gè)元素,則直接輸入a[40]就可以了,時(shí)間復(fù)雜度為O ( 1 ) O(1)O(1)。
問(wèn)題在于,當(dāng)下標(biāo)不是數(shù)字,而是一個(gè)字符串的時(shí)候,可能需要一個(gè)超大的空間才能將所有下標(biāo)妥善地存放在特定的位置。例如,若以大小寫(xiě)字母作為下標(biāo)索引,那么一位就需要預(yù)留52個(gè)空間,10位就需要52的10次方 這么大的空間,根本沒(méi)有設(shè)備可以滿足。
好在,52的10次方這么龐大的數(shù)字也超出了正常人的使用范圍,無(wú)論多長(zhǎng)的索引,我們能用上的值也絕對(duì)是有限的。
例如,現(xiàn)有下面三個(gè)字符串作為下標(biāo)
key1 = "microcold"; key2 = "tinycold"; key3 = "microcool";
其實(shí)只需要選取頭、尾兩個(gè)字母,就能很好地區(qū)分這三個(gè)字符串,即
def hash(key): ? ? return key[0]+key[-1]
但這種算法對(duì)索引字符的要求非常高,至少頭尾不能重復(fù)。所以,現(xiàn)在需要能把超長(zhǎng)字符串映射成特定短字符串而且盡量避免重復(fù)的算法。
二、散列函數(shù)
最簡(jiǎn)單的散列函數(shù)就是求余,將輸入字符串按位轉(zhuǎn)為整數(shù)之后求余。由于在字符串可能會(huì)轉(zhuǎn)成非常大的整數(shù),故需了解余數(shù)的性質(zhì)
(a+b)%c=(a%c+b %c)% c
相應(yīng)地有:
(a*b)%c=((a%c)*(b %c))% c
用C語(yǔ)言實(shí)現(xiàn)如下:
#include <stdio.h> #define MAXHASH 100 //快速取冪法,a*b^n%c int ?PowerMod (int a, int b, int n, int c)? { ? ? ? int ?ans = 1;? ? ? b = b % c;? ? ? while (n > 0) { ? ? ? ? ? if(n % 2 == 1)? ? ? ? ? ? ? ans = (ans * b) % c;? ? ? ? ? n = n / 2; ? ? ? //b >>= 1; ? ? ? ? b = (b * b) % c;? ? ? }? ? ? return (a*ans)%c;? }? int hash(char* key, int n){ ? ? int addr = 0; ? ? for(int i = 0; i < n; i++){ ? ? ? ? addr += PowerMod(key[i], 128, i, MAXHASH); ? ? } ? ? return addr%MAXHASH; } int main(){ ? ? char* str; ? ? int i; ? ? while(1){ ? ? ? ? gets(str); ? ? ? ? i = 0; ? ? ? ? while(str[i++]!='\0'){} ? ? ? ? printf("%d\n",hash(str,i)); ? ? } ? ? return 0; }
測(cè)試如下:
>gcc hash.c >a.exe asdf 21 microcold 81 tinycold 12 microcool 5 minicool 81 minicold 73
三、防撞
盡管minicool和microcold撞車了,但通過(guò)100以內(nèi)的位數(shù),去表示52的9次方 的樣本,也算是不錯(cuò)的表現(xiàn)了。
為了不發(fā)生撞車,則需更改數(shù)組中的元素類型——至少得是個(gè)結(jié)構(gòu)體。而防止撞車的方法很簡(jiǎn)單,如果發(fā)生撞車,那我就不散列了,直接發(fā)配到一個(gè)指定的數(shù)組中。
#include <stdio.h> #include <stdlib.h> #include <string.h> #define MAXHASH 100 typedef struct HASHNODE{ ? ? char *key; ? ? int next; } *hashNode; struct HASHNODE* hashTable[MAXHASH]; struct HASHNODE* crashTable[MAXHASH]; ? ? //存儲(chǔ)撞擊之后的值 int numCrash=0; ? ? ? ? ? ? ? ? ? //已有的撞擊值 void initTable(){ ? ? for(int i=0; i < MAXHASH; i++){ ? ? ? ? hashTable[i] = (hashNode)malloc(sizeof(struct HASHNODE)); ? ? ? ? hashTable[i]->key = NULL; ? ? ? ? hashTable[i]->next = -1; ? ? ? ? crashTable[i] = (hashNode)malloc(sizeof(struct HASHNODE)); ? ? ? ? crashTable[i]->key = NULL; ? ? ? ? hashTable[i]->next = -1; ? ? } } void insertCrash(char* str, int index, int n){ ? ? if(index == numCrash){ ? ? ? ? crashTable[numCrash]->key = (char*)malloc(sizeof(char)*n); ? ? ? ? strcpy(crashTable[numCrash++]->key, str); ?//此時(shí)新增一個(gè)節(jié)點(diǎn) ? ? } ? ? else { ? ? ? ? if(crashTable[index]->next==-1) ? ? ? ? ? ? crashTable[index]->next = numCrash; ? ? ? ? insertCrash(str, hashTable[index]->next, n); ? ? } } //n為字符串長(zhǎng)度 void insertHash(char* str, int index,int n){ ? ? if(hashTable[index]->key==NULL){ ? ? ? ? hashTable[index]->key = (char*)malloc(sizeof(char)*n); ? ? ? ? strcpy(hashTable[index]->key, str); ? ? }else{ ? ? ? ? if(hashTable[index]->next==-1) ? ? ? ? ? ? hashTable[index]->next = numCrash; ? ? ? ? insertCrash(str, hashTable[index]->next, n); ? ? } } void printHash(){ ? ? for(int i = 0; i < MAXHASH; i++){ ? ? ? ? if(hashTable[i]->key!=NULL) ? ? ? ? ? ? printf("hashTable[%d]:%s\n",i,hashTable[i]->key); ? ? ? ? if(crashTable[i]->key!=NULL) ? ? ? ? ? ? printf("crashTable[%d]:%s\n",i,crashTable[i]->key); ? ? } } int ?PowerMod (int a, int b, int n, int c)? { ? ? ? int ?ans = 1;? ? ? b = b % c;? ? ? while (n > 0) { ? ? ? ? ? if(n % 2 == 1)? ? ? ? ? ? ? ans = (ans * b) % c;? ? ? ? ? n = n / 2; ? ? ? //b >>= 1; ? ? ? ? b = (b * b) % c;? ? ? }? ? ? return (a*ans)%c;? }? int hash(char* key, int n){ ? ? int addr = 0; ? ? for(int i = 0; i < n; i++){ ? ? ? ? addr += PowerMod(key[i], 128, i, MAXHASH); ? ? } ? ? return addr%MAXHASH; } int main(){ ? ? initTable(); ? ? char* str; ? ? int i; ? ? while(1){ ? ? ? ? gets(str); ? ? ? ? if(strcmp(str,"exit")==0) break; ? ? ? ? i = 0; ? ? ? ? while(str[i++]!='\0'){} ? ? ? ? insertHash(str,hash(str,i),i); ? ? ? ? printf("%d\n",hash(str,i)); ? ? } ? ? printHash(); ? ? return 0; }
最后得到:
>gcc hash.c >a.exe asdf 21 hellworld 84 microcold 81 minicool 81 tinycool 20 tinycold 12 weixiaoleng 11 exit crashTable[0]:minicool hashTable[11]:weixiaoleng hashTable[12]:tinycold hashTable[20]:tinycool hashTable[21]:asdf hashTable[81]:microcold hashTable[84]:hellworld
可見(jiàn)一方面的確散列了,另一方面也的確防撞了。
到此這篇關(guān)于C語(yǔ)言寫(xiě)一個(gè)散列表的文章就介紹到這了,更多相關(guān)C語(yǔ)言寫(xiě)散列表內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C語(yǔ)言+EasyX實(shí)現(xiàn)數(shù)字雨效果
這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言+EasyX實(shí)現(xiàn)數(shù)字雨效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-11-11C語(yǔ)言數(shù)據(jù)結(jié)構(gòu)二叉樹(shù)先序、中序、后序及層次四種遍歷
這篇文章主要介紹了C語(yǔ)言數(shù)據(jù)結(jié)構(gòu)二叉樹(shù)先序、中序、后序及層次四種遍歷方式,具有一定的知識(shí)性參考價(jià)值,需要的小伙伴可以先看一下2022-02-02C語(yǔ)言實(shí)現(xiàn)用戶態(tài)線程庫(kù)案例
下面小編就為大家?guī)?lái)一篇C語(yǔ)言實(shí)現(xiàn)用戶態(tài)線程庫(kù)案例。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-05-05QT實(shí)現(xiàn)自定義Http客戶端的示例代碼
這篇文章主要為大家詳細(xì)介紹了QT如何實(shí)現(xiàn)自定義Http客戶端的,可以實(shí)現(xiàn)支持get,post請(qǐng)求方式;支持連接超時(shí)處理;支持網(wǎng)絡(luò)錯(cuò)誤,嘗試重連等功能,感興趣的小伙伴可以學(xué)習(xí)一下2022-11-11C語(yǔ)言實(shí)現(xiàn)動(dòng)態(tài)順序表的示例代碼
順序表是用一段物理地址連續(xù)的存儲(chǔ)單元依次存儲(chǔ)數(shù)據(jù)元素的線性結(jié)構(gòu)。順序表一般分為靜態(tài)順序表和動(dòng)態(tài)順序表,本文主要和大家介紹的是動(dòng)態(tài)順序表的實(shí)現(xiàn),需要的可以參考一下2022-10-10基于Matlab圖像處理的公路裂縫檢測(cè)實(shí)現(xiàn)
隨著公路的大量投運(yùn),公路日常養(yǎng)護(hù)和管理已經(jīng)成為制約公路運(yùn)營(yíng)水平提高的瓶頸,特別是路面狀態(tài)采集、檢測(cè)維護(hù)等工作更是對(duì)傳統(tǒng)的公路運(yùn)維模式提出了挑戰(zhàn)。這篇文章主要介紹了如何通過(guò)Matlab圖像處理實(shí)現(xiàn)公路裂縫檢測(cè),感興趣的可以了解一下2022-02-02