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

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

一、基本介绍

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

2、哈希函数:

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 }

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

原文地址:https://www.cnblogs.com/renyang/p/10206081.html

时间: 2024-10-08 20:33:19

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

linux内核中的哈希散列表

    介绍一下linux内核中的哈希散列表的实现,在linux内核中哈希散列表(hash list)用的非常的多, 并且自己以后在做软件设计的时候,也非常有可能用到.毕竟,哈希散列表在数据的查找过程中非常的方便.      linux内核对哈希散列表的实现非常的完美,所以非常有必要学习一下. 在哈希散列表的实现过程中,用到的两个非常有用的结构体:      哈希散列表头结构体 :                          struct hlist_head               

散列表(Hash table)及其构造

散列表(Hash table) 散列表,是根据关键码值(Key value)而直接进行访问的数据结构.它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度.这个映射函数叫做散列函数,存放记录的数组叫做散列表. 已知的查找方法: 1.顺序查找 O(N) 2.二分查找(静态查找) O(log2N) 3.二叉搜索树 O(h) h为二叉树的高度 平衡二叉树 O(log2N) Q:如何快速搜索到需要的关键字?如果关键字不方便比较怎么办? 查找的本质:已知对象找位置 有序安排对象:全序.半序 直接

散列表(hash表)

1. hash表: 又称散列表,以key-value的形式存储数据,能够由key快速定位到其指定的value,而不经过查找.它采用了函数式的映射思想,将记录的存储位置与关键词相关联,从而快速定位进行查找,复杂度为O(1). 2. hash函数: key和value的映射关系称为HASH函数,通过该函数可以计算key所对应的存储位置(表中存储位置,不是实际物理地址),即HASH地址. 构造HASH地址的方法有: (1)直接定址法:取关键词或关键词的某个线性函数为hash地址. (2)平方取中法:关

JavaScript 散列表(HashTable)

TypeScript方式实现源码 // 特性: // 散列算法的作用是尽可能快地在数据结构中找到一个值. 在之前的章节中, 你已经知道如果 // 要在数据结构中获得一个值(使用get方法) ,需要遍历整个数据结构来找到它.如果使用散列 // 函数,就知道值的具体位置,因此能够快速检索到该值.散列函数的作用是给定一个键值,然后 // 返回值在表中的地址 // ? put(key,value):向散列表增加一个新的项(也能更新散列表) // ? remove(key):根据键值从散列表中移除值 //

散列表(一).散列表基本内容介绍

一说到散列表,大家脑子想到的词就是:Hashmap.key-value.查找速度快.增删速度快等等.确实,在我们平常的学习生活中,散列表是很常见.也是用的很多的数据结构.那么散列表是怎样设计出来的,为什么它既可以和数组一样查询快,又可以和链表一样快增删,本节让我们一起了解一下什么是散列表.什么是散列函数.它究竟是如何设计出来的. 散列思想 什么是散列思想呢?散列表还有一个英文名叫做Hashtable,也叫做"哈希表"."hash表",hash我们都了解,是同过一定的

[BS]散列表 哈希表 Hash table

<第五章> 散 列 散列表的实现常常叫做散列(hashing).散列是一种用于以常数平均时间执行插入.删除和查找的技术. 关于散列有一个很重要的概念:散列函数.散列函数是散列的关键处之一,散列函数又是基于映射机制的一种对应关系(一般是多对一的关系). 这章可以分为5个部分:一般想法,散列函数,分离链接法,开放定址法(可分为线性探测.平方探测.双散列).再散列.可扩散列. 本文只写到前四节.即:一般想法,散列函数,分离链接法,开放定址法(可分为线性探测.平方探测.双散列)() 第五章第一节:一般

算法导论-散列表(Hash Table)

目录 引言 直接寻址 散列寻址 散列函数 除法散列 乘法散列 全域散列 完全散列 碰撞处理方法 链表法 开放寻址法 线性探查 二次探查 双重散列 随机散列 再散列问题 完整源码(C++) 参考资料 内容 1.引言 如果想在一个n个元素的列表中,查询元素x是否存在于列表中,首先想到的就是从头到尾遍历一遍列表,逐个进行比较,这种方法效率是Θ(n):当然,如果列表是已经排好序的话,可以采用二分查找算法进行查找,这时效率提升到Θ(logn);  本文中,我们介绍散列表(HashTable),能使查找效率

Java 散列表 hash table

Java 散列表 hash table @author ixenos hash table, HashTable, HashMap, HashSet hash table 是一种数据结构 hash table 为每个对象计算一个整数,该整数被称为散列码 hash code hash code 是由对象的实例域产生的一个整数,具有不同的数据域的对象将产生不同的hash code 如果自定义类,就要负责实现这个类的hashCode方法,注意要与equals方法兼容,即如果a.equals(b)为tr

散列表(hash table)&mdash;&mdash;算法导论(13)

1. 引言     许多应用都需要动态集合结构,它至少需要支持Insert,search和delete字典操作.散列表(hash table)是实现字典操作的一种有效的数据结构. 2. 直接寻址表     在介绍散列表之前,我们前介绍直接寻址表.     当关键字的全域U(关键字的范围)比较小时,直接寻址是一种简单而有效的技术.我们假设某应用要用到一个动态集合,其中每个元素的关键字都是取自于全域U={0,1,-,m-1},其中m不是一个很大的数.另外,假设每个元素的关键字都不同.    为表示动