哈希表(散列表)

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

哈希表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. 【Linux】- CentOS安装docker及docker-compose

    1.安装docker,命令如下: -- 把yum包更新到最新 yum update -- 安装需要的软件包, yum-util 提供yum-config-manager功能,另外两个是devicema ...

  2. MFS分布式文件系统【4】客户端的挂载MFS存储空间

    挂载基于MooseFS 分布式文件,客户端主机必须安装FUSE软件包(FUSE版本号至少2.6,推荐版本号大于2.7的fuse) [root@master ~]# rpm -qa|grep fuse ...

  3. log4j.properties的详细配置

    log4j.properties的详细配置 log4j.properties的maven配置 <dependency> <groupId>org.scala-lang</ ...

  4. MJExtension常用方法

    一.MJExtension第三方框架 我们在iOS开发过程中,我们常常需要将字典数据(也就是JSON数据)与Model模型之间的转化,例如网络请求返回的微博数据.等等,如果我们自己全部手动去创建模型并 ...

  5. 2018-2-13-win10-uwp-入门

    title author date CreateTime categories win10 uwp 入门 lindexi 2018-2-13 17:23:3 +0800 2018-2-13 17:23 ...

  6. vue实现curd功能

    一.实现效果 二.实现 (一)实现增加用户功能 Vuserlist组件中 <template> <div class="panel panel-default"& ...

  7. js循环给li绑定事件实现和弹出对应的索引

    原文:http://www.cnblogs.com/wuchuanlong/p/5945286.html 方法一,动态添加click事件,并添加属性 var itemli = document.get ...

  8. idea bug解决

    1.编译时错误:软件包 javax.servlet.http 不存在import javax.servlet.http.HttpServletRequest 解决办法:把servlet-api.jar ...

  9. 在sublime上安装markdown插件(win10)

    1.markdown插件安装 --ctrl+shift+p --在命令框中选中 package control:install package 选中它  按回车 --在命令框中输入 markdown, ...

  10. 单独编译和使用webrtc音频回声消除模块(附完整源码+测试音频文件)

    单独编译和使用webrtc音频降噪模块(附完整源码+测试音频文件) 单独编译和使用webrtc音频增益模块(附完整源码+测试音频文件) 说实话很不想写这篇文章,因为这和我一贯推崇的最好全部编译并使用w ...