哈希表(散列表)

通过哈希函数使元素的存储位置与它 的关键码之间能够建立一一映射的关系,在查找时可以很快找到该元素。

哈希表hash table(key,value) 的做法其实很简单,就是把Key通过一个固定的算法函数既所谓的哈希函数转换成一个整型数字,然后就将该数字对数组长度进行取余,取余结果就当作数组的下标,将value存储在以该数字为下标的数组空间里。

1.哈希冲突

就是键(key)经过hash函数得到的结果作为地址去存放当前的键值对,但是却发现该地址已经有人先来了就会产生冲突。这个冲突就是hash冲突了。

2.load factoe 装载因子

已占用桶的个数/桶的总数

当装载因子大于 0.8时就应该扩容。 

由于哈希冲突的存在造成哈希表的增删查时间复杂度只能无限趋近于0(1)

3. 哈希冲突的解决

1.可以把key存放在表中的“下一个“”空位置。即从发生冲突的位置开始,依次向后探测,直到找到空位置为止(线性探测)。

2.实现链式哈希表,从根本上说是由一组链表构成。每个链表都可以看做是一个“桶”,我们将所有的元素通过散列的方式放到具体的不同的桶中。插入元素时,首先将其键传入一个哈希函数(该过程称为哈希键),函数通过散列的方式告知元素属于哪个“桶”,然后在相应的链表头插入元素。查找或删除元素时,用同样的方式先找到元素的“桶”,然后遍历相应的链表,直到发现我们想要的元素。因为每个“桶”都是一个链表,所以链式哈希表并不限制包含元素的个数。然而,如果表变得太大,它的性能将会降低。

3.优点:增删改查O(1)

   缺点:1.占用内存比较大
              2.元素没有任何顺序

4.源代码:

线性探测哈希表

class LinerHashMap<T extends Comparable<T>>{
// 散列表数组
private Entry<T>[] hashTable;
// 被占用的桶的个数
private int usedBucketNum;
// 哈希表的装载因子
private double loadFactor;
// 定义素数表
private static int[] primTable;
// 记录当前使用的素数的下标
private int primIndex; // 类的静态初始化块
static{
primTable = new int[]{3, 7, 23, 47, 97, 127};
} /**
* 构造函数,初始化
*/
public LinerHashMap(){
this.primIndex = 0;
this.hashTable = new Entry[primTable[this.primIndex]];
this.usedBucketNum = 0;
this.loadFactor = 0.75;
} /**
* 增加元素
* @param key
*/
public void put(T key){
// 计算哈希表是否需要扩容
double ret = this.usedBucketNum*1.0 / this.hashTable.length;
if(ret > this.loadFactor){
resize(); // 哈希表的扩容
} // 先计算key应该放的桶的下标
int index = key.hashCode() % this.hashTable.length;
int idx = index;
do{
// 表示是从未使用过的桶
if(this.hashTable[idx] == null){
this.hashTable[idx] = new Entry<>(key, State.USING);
this.usedBucketNum++;
return;
} // 表示使用过的桶
if(this.hashTable[idx].getState() == State.USED){
this.hashTable[idx].setData(key);
this.hashTable[idx].setState(State.USING);
this.usedBucketNum++;
return;
} else {
// 正在使用中的桶,不插入重复元素
if(this.hashTable[idx].getData().compareTo(key) == 0){
return;
}
}
idx = (idx+1)%this.hashTable.length;
} while(idx != index);
} /**
* 哈希表的扩容函数
*/
private void resize() {
Entry<T>[] oldHashTable = this.hashTable;
this.hashTable = new Entry[primTable[++this.primIndex]];
this.usedBucketNum = 0; for (int i = 0; i < oldHashTable.length; i++) {
if(oldHashTable[i] != null
&& oldHashTable[i].getState() == State.USING){
this.put(oldHashTable[i].getData());
this.usedBucketNum++;
}
}
} /**
* 删除元素
* @param key
*/
public void remove(T key){
// 先计算key应该放的桶的下标
int index = key.hashCode() % this.hashTable.length; // 从当前位置开始找元素
int idx = index;
do{
// 如果遍历桶的过程中,发现了从未使用过的桶,直接返回
if(this.hashTable[idx] == null){
return;
}
if(this.hashTable[idx].getState() == State.USING
&& this.hashTable[idx].getData().compareTo(key) == 0){
this.hashTable[idx].setData(null);
this.hashTable[idx].setState(State.USED);
this.usedBucketNum--;
return;
}
idx = (idx+1)%this.hashTable.length;
} while(idx != index);
} /**
* 查询元素 返回key的值,找不到返回null
* HashMap
* @param key
* @return
*/
public T get(T key){
// 先计算key应该放的桶的下标
int index = key.hashCode() % this.hashTable.length; // 从当前位置开始找元素
int idx = index;
do{
// 如果遍历桶的过程中,发现了从未使用过的桶,直接返回
if(this.hashTable[idx] == null){
return null;
}
if(this.hashTable[idx].getState() == State.USING
&& this.hashTable[idx].getData().compareTo(key) == 0){
return key;
}
idx = (idx+1)%this.hashTable.length;
} while(idx != index); return null;
} /**
* 定义桶的状态值
*/
static enum State{
UNUSE,// 桶从未使用过
USED,// 桶被用过了
USING// 桶正在使用中
} /**
* 定义桶的元素类型
* @param <T>
*/
static class Entry<T extends Comparable<T>>{
T data;
State state; public Entry(T data, State state) {
this.data = data;
this.state = state;
} public T getData() {
return data;
} public void setData(T data) {
this.data = data;
} public State getState() {
return state;
} public void setState(State state) {
this.state = state;
}
}
}

链式探测哈希表

public class LinkHashTable<K extends Comparable<K>,V> {

    // 哈希桶
private Entry<K,V>[] table;
// 装载因子 0.75
private double loadFactor;
// 记录已经占用的桶的数量
private int usedBucketSize; /**
* 哈希表初始化
*/
public LinkHashTable(){
this.table = new Entry[3];
this.loadFactor = 0.75;
this.usedBucketSize = 0;
} /**
* 给哈希表增加元素
* @param key
* @param value
*/
public void put(K key, V value){
if(this.usedBucketSize*1.0/this.table.length>this.loadFactor){
this.expand();
}
int idx = key.hashCode() % this.table.length;
if(this.table[idx]==null){
this.table[idx]=new Entry<>(key,value,null);
this.usedBucketSize++;
return;
}
Entry<K,V>entry=this.table[idx];
//判断是否有这个key,如果有直接替换
while (entry!=null){
if(entry.key.compareTo(key)==0){
entry.value=value;
return;
}
entry=entry.next;
}
this.table[idx]=new Entry<>(key,value,this.table[idx]);
} /**
* 在哈希表中查询key是否存在,如果key存在,返回它对应的value值,
* 否则返回null
* @param key
* @return
*/
public V get(K key){
int idx = key.hashCode() % this.table.length;
if(this.table[idx]==null){
return null;
}
Entry<K,V>entry=this.table[idx];
while (entry!=null){
if(entry.key.compareTo(key)==0){
return entry.value;
}
entry=entry.next;
}
return null;
} /**
* 删除哈希表中key值为参数指定的节点
* @param key
*/
public void remove(K key){
int index = key.hashCode() % this.table.length;
if(this.table[index]==null){
return;
}else if(this.table[index].key.compareTo(key)==0){
this.table[index]=this.table[index].next;
return;
}
Entry<K,V>entry=this.table[index];
Entry<K,V>entry1=entry.next;
if(entry1!=null){
if(entry1.key.compareTo(key)==0){
entry.next=entry1.next;
}
entry=entry.next;
entry1=entry1.next;
}
if(this.table[index]==null){
this.usedBucketSize--;
}
} /**
* 哈希表的扩容函数
*/
private void expand() {
Entry<K, V>[] oldTable = this.table;
this.table = new Entry[oldTable.length * 2 + 1];
this.usedBucketSize = 0;
for (int i = 0; i < oldTable.length; i++) {
if (oldTable[i] != null) {
this.put(oldTable[i].key, oldTable[i].value);
}
}
}
/**
* 链式哈希表中节点的类型
* @param <K,V>
*/
static class Entry<K extends Comparable<K>,V> {
K key; // student id
V value; // student
Entry<K, V> next; public Entry(K key, V value, Entry<K, V> next) {
this.key = key;
this.value = value;
this.next = next;
}
}
}

java哈希表(线性探测哈希表。链式哈希表)的更多相关文章

  1. 哈希表---线性探测再散列(hash)

    //哈希表---线性探测再散列 #include <iostream> #include <string> #include <stdio.h> #include ...

  2. 数据结构 链式哈希表(Hash Table)的接口定义与实现分析(完整代码)

    链式哈希表的接口定义 关于哈希表与链式哈希表的描述可以参阅:http://www.cnblogs.com/idreamo/p/7990860.html 链式哈希表的操作与属性有:初始化.销毁.插入元素 ...

  3. DS哈希查找--线性探测再散列

    题目描述 定义哈希函数为H(key) = key%11.输入表长(大于.等于11),输入关键字集合,用线性探测再散列构建哈希表,并查找给定关键字. --程序要求-- 若使用C++只能include一个 ...

  4. [置顶] ※数据结构※→☆线性表结构(queue)☆============优先队列 链式存储结构(queue priority list)(十二)

    优先队列(priority queue) 普通的队列是一种先进先出的数据结构,元素在队列尾追加,而从队列头删除.在优先队列中,元素被赋予优先级.当访问元素时,具有最高优先级的元素最先删除.优先队列具有 ...

  5. 【Java】 大话数据结构(6) 栈的顺序与链式存储

    本文根据<大话数据结构>一书,实现了Java版的栈的顺序存储结构.两栈共享空间.栈的链式存储机构. 栈:限定仅在表尾进行插入和删除操作的线性表. 栈的插入(进栈)和删除(出栈)操作如下图所 ...

  6. [PTA] 数据结构与算法题目集 6-4 链式表的按序号查找 & 6-5 链式表操作集 & 6-6 带头结点的链式表操作集

    带不带头结点的差别就是,在插入和删除操作中,不带头结点的链表需要考虑两种情况:1.插入(删除)在头结点.2.在其他位置. 6.4 //L是给定单链表,函数FindKth要返回链式表的第K个元素.如果该 ...

  7. 算法与数据结构(二) 栈与队列的线性和链式表示(Swift版)

    数据结构中的栈与队列还是经常使用的,栈与队列其实就是线性表的一种应用.因为线性队列分为顺序存储和链式存储,所以栈可以分为链栈和顺序栈,队列也可分为顺序队列和链队列.本篇博客其实就是<数据结构之线 ...

  8. 仅当使用了列的列表,并且 IDENTITY_INSERT 为 ON 时,才能在表中为标识列指定显式值问题

    今天在处理数据库过程中碰到这样的问题在插入一条数据到表中 系统报这样的错误 仅当使用了列的列表,并且 IDENTITY_INSERT 为 ON 时,才能在表中为标识列指定显式值问题 表有一列是自增长的 ...

  9. java链式创建json对象

    我们主要介绍一下:java中如何通过最简单的方式实现链式创建json对象,解决创建json代码臃肿的问题. 1.假设我们要创建一个json对象格式如下: { "code": 0, ...

随机推荐

  1. 关于kettle

    1.点spoon.bat无报错也不运行 可以运行一下spoonDebug.bat 查看一下spoondebug.txt日志 我遇到这个问题时 是给他分配的内存太多了 我的电脑只有2g 给果他给分了20 ...

  2. python面试题之多线程好吗?列举一些让Python代码以并行方式运行的方法

    答案 Python并不支持真正意义上的多线程.Python中提供了多线程包,但是如果你想通过多线程提高代码的速度,使用多线程包并不是个好主意.Python中有一个被称为Global Interpret ...

  3. 【HDOJ】P1007 Quoit Design (最近点对)

    题目意思很简单,意思就是求一个图上最近点对. 具体思想就是二分法,这里就不做介绍,相信大家都会明白的,在这里我说明一下如何进行拼合. 具体证明一下为什么只需要检查6个点 首先,假设当前左侧和右侧的最小 ...

  4. 使用JMeter进行http压力测试

    一.背景及文档目的说明 采用JMeter测试工具对腾讯视频做负载测试,使用 JMeter图形结果和聚合图帮助测试系统在资源超负荷情况下的表现,以发现设计上的错误或验证系统的负载能力并估计系统瓶颈和并发 ...

  5. leetcood学习笔记-257-二叉树的所有路径

    题目描述: 第一次提交:参考113-路径总和② class Solution: def binaryTreePaths(self, root: TreeNode) -> List[str]: r ...

  6. Download Kali Linux

    https://www.kali.org/downloads/

  7. delphi Copy函数 和 Pos函数

    copy(a,b,c); a:就是copy源,就是一个字符串,表示你将要从a里copy一些东西, b:从a中的第b位开始copy(包含第1位), c:copy从第b位开始后的c个字符, 例如: m:= ...

  8. 01退背包——bzoj2287

    退背包就是限制某一件物品不可取的方案数 先做出无限制的方案数,然后对于当前不可取的物品,dp2[j]表示不取改物品情况下,取得体积为j的方案数 有状态方程 dp2[j]=dp1[j]-dp2[j-w[ ...

  9. 26 函数形参值回传问题——C++解决多个return的一般方法

    0 引言 在使用数组和vector作为函数的参数进行参数传递并希望得到值的回传时,由于不知道怎么写数组函数形参的引用形式,一直采用vector的引用形式.但是,刚刚测试了一下,发现数组作为参数本身就是 ...

  10. NX二次开发-比较两个string是否相等

    NX11+VS2013 #include <uf.h> #include <uf_ui.h> UF_initialize(); string A = "ABC&quo ...