【数据结构】哈希表详解以及代码实现

news/2025/2/22 18:58:06

目录

1.来源:

2.哈希函数

1.哈希函数的设计规则

 2.哈希函数的设计思路

3.哈希碰撞

4.解决哈希碰撞的方案

5.负载因子 

3.基于开散列方案的HashMap实现

 1.HashMap类中的属性

2.哈希函数

3.判断当前哈希表中是否含有指定的key值

4.判断当前哈希表中是否包含指定的value值

5.在哈希表中新增,修改元素

6.哈希表扩容

7.删除哈希表中指定的key节点


1.来源:

哈希表来源于数组的随机访问特性

当我们需要查找某个指定元素时,

链表存储:从链表头遍历到链表尾部,时间复杂度为O(n)

用平衡搜索树存储:时间复杂度为O(logn)

用数组存储,如果知道了元素的索引,那么查找元素的时间复杂度就是O(1)

利用数组的随机访问特性来查找元素,这个思想就是哈希表产生的背景

2.哈希函数

1.哈希函数的设计规则

 2.哈希函数的设计思路

3.哈希碰撞

哈希碰撞是指两个不同的值经过哈希函数计算之后得到相同的值,不论是多优秀的哈希函数,哈希碰撞都不可避免,我们只能尽量降低哈希碰撞出现的概率

取一个素数作为负载因子可以有效降低哈希碰撞的几率

4.解决哈希碰撞的方案

1.闭散列(线性探测):发生冲突时,找冲突旁边的空闲位置放入冲突元素

虽然好想好放,但是因此带来的更大问题,难找更难删,所以一般不采用

2.开散列(链地址法):出现冲突,让冲突的位置变成一个链表

就这样做解决了一部分问题,但随着数据的不断增加,哈希冲突的概率不断增大,在当前数组中,某些链表的长度会很长,查找效率依然下降

解决方案:

1.整个数组扩容,对数组内元素进行搬移,原先冲突的元素大概率不会在冲突

2.将长度过于长的链表转为BST

5.负载因子 

3.基于开散列方案的HashMap实现

 1.HashMap类中的属性

   private static class Node{
        int key;
        int value;
        Node next;

        public Node(int key, int value) {
            this.key = key;
            this.value = value;
        }
    }

    private int size;

    private int M;

    private Node[] data;

    //负载因子
    private static final double LOAD_FACTOR = 0.75;

    public MyHashMap(int capacity) {
        this.data = new Node[capacity];
        this.M = capacity;
    }

    public MyHashMap() {
        this(16);
    }

2.哈希函数

    //哈希函数
    private int hash(int key) {
        return key % this.M;
    }

3.判断当前哈希表中是否含有指定的key值

    //判断当前哈希表中是否含有指定的key值
    public boolean containsKey(int key){
        int index = hash(key);
        for (Node i = data[index]; i != null ; i = i.next) {
            if(i.key == key) {
                return true;
            }
        }
        return false;
    }

4.判断当前哈希表中是否包含指定的value值


    //判断当前哈希表中是否包含指定的value值
    public boolean containsValue(int value) {
        for(int i = 0; i < data.length; i++) {
            for(Node x = data[i]; x != null; x = x.next) {
                if(x.value == value) {
                    return true;
                }
            }
        }
        return false;
    }

5.在哈希表中新增,修改元素

    //在哈希表中新增,修改元素
    public int put(int key, int value) {
        int index = hash(key);
        //判断是否存在
        for(Node x = data[index]; x != null; x = x.next) {
            if(x.key == key) {
                int oldValue = x.value;
                x.value = value;
                return oldValue;
            }
        }
        //此时一定不存在
        Node node = new Node(key,value);
        node.next = data[index];
        data[index] = node;
        size++;
        //判断是否需要扩容
        if(size >= data.length * LOAD_FACTOR) {
            resize();
        }
        return -1;
    }

6.哈希表扩容

    private void resize() {
        this.M = data.length << 1;
        Node [] newData = new Node[M];
        for (int i = 0; i < data.length; i++) {
            for (Node x = data[i]; x != null;) {
                Node next = x.next;
                int newIndex = hash(x.key);
                x.next = newData[newIndex];
                newData[newIndex] = x;
                x = next;
            }
        }
        data = newData;
    }

7.删除哈希表中指定的key节点


    //删除哈希表中指定的key节点
    public boolean removeKey(int key) {
        int index = hash(key);
        //判空
        if(data[index] == null) {
            return false;
        }
        //判断是否为头节点
        if(data[index].key == key) {
            data[index] = data[index].next;
            size--;
            return true;
        }
        Node prev = data[index];
        while (prev.next != null) {
            if(prev.next.key == key) {
                prev.next = prev.next.next;
                size--;
                return true;
            }
        }
        return false;
    }


http://www.niftyadmin.cn/n/213251.html

相关文章

【RocketMQ】主从同步实现原理

主从同步的实现逻辑主要在HAService中&#xff0c;在DefaultMessageStore的构造函数中&#xff0c;对HAService进行了实例化&#xff0c;并在start方法中&#xff0c;启动了HAService&#xff1a; public class DefaultMessageStore implements MessageStore {public DefaultM…

产品经理必读|用户研究方法总结①

众所周知&#xff0c;理解用户需求&#xff0c;识别用户痛点&#xff0c;是产品或功能成型之前绕不开的过程。而要获取到用户真实的需求和痛点&#xff0c;唯一的方法就是做用户调研。而用研的方法都有哪些呢&#xff1f;今天我就来给大家分享一下行业中常见的用研方法。 用研的…

PEG衍生物STA-PEG-Aldehyde,Stearic acid PEG CHO,硬脂酸-PEG-醛基

一、试剂基团反应特点&#xff08;Reagent group reaction characteristics&#xff09;&#xff1a; 硬脂酸-PEG醛是一种具有胺反应性醛基的18C脂肪酸-脂质-PEG衍生物。乙醛-聚乙二醇是一种常见的N-末端胺基团化试剂。醛基与N-末端的α-胺之间的反应产生中间体席夫碱。用氢化…

Redis使用教程之jedis客户端sendCommand方法的byte[]入参,防止混淆string的byte与数值byte的区别

1.背景 在使用jedis客户端操作redis数据的过程中&#xff0c;发现了一个通用的方法sendCommand&#xff0c;封装了redis的所有命令&#xff0c;代码路径&#xff1a;redis.clients.jedis.BinaryJedis#sendCommand(redis.clients.jedis.commands.ProtocolCommand, byte[]...)&a…

网络的三层架构与VLAN

网络的三层架构 --- 园区网搭建的建议方案 园区 --- 工厂&#xff0c;政府机关&#xff0c;商场&#xff0c;写字楼&#xff0c;校园&#xff0c;公园等&#xff0c;这些公共场所为了实现数据的互通而搭建的网络称为园区网。 接入层 --- 有接入层交换机&#xff08;一般就是二层…

AI产品、技术应用合集

当下&#xff0c;AI技术正以惊人的速度改变着我们的生活和工作方式&#xff0c;其在各个领域中的应用也越来越广泛。但是&#xff0c;对于大多数人来说&#xff0c;深入了解AI技术及其应用可能是一件困难的事情。如果您想对AI技术有更深入的了解&#xff0c;或者想了解如何应用…

生产环境CPU飙高

出现CPU飙高时操作 出现cpu飙高时使用先试用top命令查看进程&#xff0c;确定是java进程还是mysql 找到进程号 一、如果是mysql进程 1、那么使用mysql终端或者数据库链接工具执行如下sql语句查看正在执行的sql&#xff1a;show processlist 2、查询慢sql&#xff0c;截取慢…

C# NOPI导出Excel 设置计算公式

说明 现需要导出Excel&#xff0c;统计每个人的工作量&#xff0c;计算方式为&#xff1a;项目分值*项目数量 效果 c#代码 //创建工作簿 MemoryStream MStream new MemoryStream();XSSFWorkbook WBook new XSSFWorkbook(); //创建sheet ISheet sheet WBook.CreateSheet(D…