详解哈希表(散列表)
来源:互联网 发布:手机淘宝如何看总消费 编辑:程序博客网 时间:2024/04/28 23:30
昨天看了哈希表,起先一直在看哈希表的实现,看的有点头大。于是硬着头皮反复看了几遍,终于看懂了一些眉目。现在做下记录
首先,为什么要有哈希表?为什么,这很重要,凡是存在的都应该有其存在的理由。就像我的另外一篇博客中说的Java中为什么要有接口一样。这里,假设我们有一个很大的数组(如一个学生考试系统中的学生成绩),我们可以很方便的根据数据元素的下标定位到该元素。但是如果现在要求根据某个学生的学号来查找该学生呢?是的,只有学号的话,我们只能通过遍历比较实现数据的查找。但是比较是耗时的。能否直接使用检索关键字(如这里的学号)来得到元素的下标呢?答案是肯定的,事实上,哈希表就是做了这件事,使用哈希表可以使算法复杂度降低到O(1)。
我们将数组元素通过哈希函数(这个哈希函数可以自己定义,其实就是一个映射,将检索关键字映射到某个下标)。那么假如现在要存储、检索的是一个单词表。单词表中的单词来自与字典,而字典中有50000个单词。那么现在我们需要存储、检索1000个单词,我们该怎么做?
一、需要能够将单词直接映射成数组下标(这个可以通过自定义哈希算法实现),
二、合理地检索速度和内存开销(这个就需要对数组进行压缩了)。
下面来使用哈希表存储80个范围从aaaa到zzzz的四位字母的字符串,实现添加、查找功能。
新建一个节点对象DataItem
package com.wly.hashing;/** * 要保存的元素对象,设定其value值是一个4位长的英文字母即(aaaa-zzzz) * @author wly * */public class DataItem {private String value;public DataItem(String value) {this.value = value;}/** * 哈希函数,得到从key映射的为处理过的index * @return */public int hashFunc() {char[] cArray = value.toCharArray();//这里之所以乘以26,是因为这里的26相对于十进制里的“十”,是为了确保每个元素都有一个唯一的key值int key = cArray[0]*26*26*26 + cArray[1]*26*26 + cArray[2]*26 + cArray[3];return key;}public String getValue() {return value;}public void setValue(String value) {this.value = value;}}然后新建一个实现哈希表功能的类MyHashTable:
package com.wly.hashing;/** * 哈希表,使用数组实现 * @author wly * */public class MyHashTable {private DataItem[] array;private int arraySize;public MyHashTable(int size) {arraySize = size;array = new DataItem[arraySize];}/** * 插入元素,插入位置由元素的哈希值决定 * @param item */public void insert(DataItem item) {//得到插入下标int insertPos = item.hashFunc() % arraySize;//插入线性探测插入数据while(insertPos < arraySize && (array[insertPos] != null)) {insertPos ++;}if(insertPos < arraySize) {array[insertPos] = item;}}/** * 查找数据项 * @param item * @return */public DataItem find(DataItem item) {int findPos = item.hashFunc() % arraySize;while(findPos < arraySize && array[findPos] != null) {if(array[findPos].getValue() == item.getValue()) {System.out.println("找到value为" + item.getValue() + "的数据记录");return array[findPos];}findPos ++;}System.out.println("未找到value为" + item.getValue() + "的数据记录");return null;}public void displayItems() {for(int i=0;i<array.length;i++) {if(array[i] != null) {System.out.println(array[i].getValue());} else {System.out.println("null");}}}}最后测试一下:
package com.wly.hashing;public class Test {public static void main(String[] args) {MyHashTable hashTable = new MyHashTable(120);//产生随机元素,注意这里可能会产生重复元素,为了避免问题变得更复杂,先暂时不管for(int i=0;i<80;i++) {char[] temp = new char[4];for(int j=0;j<4;j++) {temp[j] = (char)((Math.random() * 26) + 97);}DataItem item = new DataItem(String.valueOf(temp));hashTable.insert(item);}hashTable.insert(new DataItem("abcd")); //插入一个确定的数据,用以测试查找hashTable.displayItems(); //显示所有元素//测试查找hashTable.find(new DataItem("abcd"));}}输出结果:
null
null
null
。。。
。。。
。。。
null
abcd
djaa
null
vsiw
。。。。。。
找到value为abcd的数据记录
数组元素压缩,因为要存储的元素范围大于压缩后的容器范围,不论使用何种哈希函数(只要是使用数组,而不是数组链表实现哈希表),冲突是不可避免的,这里说的冲突就是元素的聚集,需要注意一点的时,一个小的聚集会加速大得聚集的产生,这也就是哈希表在快要被填满的时候,效率会严重下降的原因。解决具体的方法在向哈希表中添加元素时使用以下方法
线性探测:如果要插入的元素指向的下标位置已经存在其他元素了,则将向后递增。这种做法会导致大量聚集的产生。
二次探测:如果要插入的元素指向的下标位置已经存在其他元素了,则向后以二次方的增量递增,如x+1,x+4,x+9,x+16。这种方法是对线性探测的优化。
再哈希法:对当前指向的下标在进行一次哈希化处理。是每个元素的探测步长都不同。
O啦~~~
转帖请保留出处:http://blog.csdn.net/u011638883/article/details/11794205
谢谢!!
- 详解哈希表(散列表)
- 哈希表(散列表)原理详解
- 哈希表(散列表)原理详解
- 哈希表(散列表)原理详解
- 哈希表(散列表)原理详解
- 哈希表(散列表)原理详解
- 哈希表(散列表)原理详解
- 哈希表(散列表)原理详解
- 哈希表(散列表)原理详解
- 散列表详解
- 数据结构 Hash 散列表详解
- 算法导论之散列表(哈希表)详解(hash table)
- 哈希表(散列表)详解及代码实现
- hash表(散列表)详解
- 散列表(哈希表)
- 散列表(哈希表)
- 散列表(哈希表)
- 散列表(哈希表)
- sonar网址
- 过滤网址和输入框中的特殊字符,防止sql注入(C#版)
- C#图片处理之:图片缩放和剪裁
- Swing之使得JFrame窗口最大化不可用方法
- 尊重技术,写给自己
- 详解哈希表(散列表)
- Swing之厢式布局管理器实例
- 算法的时间复杂度
- 初学者学Java(一)
- 算法的时间复杂度
- 网页内容繁简体切换(任意页面点击繁体后,其他页面也变成繁体)
- kill用法详细解释(特别是信号量9的使用以及理解)
- JS二级美化版地区
- 分析一个互联网产品的思路