哈希表(概念,冲突的解决,实现哈希桶)

目录

概念

冲突

如何尽量减少冲突?

负载因子

解决冲突的几种方案

冲突严重时的解决办法

哈希表的实现

基本类型哈希桶实现

泛型哈希桶实现

注意!!!


概念

构造出一种存储结构,通过某种函数使元素的存储位置(下标)与它的关键码之间能够建立一一的映射关系,那么在查找时,通过该函数就可以很快找到该元素

哈希函数中使用的转换函数称为哈希(散列)函数,构造出的结构称为哈希表(散列表)

插入元素
根据待插入元素的关键码,通过哈希函数计算出该元素的存储位置,并按照此位置进行存放

搜素元素
通过哈希函数对元素的关键码进行计算,得到的值就是元素的存储位置,按此位置取出元素,若关键码相等则搜索成功

冲突

不同的关键码通过相同的哈希函数进行计算,可能得到相同的存储位置,这种现象就称为哈希冲突(哈希碰撞),冲突无法避免,我们只能减少冲突,并且再发生冲突时,给出解决方案

如何尽量减少冲突?

1.设计合理的哈希函数
2.调节负载因子

合理的哈希函数设计原则:
1.哈希函数的定义域必须包括需要存储的全部关键码,哈希表允许有n个地址时,其值域必须在0到n-1之间
2.哈希函数计算出来的地址要能均匀分布在整个空间中
3.哈希函数要尽量简单

常见哈希函数

1.直接定址法
取关键码的某个线性函数为散列地址:Hash(Key) = A*Key + B 优点:简单,均匀.缺点:需要事先知道关键码的分布情况和使用场景:适合查找比较小且连续的情况

2.除留余数法
设哈希表中允许的地址数为m,取一个不大于m,但最接近于或者等于m的质数p作为除数,按照哈希函数:Hash(key) = key%p(p<=m),将关键码转换为哈希地址

3.平方取中法
对关键码进行平方,取中间的三位作为哈希地址

4.md5

5.还有折叠法,随机数法,数学分析法等

负载因子

哈希表的载荷因子定义为:α = 填入表中的元素个数/哈希表长度

当我们填入哈希表中的数据越多,发生冲突的概率就越大,我们通常把载荷因子控制在0.7-0.8之间,也就是说一个哈希表最多只能存大小0.7-0.8个哈希表长度的数据,当我们存放的数据超过这么多时,我们需要对哈希表进行扩容,注意!!!哈希表括完容后,我们需要遍历哈希表,重新计算哈希表中的每个元素的地址,因为哈希表长变了.

解决冲突的几种方案

闭散列:也叫开放地址法,当发生哈希冲突时,如果哈希表未被装满,说明哈希表中还有空的位置,那么可以把key放到冲突位置的"下一个"空位置去.如何找到下一个空位置

1.线性探测
从发生冲突的位置开始,依次向后探测,直到寻到下一个空位置为止
缺陷:容易导致发生冲突的元素都集中到一起,且采用闭散列处理哈希冲突时,不能随便物理删除表中已有的元素,若直接删除会影响其它元素的搜索

2.二次探测
线性探测的缺陷是产生冲突的数据堆积在一块,这是因为线性探测在找空位置时,是挨个往后逐渐寻找,二次探测为了避免这个问题,找下一个空位置的方法为:H1 = (H0 + i²)%m,其中H1是下一个空位置的位置,i是发生冲突的次数,m是哈希表的长度

开散列(哈希桶)

开散列法:又叫链地址法(开链法),首先对关键码集合用哈希函数计算哈希地址,具有相同地址的关键码归于同一子集合,每一个子集合称为一个桶,每个桶中的元素通过一个单链表连接起来,各链表的表头存储在哈希表中(妙!)

开散列可以认为是把一个在大集合中的搜索问题转化为在小集合中做搜素了

冲突严重时的解决办法

哈希桶可以看作将大集合的搜索问题转换为小集合的搜索问题,如果冲突严重,说明小集合中有很多元素,小集合的搜索性能是不好的,这个时候我们可以将这个小集合搜索问题再进行转换
例如:1.每个桶的背后是另一个哈希表
2.每个桶背后是一颗搜索树,红黑树等

哈希表的实现

基本类型哈希桶实现

import java.util.Arrays;

public class HashBuck {
    static class Node{
        public int key;
        public int val;
        public Node next;
        public Node(int key,int val){
            this.key = key;
            this.val = val;
        }
    }
    public Node[] array;
    public int useSize;
    public HashBuck(){
        this.array = new Node[10];
    }
    public void put(int key,int val){
        int index = key%array.length;
        //遍历index下标数组,如果有相同的key进行替换
        Node cur = array[index];
        while(cur!=null){
            if(cur.key == key){
                cur.val = val;
                return;
            }else{
                cur = cur.next;
            }
        }
        //进行头插法
        Node node = new Node(key,val);
        node.next = array[index];
        array[index] = node;
        useSize++;
        if(laodFactor()){
            resize();
        }
    }
    public int get(int key){
        int index = key%array.length;
        Node cur = array[index];
        while(cur!=null){
            if(cur.key == key){
                return cur.val;
            }else{
                cur = cur.next;
            }
        }
        return -1;
    }
    private void resize(){
        Node[] newArray = new Node[2*array.length];
        for (int i = 0; i < array.length ; i++) {
            Node cur = array[i];
            while(cur!=null){
                Node curNext = cur.next;
                int newIndex = cur.key % newArray.length;
                cur.next =newArray[newIndex];
                newArray[newIndex] = cur;
                cur = curNext;
            }
        }
        array = newArray;
    }
    private boolean laodFactor(){
        return useSize*1.0f/array.length>0.75;
    }
}

泛型哈希桶实现

import java.util.Arrays;
//Hash桶实现,泛型
public class HashBuck2<K,V> {
    static class Node<K,V> {
        public K key;
        public V val;
        public Node<K, V> next;

        public Node(K key, V val) {
            this.key = key;
            this.val = val;
        }
    }
    public HashBuck2(){
        array = new Node[10];
    }
        public Node<K,V>[] array;
        public int useSize;
        public void put(K key,V val){
            int hash = Math.abs(key.hashCode());
            int index = hash%array.length;
            Node<K,V> cur = array[index];
            while(cur!=null){
                if(cur.key.equals(key)){
                    cur.val = val;
                    return;
                }else {
                    cur = cur.next;
                }
            }
            Node<K,V> node = new Node<>(key,val);
            node.next = array[index];
            array[index] = node;
            useSize++;
            resize();
        }
        public V get(K key){
            int hash = Math.abs(key.hashCode());
            int index = hash% array.length;
            Node<K,V> cur = array[index];
            while (cur!=null){
                if(cur.key.equals(key)){
                    return cur.val;
                }
                cur = cur.next;
            }
            return null;
        }
        private void resize(){
            Node<K,V>[] newArray = new Node[2*array.length];
            for (int i = 0; i < array.length; i++) {
                Node<K,V> cur = array[i];
                while (cur!=null){
                    Node<K,V> curNext = cur.next;
                    int hash = Math.abs(cur.key.hashCode());
                    int newIndex = hash%newArray.length;
                    cur.next = newArray[newIndex];
                    newArray[newIndex] = cur;
                    cur = curNext;
                }
            }
            array = newArray;
        }
        private boolean loadFactor(){
            return useSize*1.0f/array.length>0.75;
        }
}

注意!!!

 这里泛型的话,如果传入的是String类型,hashCode()算出来的值可能是一个负数

 注意:当我们要给哈希表中传入自定义类型时,我们需要重写equals方法和hashCode,此时hashCode相当于定位元素在哪个下标,equals方法帮助我们在这个下标的桶中,找到对应的值 
俩个对象的hashCode一样,equlas不一定一样,hashCode一样说明俩个对象在同一下标,同一下标中有很多不同的节点(桶),换句话说,如果一样,那么就不会有冲突的存在了
俩个对象的equals一样,hashCode一定一样,equals一样,说明俩个对象是同一个关键码,我们的hashCode就是根据关键码来进行计算的

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/5985.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

让ChatGpt可以看视频,看文档,帮你总结,并提供示例的github项目(附体验地址)

github地址&#xff1a;https://github.com/madawei2699/myGPTReader 演示 Stay updated with the latest news summaries daily with chatGPT. Use chatGPT to read and provide a summary of any webpage include the video(YouTube). 总之这个玩意有很多&#xff0c;可以…

【K8S系列】深入解析StatefulSet(一)

序言 那些看似不起波澜的日复一日&#xff0c;一定会在某一天让你看见坚持的意义。 文章标记颜色说明&#xff1a; 黄色&#xff1a;重要标题红色&#xff1a;用来标记结论绿色&#xff1a;用来标记一级论点蓝色&#xff1a;用来标记二级论点Kubernetes (k8s) 是一个容器编排平…

java毕业生就业信息管理系统servlet程序

1&#xff0e;系统登录&#xff1a;系统登录是用户访问系统的路口&#xff0c;设计了系统登录界面&#xff0c;包括用户名、密码和验证码&#xff0c;然后对登录进来的用户判断身份信息&#xff0c;判断是管理员用户还是普通用户。 2&#xff0e;系统用户管理&#xff1a;不管是…

第13章_约束

第13章_约束 &#x1f3e0;个人主页&#xff1a;shark-Gao &#x1f9d1;个人简介&#xff1a;大家好&#xff0c;我是shark-Gao&#xff0c;一个想要与大家共同进步的男人&#x1f609;&#x1f609; &#x1f389;目前状况&#xff1a;23届毕业生&#xff0c;目前在某公司…

一次压测遇到的问题和排查过程记录

文章目录问题 & 排查1、cpu使用率过高问题描述问题排查解决方案扩展内容2、504 Gateway Time-out问题描述问题排查解决方案3、限流对压测的影响问题描述问题排查解决方案jmeter相关1、beanShell 动态生成签名2、响应断言3、导出结果树请求和响应文件问题 & 排查 1、cp…

域名批量查询功能常用查询方法教程

一些用户在抱怨&#xff0c;要找到好域名怎么就那么不容易呢&#xff0c;能不能让我批量查下不含0的数字啊&#xff0c;能不能查下不含4的数字啊&#xff0c;能不能查下AABBB这样的域名啊…… 别着急&#xff0c;这就给您支招啦&#xff1a;通过西部数码强大的批量查询功能&am…

活动报名|SOFA 五周年,Live Long and Prosper!

2018 年 4 月 19 日&#xff0c;我们在北京启程&#xff0c;伴随种下希望的种子&#xff0c;举办了 SOFAStack 社区的第一个开放日。转眼来到 2023 年&#xff0c;瑞兔送福又逢春暖花开&#xff0c;怀揣着新的愿景&#xff0c;我们将于 4 月 15 日回到北京庆祝 SOFA 的五岁生日…

服务器磁盘又双叒叕爆满了?被/proc占满?

又双叒叕服务器前言排查分析前言 继上一次文章&#xff1a; MISCONF Redis is configured to save RDB snapshots, but it is currently not able to persist on disk. 通过删除tomcat下的catalina.out文件&#xff0c;解决了磁盘爆满的问题后。今天又双叒叕出现这个问题。。…

万字长文解读「新一代CMDB落地的困境及出路」

2023年3月21日春分时节&#xff0c;优维结合在CMDB技术领域的经验沉淀与洞察能力&#xff0c;梳理金融客户在数据运营中面临的问题和挑战&#xff0c;为了帮助到广大客户建立健全有效的方法参考&#xff0c;全新策划了一档“CMDB数据运营精准化专场公开课”线上直播课程。该系列…

2023-Python实现百度翻译接口调用

目录 &#x1f449;1、目标网址 ​​​​​​​&#x1f449;2、接口分析调试 ​​​​​​​&#x1f449;3、python 代码实现 学习记录&#xff1a;百度翻译 ​​​​​​​&#x1f449;1、目标网址 百度翻译&#xff1a;百度翻译-200种语言互译、沟通全世界&#xff0…

【LeetCode】二叉树的中序遍历(递归,迭代,Morris遍历)

目录 题目要求&#xff1a;给定一个二叉树的根节点 root &#xff0c;返回 它的 中序 遍历 。 方法一&#xff1a;递归 方法二&#xff1a;迭代 思路分析&#xff1a; 复杂度分析 代码展示&#xff1a; 方法三&#xff1a;Morris 遍历 思路分析&#xff1a; 复杂度分析…

Vue3学习笔记(9.1)

Vue.js style&#xff08;内联样式&#xff09; 我们可以在v-bind:style直接设置样式&#xff0c;可以简写:style <!--* Author: RealRoad1083425287qq.com* Date: 2023-04-02 19:41:53* LastEditors: Mei* LastEditTime: 2023-04-03 15:41:44* FilePath: \vscode\Vue3_li…

打气球游戏-第14届蓝桥杯STEMA测评Scratch真题精选

[导读]&#xff1a;超平老师的《Scratch蓝桥杯真题解析100讲》已经全部完成&#xff0c;后续会不定期解读蓝桥杯真题&#xff0c;这是Scratch蓝桥杯真题解析第118讲。 蓝桥杯选拔赛现已更名为STEMA&#xff0c;即STEM 能力测试&#xff0c;是蓝桥杯大赛组委会与美国普林斯顿多…

Java包装类

包装类1. 包装类&#xff08;Wrapper&#xff09;1.1 基本数据类型和包装类之间的转换1.2 String类和包装类&#xff08;基本数据类型&#xff09;之间的转换2. 面试题3.包装类练习1. 包装类&#xff08;Wrapper&#xff09; 继承于java.lang.Number类&#xff1b;实现了java.…

java校园行为分析预警管理系统

目 录 摘 要 II ABSTRACT III 第一章 绪论 1 1.1研究背景 1 1.2选题目的 1 1.3本文研究内容 2 第二章 开发技术介绍 3 2.1开发工具介绍 3 2.2 JAVA技术介绍 3 2.3 MYSQL数据库介绍 4 第三章 系统需求分析 6 3.1可行性分析 6 3.1.1技术…

python之lambdas函数(lambda表达式)

python之lambdas函数&#xff08;lambda表达式&#xff09; lambda函数&#xff0c;也称为lambda表达式。 lambda函数&#xff08;或lambda表达式&#xff09;的语法&#xff1a; lambda arguments: expression 创建一个返回表达式值的匿名函数。其中&#xff1a; lambda 是…

Power Apps从入门到放弃教程

Power Apps从入门到放弃教程前言啥是Power apps文档资料官方文档官方公式文档官方控件文档案例实操添加数据源用户登录登录成功,跳转主界面添加组件提示语言流前言 Hello&#xff01;欢迎各位,当你选择阅读这篇文章时&#xff0c;相信你最近也在学习Power apps&#xff0c;并且…

Debian 达梦数据库 disql工具输入命令 左右移动光标乱码

Debian 达梦数据库 disql工具输入命令 左右移动光标乱码1、下载安装包rlwrap-0.46.12、编译安装rlwrap-0.46.12.1、安装依赖包2.2、编译安装2.3、安装成功3、设置rlwrap系统环境变量4、配置达梦护数据库用户环境变量5、测试效果1、下载安装包rlwrap-0.46.1 https://github.com…

K8s (一) --------- K8s 概述

目录一、kubernetes 基本介绍二、kubernetes 功能和架构1. 概述2. K8s 功能:3. 应用部署架构分类4. K8s 集群架构5. K8s 集群架构节点角色功能一、kubernetes 基本介绍 kubernetes&#xff0c;简称 K8s&#xff0c;是用 8 代替 8 个字符“ubernete”而成的缩写。是一个开源的&…

【数据结构】二叉树<遍历>

【二叉树遍历】|-前序-中序-后序-层序-|<二叉树的遍历>1.前序遍历【递归】2.中序遍历【递归】3.后序遍历【递归】4.层序遍历【非递归】4.1判断是否是完全二叉树<二叉树的遍历> 在学习二叉树遍历之前我们先了解下二叉树的概念。 二叉树是&#xff1a; 1.空树 2.非空…