哈希表是一种可以提供快速插入和查找操作的数据结构,查找和删除只需要接近常量的时间!操作速度快,编程实现也相对容易。哈希表的限制为程序员必须清楚表中要存储多少数据,比较难于扩展。另个缺点是没有很好的方法遍历哈希表。在不需要有序遍历,以及可以提前预测数据量大小的情况下,使用哈希表在速度和易用性方面是无与伦比的。
什么是哈希表?
我们日常使用的英文字典是个比较形象的哈希表的例子。假定现在把英语中从单词a到zyzzyva的50000个单词,都用编码来实现,来存入计算机内。一种编码方式是:用1来给a编码,2是b,...26是z,0表示空格。为了使得每个单词都有唯一的编码,决定只用幂的连乘,cat这个单词就可以用3*27*27+1*27+20(基值和权值的连乘之和)来表示。但是当单词比较长的时候比如zzzzzzzzz,编码所使用的整数范围会变得非常大。而在其中只有50000个编码代表的有效单词,大多数单元是空的。
哈希化
这个时候就需要一种压缩方法,把连乘系统中的得到的大的整数范围压缩到可接受的数组范围中。对于50000个有效单词,最终编码容量压缩到100000(2倍于有效单词,保持一定的空闲率)比较合适。现在就是要找一直方法把0到超过7000000000000的范围,压缩到0到100000。有一种简单方法就是取余数。即把当前的编码对100000取余,结果作为新的该单词的编码。这样也会带来一个问题:有可能两个单词对100000取余后的结果是相同的,这样就会造成两个单词的重码。
解决冲突的方案:由于指定的数组大小两倍于需要存储的数据量,因此,可能一半是空的。当冲突发生时,一个方法是通过系统的方法找到数组的一个空位,并把这个单词填入。这个方法叫做开放地址法。例如:cats哈希化的结果是5421,但它的位置已经被parsnip占用,那么可以考虑把cats放到5422的位置上。如果5422也被占用了,就用5423,依次类推。数组下标一直递增,直到找到空位。这叫做线性探测。
具体代码如下:
- import java.io.*;
- public class DataItem {
- private int iData; //Data
- //------------------------------------------
- public DataItem(int ii)//构造函数
- {
- iData=ii;
- }
- //------------------------------------------
- public int getKey()
- {
- return iData;
- }
- //------------------------------------------
- }//end class DataItem
- public class HashTable {
- private DataItem[]hashArray; //哈希表
- private int arraySize; //数组大小
- private DataItem nonItem; //针对删除的项
- //-----------------------------------
- public HashTable(int size)//constructor
- {
- arraySize=size; //Initialization
- hashArray=new DataItem[arraySize];
- nonItem=new DataItem(-1);
- }
- //------------------------------------------
- public void displayTable()
- {
- System.out.print("Table: ");
- for(int j=0;j<arraySize;j++)
- {
- if(hashArray[j]!=null)
- System.out.print(hashArray[j].getKey()+" ");
- else
- System.out.print("** ");
- }
- System.out.println("");
- }
- //------------------------------------------
- public int hashFunc(int key)
- {
- return key%arraySize;//hash function
- }
- //------------------------------------------
- public void insert(DataItem item)//insert a DataItem
- { //假定数组不满
- int key=item.getKey();
- int hashVal=hashFunc(key);
- while(hashArray[hashVal]!=null&&hashArray[hashVal].getKey()!=-1)
- {
- ++hashVal;
- hashVal%=arraySize;
- }
- hashArray[hashVal]=item;
- }//end insert()
- //------------------------------------------
- public DataItem delete(int key)//删除
- {
- int hashVal=hashFunc(key);
- while(hashArray[hashVal]!=null)
- {
- if(hashArray[hashVal].getKey()==key)
- {
- DataItem temp=hashArray[hashVal];
- hashArray[hashVal]=nonItem;
- return temp;
- }
- else
- ++hashVal;
- hashVal%=arraySize;
- }
- return null;
- }//end delete
- //------------------------------------------
- public DataItem find(int key)//寻找
- {
- int hashVal=hashFunc(key);//找到区间起点
- while(hashArray[hashVal]!=null)//遇到null终止
- {
- if(hashArray[hashVal].getKey()==key)//找到
- return hashArray[hashVal];
- else //否则向下查找
- ++hashVal;
- hashVal%=arraySize; //循环掉头wrap around
- }
- return null; //没找到返回null
- }
- }
- import java.io.*;
- public class HashTableApp {
- /**
- * @param args
- * @throws IOException
- */
- public static void main(String[] args) throws IOException {
- // TODO Auto-generated method stub
- DataItem aDataItem;
- int aKey,size,n,keysPerCell;
- System.out.print("输入哈希表的大小:");
- size=getInt();
- System.out.print("输入初始项的数量:");
- n=getInt();
- keysPerCell=10;
- HashTable theHashTable=new HashTable(size);
- for(int j=0;j<n;j++)
- {
- aKey=(int)(java.lang.Math.random()*keysPerCell*size);
- aDataItem=new DataItem(aKey);
- theHashTable.insert(aDataItem);
- }
- while(true)
- {
- System.out.print("输入以下指令的首字母");
- System.out.print("show,insert,delete or find: ");
- char choice=getChar();
- switch(choice)
- {
- case's':
- theHashTable.displayTable();
- break;
- case'i':
- System.out.print("输入要插入的整数:");
- aKey=getInt();
- aDataItem=new DataItem(aKey);
- theHashTable.insert(aDataItem);
- break;
- case'd':
- System.out.print("输入要删除的整数:");
- aKey=getInt();
- theHashTable.delete(aKey);
- break;
- case'f':
- System.out.print("输入要查询的整数:");
- aKey=getInt();
- aDataItem=theHashTable.find(aKey);
- if(aDataItem!=null)
- {
- System.out.println("找到 "+aKey);
- }
- else
- System.out.println("未找到"+aKey);
- break;
- default:
- System.out.print("非法输入/n");
- }//end switch
- }//end while
- } //end main()
- //----------------------------------------------------------
- public static String getString()throws IOException
- {
- InputStreamReader isr=new InputStreamReader(System.in);
- BufferedReader br=new BufferedReader(isr);
- String s=br.readLine();
- return s;
- }
- //----------------------------------------------------------
- public static char getChar()throws IOException
- {
- String s=getString();
- return s.charAt(0);
- }
- //----------------------------------------------------------
- public static int getInt()throws IOException
- {
- String s=getString();
- return Integer.parseInt(s);
- }
- //----------------------------------------------------------
- }//end class HashTableApp
输入结果为:
- 输入哈希表的大小:12
- 输入初始项的数量:8
- 输入以下指令的首字母show,insert,delete or find: s
- Table: 0 ** 74 99 100 89 62 5 ** ** 46 **
- 输入以下指令的首字母show,insert,delete or find: f
- 输入要查询的整数:99
- 找到 99
- 输入以下指令的首字母show,insert,delete or find: i
- 输入要插入的整数:66
- 输入以下指令的首字母show,insert,delete or find: s
- Table: 0 ** 74 99 100 89 62 5 66 ** 46 **
- 输入以下指令的首字母show,insert,delete or find: d
- 输入要删除的整数:100
- 输入以下指令的首字母show,insert,delete or find: s
- Table: 0 ** 74 99 -1 89 62 5 66 ** 46 **
- 输入以下指令的首字母show,insert,delete or find: