哈希表在查找方面有非常大应用价值,本文记录一下利用哈希散列表来统计文本文件中每个单词出现的重复次数,这个需求当然用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. 在win10下安装双系统ubuntu16.04.3教程

    闲暇了两天,终于想起来要装一个Liunx系统了.于是捣鼓了一番,实现了在Win10下安装Ubuntu16.04.3版本. 一.准备工作 下载Ubuntu 16.04.3镜像 准备一个2G以上的U盘 下 ...

  2. python list 中元素的统计与排序

    1.  用count和dict.  dict的存储是散乱的, 不方面打印. 2. 用sorted.  注意, 得到的是一个元组list, 而不再是dict. dict_x = {} for item ...

  3. noip2009最优贸易(水晶球)

    题目:http://codevs.cn/problem/1173/ https://www.luogu.org/problemnew/show/P1073 本来考虑缩点什么的,后来发现不用. 只要记录 ...

  4. linux中bin与sbin目录的作用及区别介绍

    linux中bin与sbin目录的作用及区别介绍 本文介绍下,linux中的二个主要目录:bin与sbin,它们的作用与区别,学习linux的朋友可以参考下 在linux系统中,有两个重要的目录:bi ...

  5. oracle的热备份和冷备份

    一.冷备份介绍:    冷备份数据库是将数据库关闭之后备份所有的关键性文件包括数据文件.控制文件.联机REDO LOG文件,将其拷贝到另外的位置.此外冷备份也可以包含对参数文件和口令文件的备份,但是这 ...

  6. AHK按键转载

    AHK按键一个顶三个 需求: 我想要,用win键代替3个键,代替了之后, 以后用win+任意键,例如: 就是win+1就是ctrl+alt+shift+1 其中1是可以变的. 解决:考虑到win键比较 ...

  7. Android XML数据解析

    XML:可扩展标记语言.一般用于数据存储,SharedPreference就是使用xml文件保存信息的,SQLite底层也是xml文件,在网络方面通常作为信息的载体,把数据包装成xml来传递. XML ...

  8. 并发包学习(三)-AbstractQueuedSynchronizer总结

    J.U.C学习的第二篇AQS.AQS在Java并发包中的重要性,毋庸置疑,所以单独拿出来理一理.本文参考总结自<Java并发编程的艺术>第五章第二节队列同步器. 什么是AbstractQu ...

  9. Selenium2+python自动化64-100(大结局)[已出书]

    前言 小编曾经说过要写100篇关于selenium的博客文章,前面的64篇已经免费放到博客园供小伙伴们学习,后面的内容就不放出来了,高阶内容直接更新到百度阅读了. 一.百度阅读地址: 1.本书是在线阅 ...

  10. servlet笔记1

    Myeclipse关于Servlet项目文件的组织方面,如下: WEB-INF:这个目录名称和位置是固定的,放置在该目录下的文件或目录,对外界来说的封闭的,也就是客户端无法用http的任何方式访问到其 ...