zoukankan      html  css  js  c++  java
  • 散列·分离链接法

    一、介绍

    ​ 散列表的实现被叫做散列,是一种用于常数平均时间执行插入、删除和查找的技术。通常做法是保存key-value的数据结构,理想的散列表数据结构不过是具有固定大小的数组key 作为关键字,value 是真正存储的数据。将不同的value根据各自的key 存取在散列表中的算法叫做散列函数

    理想散列情况

    ​ 我们寻找一个散列函数,该函数要在单元之间均匀的分配关键字,每个关键字 key 落在不同的位置,均匀的分布在散列数组中,互补干扰。

    冲突

    ​ 数组大小总是有限的,例如我们将12个数据插入大小为11的散列数组中,必定会有一个2个数据争抢一个位置,这种情况叫做冲突

    ​ 散列最主要的问题就是确定散列表的大小,以及解决冲突!

    散列函数

    ​ 将 key-value 数据存入散列表中的位置,确定该位置的算法成为散列函数。

    装填因子(装载因子/加载因子)

    ​ 定义装填因子 (lambda) = 表中数据个数/表大小。

    装填因子-再散列。https://www.cnblogs.com/dhcao/p/10548383.html#定义

    二、分离链表法

    将散列到同一个值的所有元素保留到一个表中。设我们散列表的数组大小为10,当我们产生冲突时,即有2个数据散列到了位置为2的地方,那我们将位置为2的位置再设定为一个散列数组。

    图表说明

    上图: 位置为1、4、6、9位置产生了冲突,每个位置被分配了2个数据,这时候,我们在冲突位置再建一个散列表,让冲突的值重新分配。

    ​ 位置1:先存入数据1,再存入数据1时发生冲突,则建立新表,并将新表地址存入位置1,然后将数据1和数据81依次存入新表。我们将新元素插入到链表的前端,因为通常情况:新近插入的元素最有可能不久又被访问

    三、代码实现

    Java代码说明https://github.com/dhcao/dataStructuresAndAlgorithm/blob/master/src/chapterFive/SeparateChainingHashTable.java

    package chapterFive;
    
    import java.util.LinkedList;
    import java.util.List;
    
    /**
     * 散列表实现方法之一:分离链接法
     * 分离链接法:在遇到冲突的时候,构建一个链表(linkedList)来存储,原位置存储链表位置。
     *
     * @Author: dhcao
     * @Version: 1.0
     */
    public class SeparateChainingHashTable<T> {
    
    	/**
    	 * desc: 构造函数
    	 *
    	 * @return:
    	 * @auther: dhcao
    	 */
    	public SeparateChainingHashTable() {
    		this(DEFAULT_TABLE_SIZE);
    	}
    
    	public SeparateChainingHashTable(int size) {
    		theLists = new LinkedList[nextPrime(size)];
    
    		// 为表中每一个位置都创建一个表,在冲突时放入数据
    		for (int i = 0; i < theLists.length; i++) {
    			theLists[i] = new LinkedList<>();
    		}
    	}
    
    	/**
    	 * desc: 插入
    	 *
    	 * @auther: dhcao
    	 */
    	public void insert(T t) {
    		List<T> whichList = theLists[myhash(t)];
    
    		if (!whichList.contains(t)) {
    			whichList.add(t);
    		}
    
    		if (++currentSize > theLists.length) {
    			rehash();
    		}
    	}
    
    	/**
    	 * desc: 删除
    	 *
    	 * @auther: dhcao
    	 */
    	public void remove(T t) {
    		List<T> whichList = theLists[myhash(t)];
    		if (whichList.contains(t)) {
    			whichList.remove(t);
    			currentSize--;
    		}
    	}
    
    	/**
    	 * desc: 是否含有
    	 *
    	 * @auther: dhcao
    	 */
    	public boolean contains(T t) {
    
    		// 在散列表中找到散列位置都链表
    		List<T> whichList = theLists[myhash(t)];
    		return whichList.contains(t);
    	}
    
    	/**
    	 * desc: 清空散列表
    	 *
    	 * @auther: dhcao
    	 */
    	public void makeEmpty() {
    
    		// 清空表都每个位置
    		for (int i = 0; i < theLists.length; i++) {
    			theLists[i].clear();
    		}
    
    		// 将当前表都大小置为0
    		currentSize = 0;
    	}
    
    	private static final int DEFAULT_TABLE_SIZE = 101;
    
    	private List<T>[] theLists;
    
    	// 当前大小
    	private int currentSize;
    
    	/**
    	 * desc: 重新构造散列表(在散列表达到最大大小之后)
    	 *
    	 * @auther: dhcao
    	 */
    	private void rehash() {
    		List<T>[] oldLists = theLists;
    
    		theLists = new List[nextPrime(2 * theLists.length)];
    
    		for (int j = 0; j < theLists.length; j++) {
    			theLists[j] = new LinkedList<>();
    		}
    
    		currentSize = 0;
    
    		for (int i = 0; i < oldLists.length; i++) {
    			for (T item : oldLists[i]) {
    				insert(item);
    			}
    		}
    	}
    
    	/**
    	 * desc:获取hash值
    	 *
    	 * @auther: dhcao
    	 */
    	private int myhash(T x) {
    
    		int hashVal = x.hashCode();
    
    		hashVal %= theLists.length;
    
    		if (hashVal < 0) {
    			hashVal += theLists.length;
    		}
    
    		return hashVal;
    	}
    
    	/**
    	 * desc:下一个素数
    	 *
    	 * @auther: dhcao
    	 */
    	private static int nextPrime(int n) {
    
    		boolean state = isPrime(n);
    		while (!state) {
    			state = isPrime(++n);
    		}
    		return n;
    	}
    
    	/**
    	 * desc:是不是素数
    	 *
    	 * @auther: dhcao
    	 */
    	private static boolean isPrime(int n) {
    		if (n == 2 || n == 3) {
    			return true;
    		}
    		if (n % 6 != 1 && n % 6 != 5) {
    			return false;
    		}
    		for (int i = 5; i * i <= n; i += 6) {
    			if (n % i == 0 || n % (i + 2) == 0) {
    				return false;
    			}
    		}
    		return true;
    	}
    
    }
    
    凡你能说的,你说清楚。凡你不能说的,留给沉默!
  • 相关阅读:
    第五届蓝桥杯JavaB组省赛真题
    第五届蓝桥杯JavaB组省赛真题
    第五届蓝桥杯JavaA组省赛真题
    第五届蓝桥杯JavaA组省赛真题
    第五届蓝桥杯JavaA组省赛真题
    第五届蓝桥杯JavaA组省赛真题
    FastReport的交叉表实际使用的一个例子
    成熟的人首先得明白自己是个什么样的人
    ACL 我为什么要发明一个轮子?
    利润就是被存储的,接到指令就可以被使用的劳动时间
  • 原文地址:https://www.cnblogs.com/dhcao/p/10505775.html
Copyright © 2011-2022 走看看