hash表
定义
这里先说一下哈希(hash)表的定义:哈希表是一种根据关键码去寻找值的数据映射结构,该结构通过把关键码映射的位置去寻找存放值的地方,说起来可能感觉有点复杂,我想我举个例子你就会明白了,最典型的的例子就是字典,大家估计小学的时候也用过不少新华字典吧,如果我想要获取“按”字详细信息,我肯定会去根据拼音an去查找 拼音索引(当然也可以是偏旁索引),我们首先去查an在字典的位置,查了一下得到“安”,结果如下。这过程就是键码映射,在公式里面,就是通过key去查找f(key)。其中,按就是关键字(key),f()就是字典索引,也就是哈希函数,查到的页码4就是哈希值。
哈希冲突
但是问题又来了,我们要查的是“按”,而不是“安,但是他们的拼音都是一样的。也就是通过关键字按和关键字安可以映射到一样的字典页码4的位置,这就是哈希冲突(也叫哈希碰撞),在公式上表达就是key1≠key2,但f(key1)=f(key2)。冲突会给查找带来麻烦,你想想,你本来查找的是“按”,但是却找到“安”字,你又得向后翻一两页,在计算机里面也是一样道理的。
但哈希冲突是无可避免的,为什么这么说呢,因为你如果要完全避开这种情况,你只能每个字典去新开一个页,然后每个字在索引里面都有对应的页码,这就可以避免冲突。但是会导致空间增大(每个字都有一页)。
既然无法避免,就只能尽量减少冲突带来的损失,而一个好的哈希函数需要有以下特点:
1.尽量使关键字对应的记录均匀分配在哈希表里面(比如说某厂商卖30栋房子,均匀划分ABC3个区域,如果你划分A区域1个房子,B区域1个房子,C区域28个房子,有人来查找C区域的某个房子最坏的情况就是要找28次)。
2.关键字极小的变化可以引起哈希值极大的变化。
比较好的哈希函数是time33算法。PHP的数组就是把这个作为哈希函数。
谷歌公司的一个上机题
public class HashDemo {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
//创建一个Hash表
HashTab hash = new HashTab(7);
Random r = new Random();
for (int i = 0; i < 100; i++) {
int id =r.nextInt(99999)+10000;
System.out.println("id:"+id);
hash.add(new Emp("雇员"+i,id));
}
String key = "";
Scanner scan = new Scanner(System.in);
while(true){
System.out.println("1.添加雇员 list.显示雇员 exit.退出 find.查找del删除");
key = scan.nextLine();
switch (key) {
case "1":
System.out.println("输入ID:");
int id = scan.nextInt();
System.out.println("输入名字");
String name = scan.next();
hash.add(new Emp(name,id));
break;
case "list":
hash.list();
break;
case "exit":
System.exit(0);
break;
case "find":
System.out.println("输入id:");
int findById = scan.nextInt();
hash.findById(findById);
break;
case "del":
System.out.println("输入id:");
int del = scan.nextInt();
hash.deleteEmp(del);
break;
default:
break;
}
}
}
}
class Emp{
private String name;
private int id;
public Emp next;
public Emp(String name, int id) {
super();
this.name = name;
this.id = id;
}
public String toString() {
return "Emp [name=" + name + ", id=" + id + "]";
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
}
//创建EmpLinkedList,表示链表
class EmpLinkedList{
//头指针,指向链表里的第一个元素
private Emp head;
/**
* 添加雇员 插到链表尾部
*/
public void add(Emp emp){
if(head == null){
head = emp;
return;
}
Emp cur = head;
while(cur.next != null){
cur = cur.next;
}
cur.next = emp;
}
/**
* 遍历链表
*/
public void list(int i){
if(head == null){
System.out.println("第"+i+"条链表为空");
return;
}
System.out.println("第"+i+"条链表信息为:");
Emp cur = head;
while(cur != null){
System.out.println(cur.toString());
cur = cur.next;
}
}
/**
* 根据ID查找雇员信息
* @param id 雇员ID
* @return 查找到就返回否则返回null
*/
public Emp findEmpById(int id){
//判断链表是否为空
if(head == null){
System.out.println("链表为空");
return null;
}
Emp cur = head;
while(cur != null){
if(cur.getId() == id){
return cur;
}
}
return null;
}
/**
* 删除链表
* @param id
* @return
*/
public Emp deleteEmp(int id){
if(head == null){
System.out.println("链表为空");
return null;
}
Emp result = null;
if(head.getId() == id){
result = head;
head = null;
return result;
}
Emp cur = head;
while(cur.next != null){
if(cur.next.getId() == id){
result = cur.next;
if(cur.next.next != null){
cur.next = cur.next.next;
}else{
cur.next = null;
}
}
cur = cur.next;
}
return result;
}
}
/**
* HASH表
*/
class HashTab{
//链表数组,每个元素代表一个链表
private EmpLinkedList[] empLinkedListArray;
//表示链表的数量
private int size;
//初始化链表数组
public HashTab(int size) {
this.size = size;
this.empLinkedListArray = new EmpLinkedList[size];
//初始化链表数组内每个元素
for (int i = 0; i < size; i++) {
empLinkedListArray[i] = new EmpLinkedList();
}
}
/**
* 添加雇员
* @param emp
*/
public void add(Emp emp){
//根据员工的id,得到该员工应该添加到哪条链表里
int empLinkedListId = hashFun(emp.getId());
//将emp添加到对应的链表中
empLinkedListArray[empLinkedListId].add(emp);
}
/**
* 遍历所有的链表,即遍历hash表
*/
public void list(){
for (int i = 0; i < size; i++) {
empLinkedListArray[i].list(i);
}
}
/**
* 根据ID返回雇员信息
* @param id
* @return
*/
public Emp findById(int id){
//使用散列函数确定去哪条链表里找
int empLinkedListId = hashFun(id);
Emp emp = empLinkedListArray[empLinkedListId].findEmpById(id);
if(emp == null){
System.out.println("没有找到这个id");
}else{
System.out.println("在第"+empLinkedListId+"条链表找到:"+emp.toString());
}
return emp;
}
/**
* 根据ID在链表内删除雇员
* @param id
* @return
*/
public Emp deleteEmp(int id){
//使用散列函数确定去哪条链表里找
int empLinkedListId = hashFun(id);
Emp emp = empLinkedListArray[empLinkedListId].deleteEmp(id);
if(emp == null){
System.out.println("没有找到这个id");
}else{
System.out.println("在第"+empLinkedListId+"条链表删除:"+emp.toString());
}
return emp;
}
/**
* 编写散列函数,使用简单的取模法
* @param id
* @return
*/
public int hashFun(int id){
int result = id % size;
while(result > size){
result = result % size;
}
System.out.println(result);
return result;
}
}
Emp :存储雇员信息
EmpLinkedList : 存储雇员链表
HashTab: 哈希表,里面有一个数组存储所有链表