哈希表在查找方面有非常大应用价值,本文记录一下利用哈希散列表来统计文本文件中每个单词出现的重复次数,这个需求当然用NLP技术也很容易实现。

一、基本介绍

1、Hash Key值:将每个单词按照字母组成通过一个乘子循环运算得出一个小于29989的整数,29989是一个比较大的质数。0~29989即为Key值。

2、哈希函数:

 //哈希函数
unsigned int hashIndex(const char* pWord) //返回hash表的索引(即hash指针数组的下标)
{
assert(pWord != NULL);
unsigned int index = ; //以下四行为将一个单词映射到一个小于HASHNUMBER的正整数的函数
for (; *pWord != '\0'; pWord++)
index = MULT * index + *pWord;
return index % HASHNUMBER;
}

3、数据结构定义:

(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;

  

4、哈希表解决冲突的途径:链地址法。   即上面定义的存储结构为链表。

二、源代码

// 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;
}

  三、测试

由于这里无法上传测试文件,请自己构造一个单词文件,单词与单词之间的间隔只能是换行或者制表符,因为目前代码中定义的区分单词的间隔只有制表符和换行符,所以构造文件的时候,直接复制一篇英语作文进去,将其中的标点符号全部删除,空格一律改成制表符,然后将该文本文件命名成data.txt,放入项目目录下,运行程序,即可读取该文件,并将统计结果的文件存储在项目目录下。

最后,附上笔者的实现项目源码(包含data.txt测试文件):https://pan.baidu.com/s/17OVIuhf5tbaJ3TwsWzw-HA

参考链接:https://blog.csdn.net/shangshanhu/article/details/5917230

用Hash Table(哈希散列表)实现统计文本每个单词重复次数(频率)的更多相关文章

  1. Hash表(hash table ,又名散列表)

    直接进去主题好了. 什么是哈希表? 哈希表(Hash table,也叫散列表),是根据key而直接进行访问的数据结构.也就是说,它通过把key映射到表中一个位置来访问记录,以加快查找的速度.这个映射函 ...

  2. Hash表 hash table 又名散列表

    直接进去主题好了. 什么是哈希表? 哈希表(Hash table,也叫散列表),是根据key而直接进行访问的数据结构.也就是说,它通过把key映射到表中一个位置来访问记录,以加快查找的速度.这个映射函 ...

  3. 哈希表(散列表)—Hash表解决地址冲突 C语言实现

    哈希表(Hash table,也叫散列表),是根据关键码值(Key value)而直接进行访问的数据结构.也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度.具体的介绍网上有很详 ...

  4. 散列表(Hash table)及其构造

    散列表(Hash table) 散列表,是根据关键码值(Key value)而直接进行访问的数据结构.它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度.这个映射函数叫做散列函数,存放记录 ...

  5. 哈希表(Hash Table)原理及其实现

    原理 介绍 哈希表(Hash table,也叫散列表), 是根据关键码值(Key value)而直接进行访问的数据结构.也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度.这个映 ...

  6. java资料——哈希表(散列表)(转)

    哈希表       散列表(Hash table,也叫哈希表),是根据关键码值(Key value)而直接进行访问的数据结构.也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度. ...

  7. 数据结构 -- 哈希表(hash table)

    简介   哈希表(Hash table,也叫散列表),是根据关键码值(Key value)而直接进行访问的数据结构.也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度.这个映射函 ...

  8. 【阅读笔记:散列表】Javascript任何对象都是一个散列表(hash表)!

    什么是散列表? 散列表是Dictionary(字典)的一种散列表实现方式,字典传送门 一个很常见的应用是使用散列表来表示对象.Javascript语言内部就是使用散列表来表示每个对象.此时,对象的每个 ...

  9. linux内核的双链表list_head、散列表hlist_head

    一.双链表list_head 1.基本概念 linux内核提供的标准链表可用于将任何类型的数据结构彼此链接起来. 不是数据内嵌到链表中,而是把链表内嵌到数据对象中. 即:加入链表的数据结构必须包含一个 ...

随机推荐

  1. spring注解事务使用总结

    在使用spring的注解事务的时候,需要考虑到事务的传播行为.遇到什么类型的异常时,事务才起作用.事务方法之间的嵌套调用时,怎么样才生效等等诸多问题.网上搜到很多的主要还是一堆理论文字描述,我这里给出 ...

  2. 【liunx命令】上传下载文件的方法

    scp   帮助命令: man scp   scp功能: 下载远程文件或者目录到本地, 如果想上传或者想下载目录,最好的办法是采用tar压缩一下,是最明智的选择.   从远程主机 下载东西到 本地电脑 ...

  3. python3的eval和exec的区别与联系

    eval: 可以把字符串里的字符转换为可执行代码,但只支持一行字符.可以返回执行后得到的值.如下: f = "3+6+9+8"s = eval(f)print(s)输出: &quo ...

  4. wpf 客户端【JDAgent桌面助手】业余开发的终于完工了。。晒晒截图

    目录区域: 业余开发的wpf 客户端终于完工了..晒晒截图 wpf 客户端[JDAgent桌面助手]开发详解-开篇 wpf 客户端[JDAgent桌面助手]详解(一)主窗口 圆形菜单... wpf 客 ...

  5. purescript 基本试用

    安装环境 安装预编译文件 https://github.com/purescript/purescript/releases 配置环境变量: export PATH=$PATH:/Users/dalo ...

  6. The type org.springframework.context.support.AbstractApplicationContext cannot be resolved

    在 myeclipse中,使用 jdk6和7,并使用 spring-framework-5.0.2.RELEASE 时,编写代码: import org.springframework.context ...

  7. PYTHON 常用API ***

    1.类型判断 data = b'' data = bytes() print (type(data)) #<class 'bytes'> isinstance(123,int) if ty ...

  8. erlang和ruby互相调用

    erlang调用ruby https://github.com/mojombo/erlectricity ruby调用erlang https://github.com/davebryson/rint ...

  9. [转]MongoDB随笔2:使用查询

    转自:http://www.cnblogs.com/yangecnu/archive/2011/07/16/2108450.html 一.通过查询获取数据 在深入讨论查询之前,首先来了解一下查询返回的 ...

  10. Keras函数式 API

    用Keras定义网络模型有两种方式, Sequential 顺序模型 Keras 函数式 API模型 之前我们介绍了Sequential顺序模型,今天我们来接触一下 Keras 的函数式API模型. ...