数据结构 No.13 哈希表

简介:

       这里先说一下哈希表的定义:哈希表是一种根据关键码去寻找值的数据映射结构,该结构通过把关键码映射的位置去寻找存放值的地方,说起来可能感觉有点复杂,我想我举个例子你就会明白了,最典型的的例子就是字典,大家估计小学的时候也用过不少新华字典吧,如果我想要获取“按”字详细信息,我肯定会去根据拼音an去查找 拼音索引(当然也可以是偏旁索引),我们首先去查an在字典的位置,查了一下得到“安”,结果如下。这过程就是键码映射,在公式里面,就是通过key去查找f(key)。其中,按就是关键字(key),f()就是字典索引,也就是哈希函数,查到的页码4就是哈希值。


基本概念

 

通过字典查询数据

  •        若关键字为k,则其值存放在f(k)的存储位置上。由此,不需比较便可直接取得所查记录。称这个对应关系f为散列函数,按这个思想建立的表为散列表。

    对不同的关键字可能得到同一散列地址,即k1≠k2,而f(k1)=f(k2),这种现象称为碰撞(英语:Collision)。具有相同函数值的关键字对该散列函数来说称做同义词。综上所述,根据散列函数f(k)和处理碰撞的方法将一组关键字映射到一个有限的连续的地址集(区间)上,并以关键字在地址集中的“像”作为记录在表中的存储位置,这种表便称为散列表,这一映射过程称为散列造表或散列,所得的存储位置称散列地址。
    若对于关键字集合中的任一个关键字,经散列函数映象到地址集合中任何一个地址的概率是相等的,则称此类散列函数为均匀散列函数(Uniform Hash function),这就是使关键字经过散列函数得到一个“随机的地址”,从而减少碰撞。

处理冲突


       1. 开放寻址法:Hi=(H(key) + di) MOD m,i=1,2,…,k(k<=m-1),其中H(key)为散列函数,m为散列表长,di为增量序列,可有下列三种取法:
             1.1. di=1,2,3,…,m-1,称线性探测再散列;
             1.2. di=1^2,-1^2,2^2,-2^2,⑶^2,…,±(k)^2,(k<=m/2)称二次探测再散列;
             1.3. di=伪随机数序列,称伪随机探测再散列。
       2. 再散列法:Hi=RHi(key),i=1,2,…,k RHi均是不同的散列函数,即在同义词产生地址冲突时计算另一个散列函数地址,直到冲突不再发生,这种方法不易产生“聚集”,但增加了计算时间。
       3. 链地址法(拉链法)

       4. 建立一个公共溢出区

代码实现

       这里主要演示的是开放寻址法和连地址法

扫描二维码关注公众号,回复: 930302 查看本文章

基本代码

       这里演示的是没有经过hash冲突处理的。

/**
 * 基础员工类
 * @project JavaData
 * @date 2018年4月10日 下午5:15:20 
 * @author Huaxu-Charles
 */
public class Info {
	private String key;
	private String name;
	
	public Info(String key, String name) {
		this.key = key;
		this.name = name;
	}

	public String getKey() {
		return key;
	}

	public void setKey(String key) {
		this.key = key;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
}
       基本功能实现
public class HashTable {
	private Info[] arr;
	
	/**
	 * 默认的构造方法
	 */
	public HashTable() {
		arr = new Info[100];
	}
	
	/**
	 * 指定数组初始化大小
	 */
	public HashTable(int maxSize) {
		arr = new Info[maxSize];
	}
	
	/**
	 * 插入数据
	 */
	public void insert(Info info) {
		arr[hashCode(info.getKey())] = info;
	}
	
	/**
	 * 查找数据
	 */
	public Info find(String key) {
		return arr[hashCode(key)];
	}
	
	public int hashCode(String key) {
		
		BigInteger hashVal = new BigInteger("0");
		BigInteger pow27 = new BigInteger("1");
		for(int i = key.length() - 1; i >= 0; i--) {
			int letter = key.charAt(i) - 96;
			BigInteger letterB = new BigInteger(String.valueOf(letter));
			hashVal = hashVal.add(letterB.multiply(pow27));
			pow27 = pow27.multiply(new BigInteger(String.valueOf(27)));
		}
		return hashVal.mod(new BigInteger(String.valueOf(arr.length))).intValue();
	}
}
     测试方法
public class TestHashTable {
	public static void main(String[] args) {
		HashTable ht = new HashTable();
		ht.insert(new Info("a","张三"));
		ht.insert(new Info("ct","李四"));
		ht.insert(new Info("wangwu","王五"));
		
		System.out.println(ht.find("a").getName());
		System.out.println(ht.find("ct").getName());
	}
}

开放寻址法

       当要插入一个元素时,可以连续地检查散列表的个各项,直到找到一个空槽来放置这个元素为止。检查顺序可以是线性的,可以是二次的,也可以是再次散列的。普通话解释就是,当你去超市买的这个东西没有时,你会继续寻找别的超市,知道买到这个东西。但是找的方式可以有很多种。这里需要注意的就是插入定义的规则一定也要落实到查找和删除中。

public class HashTable {
	private Info[] arr;
	
	/**
	 * 默认的构造方法
	 */
	public HashTable() {
		arr = new Info[100];
	}
	
	/**
	 * 指定数组初始化大小
	 */
	public HashTable(int maxSize) {
		arr = new Info[maxSize];
	}
	
	/**
	 * 插入数据
	 */
	public void insert(Info info) {
		//获得关键字
		String key = info.getKey();
		//关键字所自定的哈希数
		int hashVal = hashCode(key);
		//如果这个索引已经被占用,而且里面是一个未被删除的数据
		while(arr[hashVal] != null && arr[hashVal].getName() != null) {
			//进行递加
			++hashVal;
			//循环
			hashVal %= arr.length;
		}
		arr[hashVal] = info;
	}
	
	/**
	 * 查找数据
	 */
	public Info find(String key) {
		int hashVal = hashCode(key);
		while(arr[hashVal] != null) {
			if(arr[hashVal].getKey().equals(key)) {
				return arr[hashVal];
			}
			++hashVal;
			hashVal %= arr.length;
		}
		return null;
	}
	
	/**
	 * 删除数据
	 * @param key
	 * @return
	 */
	public Info delete(String key) {
		int hashVal = hashCode(key);
		while(arr[hashVal] != null) {
			if(arr[hashVal].getKey().equals(key)) {
				Info tmp = arr[hashVal];
				tmp.setName(null);
				return tmp;
			}
			++hashVal;
			hashVal %= arr.length;
		}
		return null;
	}
	
	public int hashCode(String key) {
//		int hashVal = 0;
//		for(int i = key.length() - 1; i >= 0; i--) {
//			int letter = key.charAt(i) - 96;
//			hashVal += letter;
//		}
//		return hashVal;
		
		BigInteger hashVal = new BigInteger("0");
		BigInteger pow27 = new BigInteger("1");
		for(int i = key.length() - 1; i >= 0; i--) {
			int letter = key.charAt(i) - 96;
			BigInteger letterB = new BigInteger(String.valueOf(letter));
			hashVal = hashVal.add(letterB.multiply(pow27));
			pow27 = pow27.multiply(new BigInteger(String.valueOf(27)));
		}
		return hashVal.mod(new BigInteger(String.valueOf(arr.length))).intValue();
	}
}

      测试

public class TestHashTable {
	public static void main(String[] args) {
		HashTable ht = new HashTable();
		ht.insert(new Info("a","张三"));
		ht.insert(new Info("ct","李四"));
		ht.insert(new Info("b","王五"));
		
		System.out.println(ht.find("a").getName());
		System.out.println(ht.find("ct").getName());
		System.out.println(ht.find("b").getName());
		
		ht.delete("b");
		System.out.println(ht.find("b").getName());
	}
}

链地址法

       把具有相同散列地址的关键字(同义词)值放在同一个单链表中,称为同义词链表。有m个散列地址就有m个链表,同时用指针数组T[0..m-1]存放各个链表的头指针,凡是散列地址为i的记录都以结点方式插入到以T[i]为指针的单链表中。T中各分量的初值应为空指针。

/*
 * 链结点
 */
public class Node {
	//数据域
	public Info info;
	//指针域
	public Node next;
	
	public Node(Info info) {
		this.info = info;
	}
	
}
public class LinkList {
	//头结点
	private Node first;
	
	public LinkList() {
		first = null;
	}
	
	/**
	 * 插入一个结点,在头结点后进行插入
	 */
	public void insertFirst(Info info) {
		Node node = new Node(info);
		node.next = first;
		first = node;
	}
	
	/**
	 * 删除一个结点,在头结点后进行删除
	 */
	public Node deleteFirst() {
		Node tmp = first;
		first = tmp.next;
		return tmp;
	}
	
	
	/**
	 * 查找方法
	 */
	public Node find(String key) {
		Node current = first;
		while(!key.equals(current.info.getKey())) {
			if(current.next == null) {
				return null;
			}
			current = current.next;
		}
		return current;
	}
	
	/**
	 * 删除方法,根据数据域来进行删除
	 */
	public Node delete(String key) {
		Node current = first;
		Node previous = first;
		while(!key.equals(current.info.getKey())) {
			if(current.next == null) {
				return null;
			}
			previous = current;
			current = current.next;
		}
		
		if(current == first) {
			first = first.next;
		} else {
			previous.next = current.next;
		}
		return current;
		
	}
}


public class HashTable {
	private LinkList[] arr;
	
	/**
	 * 默认的构造方法
	 */
	public HashTable() {
		arr = new LinkList[100];
	}
	
	/**
	 * 指定数组初始化大小
	 */
	public HashTable(int maxSize) {
		arr = new LinkList[maxSize];
	}
	
	/**
	 * 插入数据
	 */
	public void insert(Info info) {
		//获得关键字
		String key = info.getKey();
		//关键字所自定的哈希数
		int hashVal = hashCode(key);
		if(arr[hashVal] == null) {
			arr[hashVal] = new LinkList();
		}
		arr[hashVal].insertFirst(info);
	}
	
	/**
	 * 查找数据
	 */
	public Info find(String key) {
		int hashVal = hashCode(key);
		return arr[hashVal].find(key).info;
	}
	
	/**
	 * 删除数据
	 * @param key
	 * @return
	 */
	public Info delete(String key) {
		int hashVal = hashCode(key);
		return arr[hashVal].delete(key).info;
	}
	
	public int hashCode(String key) {
//		int hashVal = 0;
//		for(int i = key.length() - 1; i >= 0; i--) {
//			int letter = key.charAt(i) - 96;
//			hashVal += letter;
//		}
//		return hashVal;
		
		BigInteger hashVal = new BigInteger("0");
		BigInteger pow27 = new BigInteger("1");
		for(int i = key.length() - 1; i >= 0; i--) {
			int letter = key.charAt(i) - 96;
			BigInteger letterB = new BigInteger(String.valueOf(letter));
			hashVal = hashVal.add(letterB.multiply(pow27));
			pow27 = pow27.multiply(new BigInteger(String.valueOf(27)));
		}
		return hashVal.mod(new BigInteger(String.valueOf(arr.length))).intValue();
	}
}
    测试
public class TestHashTable {
	public static void main(String[] args) {
		HashTable ht = new HashTable();
		ht.insert(new Info("a","张三"));
		ht.insert(new Info("ct","李四"));
		ht.insert(new Info("b","王五"));
		ht.insert(new Info("dt","赵柳"));
		
		System.out.println(ht.find("a").getName());
		System.out.println(ht.find("ct").getName());
		System.out.println(ht.find("b").getName());
		System.out.println(ht.find("dt").getName());
		
//		System.out.println(ht.hashCode("a"));
//		System.out.println(ht.hashCode("ct"));
		
		System.out.println(ht.delete("a").getName());
		System.out.println(ht.find("a").getName());

	}
}




猜你喜欢

转载自blog.csdn.net/weixin_39923425/article/details/79884162