数据结构与算法 HASH表

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: 哈希表,里面有一个数组存储所有链表

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值