基於BKDRhash實現Hash演算法
雜湊(Hash)演算法,即雜湊函式。它是一種單向密碼體制,即它是一個從明文到密文的不可逆的對映,只有加密過程,沒有解密過程。同時,雜湊函式可以將任意長度的輸入經過變化以後得到固定長度的輸出。hash演算法一般用於快速查詢和加密。
hash演算法可以使用的雜湊函式種類很多,處理衝突的方法也有開放定址、再雜湊、鏈地址、公共溢位區等。
因此,在編寫程式碼之前,首先需要根據所要處理的資料,選擇合適的hash函式和衝突處理辦法。開放定址需要空閒儲存單元,所需要的表比實際容量大,而且容易產生二次聚集發生新衝突。鏈地址使用連結串列儲存關鍵字,可以隨時插入新資料,資料量大小不受限制。缺點是要用到指標,給新單元分配地址需要時間,會一定程度上減慢演算法速度,但影響不大可以忽略。
筆者需要處理的是一個10W行字串的字典,關鍵字重複率高。因此選擇適用於字串的雜湊函式,常用字串雜湊函式有 BKDRhash,APHash,DJBHash,JSHash,RSHash,SDBMHash,PJWHash,ELFHash等,個人傾向於BKDRHash,記憶和使用都很簡便。
BKDRHash函式程式碼如下:
unsigned int BKDRhash(TYPE key) {//BKDRhash函式 unsigned int seed = 131; unsigned int hash = 0; while(*key != '\n' && *key != 0) //通常使用時,判別條件為*key != 0即可,此處的*key != '\n'是因筆者程式需要 hash = hash * seed + (*key++); return hash % DICLEN; }
對於關鍵字重複的衝突處理方法,筆者這裡使用鏈地址法。hash表結構體如下:
#define STRLEN 15 #define DICLEN 100000 typedef char* TYPE; typedef int BOOL; typedef struct _NODE{ TYPE data; struct _NODE* next; }NODE; typedef struct _HASH_TABLE{ NODE* phead; //此變數可以不用,這裡使用是為了減少其他函式中的重新定義過程 NODE** chainhash; }HASH_TABLE;
準備工作OK,整理好思路,可以開始編寫hash演算法了。O(∩_∩)O
首先,建立一個hash表,並對雜湊表,連結串列,頭節點進行初始化。
NODE* create_node() {//開闢節點 NODE* pnode = (NODE*)malloc(sizeof(NODE)); memset(pnode, 0, sizeof(NODE)); pnode->data = (char*)malloc(STRLEN * sizeof(char)); memset(pnode->data, 0, STRLEN * sizeof(char)); pnode->next = NULL; return pnode; } HASH_TABLE* create_hash() {//建立hash表 HASH_TABLE* new_hash_table = (HASH_TABLE*)malloc(sizeof(HASH_TABLE)); memset(new_hash_table, 0, sizeof(HASH_TABLE)); new_hash_table->phead = create_node(); new_hash_table->chainhash = (NODE**)malloc(DICLEN * sizeof(NODE*)); for(int i = 0; i < DICLEN; i++){ new_hash_table->chainhash[i] = (NODE*)malloc(sizeof(NODE)); memset(new_hash_table->chainhash[i], 0, sizeof(NODE)); } return new_hash_table; }
插入資料
連結串列的chainhash每個分量的初始狀態都是空指標,凡是雜湊函式值 BKDRhash(data)相同的記錄,都插入同一個連結串列chainhash[i],此時i = BKDRhash(data)。該連結串列頭結點不為空的話,指標就後移,在表尾插入新記錄(表頭、表尾插入均可,只要保持每次操作相同,即同一連結串列中的關鍵字有序)。
BOOL insert_data(HASH_TABLE* hash, NODE* phead, TYPE data) {//插入新資料 if(hash == NULL) return 0; if(hash->chainhash[BKDRhash(data)]->data == NULL){ NODE* newnode = create_node(); strcpy(newnode->data, data); newnode->next = NULL; hash->chainhash[BKDRhash(data)]->data = newnode->data; hash->chainhash[BKDRhash(data)]->next = newnode->next; free(newnode); return 1; } else{ phead = hash->chainhash[BKDRhash(data)]; while(phead->next != NULL) phead = phead->next; phead->next = create_node(); strcpy(phead->next->data, data); phead->next->next = NULL; return 1; } }
查詢資料
查詢資料時,首先通過雜湊函式值找到對應的連結串列,然後比較字串內容。
NODE* find_data(HASH_TABLE* hash, NODE* phead, TYPE data) {//查詢資料 phead = hash->chainhash[BKDRhash(data)]; if(hash == NULL) return NULL; while(phead != NULL){ if(strncmp(phead->data, data, STRLEN) == 0) return phead; else phead = phead->next; } return NULL; }
刪除資料
刪除資料類似於單連結串列的刪除操作
BOOL del_data(HASH_TABLE* hash, NODE* phead, TYPE data) {//刪除資料 phead->next = create_node(); phead->next = hash->chainhash[BKDRhash(data)]; if(hash == NULL) return 0; while(phead->next != NULL){ if(strncmp(phead->next->data, data, STRLEN) == 0){ if(phead->next->data == hash->chainhash[BKDRhash(data)]->data) hash->chainhash[BKDRhash(data)] = phead->next->next; else phead->next = phead->next->next; return 1; } else phead->next = phead->next->next; } free(phead->next); return 0; }
修改資料
修改資料非常簡單,即先刪除後插入
BOOL alter_data(HASH_TABLE* hash, NODE* phead, TYPE data, TYPE new_data) {//修改資料 if(hash == NULL) return 0; if(data == new_data) return 1; if(del_data(hash, phead, data) == 1){ if(insert_data(hash, phead, new_data) == 1) return 1; else return 0; } else return 0; }
這樣,一個簡單的hash演算法就寫好了!筆者冗長的測試程式碼如下。。。。至於為什麼測試要寫這麼長,筆者也不造o(╯□╰)o
int main(int argc, char* argv[]) {//測試 int i = 0; char* testdata = "kyxntghcxolgqlw\n"; char data[STRLEN + 2] = {0}; HASH_TABLE* dic = create_hash(); FILE* fp = fopen("dic.txt", "r+"); assert(fp != 0); while(i < DICLEN){ fgets(data, STRLEN + 2, fp); insert_data(dic, dic->phead, data); i++; } //查詢測試 if(find_data(dic, dic->phead, testdata) != NULL) printf("find it: %s\n", (find_data(dic, dic->phead, testdata))->data); else printf("no this data!\n"); //刪除再查詢測試 if(del_data(dic, dic->phead, testdata) == 1) printf("delete it!\n"); else printf("try again!\n"); if(find_data(dic, dic->phead, testdata) != NULL) printf("find it: %s\n", (find_data(dic, dic->phead, testdata))->data); else printf("no this data!\n"); //修改資料測試 testdata = "fpwdwpk"; char* newdata = "bibibibibiu\n"; if(alter_data(dic, dic->phead, testdata, newdata) == 1){ if(find_data(dic, dic->phead, newdata) != NULL) printf("find it: %s\n", (find_data(dic, dic->phead, newdata))->data); else printf("no this data!\n"); } fclose(fp); free(dic); return 0; }
相關文章
- 基於react的hash路由簡易實現React路由
- 基於list_head實現的通用核心Hash表
- 短連結演算法實現–加鹽hash演算法
- 排序演算法總結(基於Java實現)排序演算法Java
- 基於 Redis 實現基本搶紅包演算法Redis演算法
- 【策略】一致性Hash演算法(Hash環)的java程式碼實現演算法Java
- 基於Gossip流言演算法實現alertmanager高可用Go演算法
- 手動實現一致性 Hash 演算法演算法
- K均值演算法基於CUDA環境的實現演算法
- 基於令牌桶演算法實現一個限流器演算法
- 自己實現一個一致性 Hash 演算法演算法
- 一致性hash演算法原理及go實現演算法Go
- 基於PHP + TRIE樹實現敏感詞過濾演算法PHP演算法
- 如何基於MindSpore實現萬億級引數模型演算法?模型演算法
- 基於鄰域粗糙集演算法python實現演算法Python
- 基於MeanShift的目標跟蹤演算法、實現演算法
- Java實現一致性Hash演算法深入研究Java演算法
- Hash演算法演算法
- iOS基於灰度的均值雜湊演算法實現影象匹配iOS演算法
- 一個hash表的實現
- [轉載] PHP 基於字典樹演算法實現搜尋聯想功能PHP演算法
- 基於時間序列檢測演算法的智慧報警實現演算法
- 基於粒子群演算法的分組揹包MATLAB實現演算法Matlab
- Python實現一條基於POS演算法的區塊鏈Python演算法區塊鏈
- NodeJS 基於redis的分散式鎖的實現(Redlock演算法)NodeJSRedis分散式演算法
- 基於MATLAB的指紋識別演算法模擬實現Matlab演算法
- 基於jquery實現的ExceljQueryExcel
- 基於JVMTI的Agent實現JVM
- 基於Python實現MapReducePython
- C#基於RSA加密演算法實現軟體註冊實戰演練C#加密演算法
- 基於CNN的新詞發現演算法CNN演算法
- 分散式鎖與實現(一)基於Redis實現!分散式Redis
- 關於Hash Partition
- 基於Masstransit實現Eventbus的功能
- 基於多 goroutine 實現令牌桶Go
- 基於 SplPriorityQueue 實現的排序方法排序
- 基於redis實現分散式鎖Redis分散式
- 基於jquery實現穿梭框效果jQuery