散列表(哈希表)-- 计算机科学里的一个伟大发明
来源:互联网 发布:java和嵌入式 编辑:程序博客网 时间:2024/05/16 15:34
“ 散列表是计算机科学里的一个伟大发明,它是由数组、表和一些数学方法相结合,构造起来的一种能够有效支持动态数据的存储和提取的结构。散列表的一个典型应用是符号表,在一些值(数据)与动态的字符串(关键码)集合的成员间建立一种关联。你最喜欢用的编译系统十之八九是使用了散列表,用于管理你的程序里各个变量的信息。你的网络浏览器可能也很好地使用了一个散列表来维持最近使用的页面踪迹。你与I n t e r n e t的连接可能也用到一个散列表,缓存最近使用的域名和它们的I P地址。” 在《The Prictice of Programming》中散列表那一节的开头就这样写到。 最近在看《Programming Pearls》和《The Prictice of Programming》这两本书,写的都很不错。里面讲了很多在实际编程遇到问题的比较优美的解决方法,和需要注意的问题。这两本书都很薄,简短,精炼,却又全面的。例如在前面写的一篇文章中讲到用STL的map来解决统计单词出现的次数问题,也提到可以用哈希表来解决。在其他语言,C#和Java好像有hash table 这个类,而STL中没有,其实有些c++库有包含hash table。以前在学数据结构这门课时,只是了解,没有实际编过。后来在复习数据结构时,发现哈希表是一个很高效的数据结构。如果选择了好的哈希函数,而且表并不太长,它对元素访问提供了一个O( 1 )的期望性能(二叉查找树是O(logn))。哈希表也有一些缺点。如果散列函数不好,或者所用的数组太小,其中的链接表就可能变得很长。 其中构造哈希函数是个关键,它应该把数据均匀地散布到数组里。对于字符串,网上有很多构造字符串的哈希函数,最常见的散列算法之一(而且比较好理解,我个认为)就是:逐个把字节加到已经构造的部分哈希值的一个倍数上。乘法能把新字节在已有的值中散开来根据经验,在对A S C I I串的哈希函数中,选择3 1和3 7作为乘数是很好的。字符串哈希函数如下: #define MULT 31 unsigned int hash(char *str) for (p = (unsigned char *)str ; *p != '/0'; p++) return h % NHASH; 在这个计算中用到了无符号字符。这样做的原因是, C和C + +对于c h a r是不是有符号数据没有给出明确定义。而我们需要哈希函数总返回正值。 还有一个问题就是如何取哈希表的大小,用素数作为数组的大小是比较明智的,因为这样能保证在数组大小、散列的乘数和可能的数据值之间不存在公因子。 在这两本书中都讲了哈希表实现的伪代码或C语言代码,书上有些编码还有点不习惯,于是自己动手写了一个C语言实现的哈希表,并用来解决统计单词出现的次数问题,大部分代码和《Programming Pearls》中的差不多。 #include <stdio.h> typedef struct node *nodeptr; #define NHASH 29989 unsigned int hash(char *str) for (p = (unsigned char *)str ; *p != '/0'; p++) return h % NHASH; void insertWord(char *str) h = hash(str); for(p = bin[h]; p != NULL; p = p->next) p = (nodeptr)malloc(sizeof(node)); int main() for (i = 0; i < NHASH; i++) while (scanf("%s", word) != EOF) for(i = 0; i < NHASH; i++) return 0; 后来我用一个2M的英文文本分别测试了用map 和 hash table来实现的两个程序,如果是用cin,cout函数来输入输出的话,前者运行时间为6.578秒,后者为3.031秒!如果用scanf,printf函数来输入输出的话,后者运行时间为0.421秒!总之,哈希表如果使用得当,常数时间的检索、插入和删除操作是任何其他技术都望尘莫及的。 查找和字符串处理都是些很实际的问题,很多地方都要用到它。我们编程的时候会面临这样一个问题,是用标准库呢还是自定义的组件呢?虽然STL中的map,set和string都很方便使用,减少代码编写量,但是它们没有针对特殊目的的哈希函数来的高效。
{
unsigned int h = 0;
unsigned char * p;
h = MULT * h + *p;
}
#include <stdlib.h>
#include <string.h>
typedef struct node {
char *word;
int count;
nodeptr next;
} node;
#define MULT 31
nodeptr bin[NHASH];
{
unsigned int h = 0;
unsigned char * p;
h = MULT * h + *p;
}
{
int h;
nodeptr p;
if(strcmp(p->word,str) == 0)
{
p->count++;
return;
}
if(p != NULL)
{
p->count = 1;
p->word = (char *)malloc(strlen(str)+1);
strcpy(p->word, str);
p->next = bin[h];
bin[h] = p;
}
}
{
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
int i;
nodeptr p;
char word[100];
bin[i] = NULL;
insertWord(word);
for(p = bin[i]; p != NULL; p = p->next)
printf("%s: %d/n", p->word, p->count);
}
- 散列表(哈希表)-- 计算机科学里的一个伟大发明
- 让人类难堪的伟大发明
- 生命中最伟大的发明
- 伟大的公司都有伟大的发明
- IT史上最伟大的十大存储发明
- 计算机编程领域最伟大的20个发明
- 21世纪最伟大的发明!软件史上的丰碑!再现伟大的四大发明的最伟大的精髓——这就是巨伟大的“活字输入法”!
- 一个值得怀疑的重大发明
- 不一定要有一个很重大的发明
- 转一个伟大的笑话
- [原创]MC-UCT_一个伟大的自然选择算法(转)
- 悼念一个伟大的公司——Sun(转)
- 如何成为一个伟大的开发者(一)
- 如何成为一个伟大的开发者(一)
- 如何成为一个伟大的开发者(二)
- 如何成为一个伟大的开发者(三)
- 如何成为一个伟大的开发者(四)
- python里给出一个列表,怎么样从列表里取出最小两项的索引值
- EntitySpaces2009的开发文档地址
- EntitySpaces2009支持事务
- hibernate 数据保存操作方法的原理对比
- EntitySpaces2009中连接Access的连接设置
- JAVA图片水印效果代码
- 散列表(哈希表)-- 计算机科学里的一个伟大发明
- EntitySpaces2009中的关系
- Sync Moto-A1200 with GMAIL contacts by SyncML
- 兽药价值营销对抗同质化
- Linux /proc/cpuinfo flags
- JSP文件下载
- PKU2299-求逆序数的题-实现(归并排序和树状数组)
- 如何得到其他程序焦点变化和启动的通知
- PostgreSQL RPM 安装笔记