哈希表在查找方面有很是大應用價值,本文記錄一下利用哈希散列表來統計文本文件中每一個單詞出現的重複次數,這個需求固然用NLP技術也很容易實現。node
1、基本介紹ios
一、Hash Key值:將每一個單詞按照字母組成經過一個乘子循環運算得出一個小於29989的整數,29989是一個比較大的質數。0~29989即爲Key值。數組
二、哈希函數:數據結構
1 //哈希函數 2 unsigned int hashIndex(const char* pWord) //返回hash表的索引(即hash指針數組的下標) 3 { 4 assert(pWord != NULL); 5 unsigned int index = 0; //如下四行爲將一個單詞映射到一個小於HASHNUMBER的正整數的函數 6 for (; *pWord != '\0'; pWord++) 7 index = MULT * index + *pWord; 8 return index % HASHNUMBER; 9 }
三、數據結構定義:ide
(1)整體採用數組法,數組下標就是Key值,Key取值範圍是1~29989,也即數組大小爲29989,數組的每一個項存儲該Key值下含有的單詞鏈表的頭指針,根據頭指針就能遍歷整個單詞鏈表函數
hashNodePtr bin[HASHNUMBER] = { NULL }; //HASHNUMBER大小的指針數組 做爲hash表
(2)單詞節點定義: 鏈表存儲同一Key值下的單詞,單詞節點主要包含單詞內容、單詞的重複次數、指向下一個單詞的指針;測試
typedef struct hashnode { //鏈表中每一個節點的結構 hashnode() { word = NULL; count = 0; next = NULL; } char* word; //單詞 int count; //出現頻率 struct hashnode *next; //指向鏈表中具備相同hash值的下個節點 }hashNode,*hashNodePtr;
四、哈希表解決衝突的途徑:鏈地址法。 即上面定義的存儲結構爲鏈表。spa
2、源代碼.net
// case1.cpp : 此文件包含 "main" 函數。程序執行將在此處開始並結束。 // #include "pch.h" #include <iostream> #include <assert.h> #define HASHNUMBER 29989 //散列表的大小,29989爲質數 #define MULT 31 //hash函數的一個乘子
//單詞節點的定義 typedef struct hashnode { //鏈表中每一個節點的結構 hashnode() { word = NULL; count = 0; next = NULL; } char* word; //單詞 int count; //出現頻率 struct hashnode *next; //指向鏈表中具備相同hash值的下個節點 }hashNode,*hashNodePtr; hashNodePtr bin[HASHNUMBER] = { NULL }; //HASHNUMBER大小的指針數組 做爲hash表 //這裏將每一個單詞映射爲一個小於HASHNUMBER的正整數 //哈希函數 unsigned int hashIndex(const char* pWord) //返回hash表的索引(即hash指針數組的下標) { assert(pWord != NULL); unsigned int index = 0; //如下四行爲將一個單詞映射到一個小於HASHNUMBER的正整數的函數 for (; *pWord != '\0'; pWord++) index = MULT * index + *pWord; return index % HASHNUMBER; } //想hash表中插入單詞 void insertWord(const char* pWord) //在hash表中插入單詞,若是已經存在了,則增長單詞的出現次數count { assert(pWord != NULL); hashNodePtr p; unsigned int index = hashIndex(pWord); //用hash函數獲得單詞的hash值,也就是hash數組的下標 for ( p = bin[index]; p !=NULL; p++) { //查找單詞是否已經在hash表中了 if (strcmp(pWord,p->word)==0) { //找到的話,直接將單詞的次數增長1便可 (p->count)++; return; } } //若是上面沒返回,也就是說hash表中沒有這個單詞,添加新節點,加入這個單詞 p = (hashNodePtr)malloc(sizeof(hashNode)); p->count = 1; //新節點的出現次數設置爲1 p->word = (char *)malloc(strlen(pWord) + 1); strcpy(p->word, pWord); p->next = bin[index]; //將新生成的節點插入到index爲下標的鏈表中去 bin[index] = p; } //讀取Data.txt中的單詞,並將每一個單詞插入到前面設計好的hash表中 void readWordToHashTable(const char *path) { //從文本文件中讀取單詞,插入到hash表中 FILE *fp; char buf[1024]; //存儲一行字符串 char *p; fp = fopen(path, "r"); if (fp==NULL) { printf("open file error!exit\n"); exit(-1); } while (NULL!=fgets(buf,sizeof(buf),fp)) //數據讀完,到文本末尾了 { buf[strlen(buf) - 1] = '\0'; //出去單詞最後的換行符 //print("%s/n",buf); if (strcmp("",buf)==0) //若是是空行,則繼續 { continue; } p = strtok(buf, "'\t','\n',' '"); //用strtok函數從一行字符串中分離出每一個單詞,分隔符設置爲(空格、逗號、換行、製表符) while (p!=NULL) { insertWord(p); //調用insertWord(),向hash表中插入分隔出來的單詞 p = strtok(NULL, "'\t','\n'"); } } fclose(fp); } void writeHashTable(const char *path) {//將結果寫到path中。 FILE *fp; hashNodePtr p; int i; fp = fopen(path, "w"); if (fp == NULL) { printf("write file error!exit"); exit(-1); } for (i = 0; i < HASHNUMBER; i++) { for (p = bin[i]; p != NULL; p = p->next) { fprintf(fp, "index %d:<%s,%d>", i, p->word, p->count); if (p->next == NULL) fprintf(fp, "\n"); } } fclose(fp); } //釋放hash表中佔用的內存 void freeHashTable() { int i; hashNodePtr p, q; p = q = NULL; for (i = 0; i < HASHNUMBER; i++) { p = bin[i]; while (p!=NULL) { q = p; p = p->next; free(q->word); free(q); } } } int main() { readWordToHashTable("data.txt"); writeHashTable("result.txt"); return 0; }
3、測試設計
因爲這裏沒法上傳測試文件,請本身構造一個單詞文件,單詞與單詞之間的間隔只能是換行或者製表符,由於目前代碼中定義的區分單詞的間隔只有製表符和換行符,因此構造文件的時候,直接複製一篇英語做文進去,將其中的標點符號所有刪除,空格一概改爲製表符,而後將該文本文件命名成data.txt,放入項目目錄下,運行程序,便可讀取該文件,並將統計結果的文件存儲在項目目錄下。
最後,附上筆者的實現項目源碼(包含data.txt測試文件):https://pan.baidu.com/s/17OVIuhf5tbaJ3TwsWzw-HA
參考連接:https://blog.csdn.net/shangshanhu/article/details/5917230