【数据结构】哈希表的原理及实现

news2024/10/7 3:21:02

1.什么是哈希表

  • 哈希表又称为散列表,它是一种以键值对形式来存储数据的结构,只要输入待查找的key,就可以通过该key寻找到对应的值。对应函数:y = f(key)
  • 通过把关键码映射到表中的对应位置来访问对应信息,来加快查找速度
  • 哈希表用的是数据支持下标随机、访问数据的特性来实现的,所以说哈希表是数组的扩展,是由数组演化而成。
  • 数据储存 可以通过 数组+链表 的方式来实现
  • 哈希表示意图:

在这里插入图片描述

为什么要使用哈希表?

  • 我们知道,在使用数据进行数据查找是,是从头来进行查询的,这样即查找的方式即慢,又不方便。但使用哈希表就可以提高检索速度,减少资源的消耗。

2.哈希表功能实现

哈希表中增加数据put(K key,V value)
根据key获取对应的valueV get(K key)
删除指定key的元素remove(K key)
输出哈希表中所有元素show()

(1)哈希表初始化

在这里插入图片描述

(2)新增哈希表中数据

在这里插入图片描述

(3)修改哈希表中指定key 的value

在这里插入图片描述

(4)删除哈希表中指定key的元素

在这里插入图片描述

(5)遍历输出哈希表中所有数据

在这里插入图片描述

3.哈希表代码实现

(1)整体代码实现

/**
 * 哈希表实现
 * @param <K>
 * @param <V>
 */
public class HashTable<K,V> {

    public static void main(String[] args) {
        HashTable<Integer,String> hashTable = new HashTable<>();
        hashTable.put(1,"李祥");
        hashTable.put(2,"张三");
        hashTable.put(3,"李四");
        System.out.println("新增三个元素 :李祥,张三,李四");
        System.out.println("打印当前哈希表:");
        hashTable.show();
        System.out.println("获取key为3的元素value:"+ hashTable.get(3));
        System.out.println("修改key为3的元素value为 小李 :");
        hashTable.put(3,"小李");
        System.out.println("打印当前哈希表:");
        hashTable.show();
        System.out.println("删除key为2的元素:");
        hashTable.remove(2);
        hashTable.show();

    }

    /**
     * 定义链表数组
     */
    private final LinkedList<K,V>[] linkedArr;

    /**
     * 默认数组长度
     */
    private final static int DEFAULT_LENGTH = 10;

    /**
     * 定义数组的容量
     */
    public int maxSize;

    /**
     * 构造方法初始化
     */
    public HashTable(){
        //定义数组的最大长度
        this.maxSize = DEFAULT_LENGTH;
        //初始化数组
        linkedArr = (LinkedList<K,V>[]) new LinkedList[maxSize];
        //对数组中的每一条链表都要初始化
        for(int i=0;i<maxSize;i++){
            linkedArr[i]=new LinkedList<>();
        }
    }

    /**
     * put 元素
     * @param key
     * @param value
     */
    public void put(K key,V value){
        //key取hashcode根据数组长度取余获取index
        int hashCode = key.hashCode();
        int index =(hashCode % maxSize);
        //把对应的课程 插入到对应的哈希表
        Node<K, V> oldNode = linkedArr[index].get(key);
        //如果当前key 不存在 则表示新增,如果key存在表示替换原有的value
        if(oldNode==null){
            //创建Node节点
            Node<K,V> node = new Node<>(key,value);
            linkedArr[index].add(node);
        }else{
            linkedArr[index].update(key,value);
        }
    }

    /**
     * 根据key值获取value
     * @param key
     * @return
     */
    public V get(K key){
        //获取数组的index
        int hashCode = key.hashCode();
        int index =(hashCode % maxSize);
        //链表中查找key对应的value
        Node<K, V> node = linkedArr[index].get(key);
        return node.value;
    }

    /**
     * 根据key值获取value
     * @param key
     * @return
     */
    public void remove(K key){
        //获取数组的index
        int hashCode = key.hashCode();
        int index =(hashCode % maxSize);
        //链表中查找key对应的value
        linkedArr[index].remove(key);
    }

    /**
     * 输出当前哈希表中的所有元素
     */
    public void show(){
        List<String> showStr = new ArrayList<>();
        for (int i = 0; i < linkedArr.length; i++) {
            List<Node<K, V>> list = linkedArr[i].list();
            for (Node<K, V> node : list) {
                String strNode = "{key="+node.key+",value="+node.value+"}";
                showStr.add(strNode);
            }
        }
        System.out.println("["+String.join(",",showStr)+"]");
    }

    /**
     * 链表存储数据
     * @param <K>
     * @param <V>
     */
    class LinkedList<K,V>{

        /**
         * 定义头节点
         */
        private Node<K,V> head;

        /**
         * 链表中增加数据
         * @param course
         */
        public void add(Node<K,V> data){
            //如果说 head==null  把第一个元素加进去
            if(head==null){
                head=data;
                return;
            }
            // 遍历添加
            Node<K,V> cur=head;
            while (cur.next != null) {
                //跳出循环的条件
                //找到最后一个节点
                cur = cur.next;
            }
            cur.next=data;
        }

        /**
         * 获取链表中全部数据
         */
        public List<Node<K,V>> list(){
            List<Node<K,V>> nodeList = new ArrayList<>();
            if(head==null){
                return nodeList;
            }
            //获取每一项加入到集合中
            Node<K,V> cur=head;
            while (true){
                nodeList.add(cur);
                if(cur.next==null){
                    break;
                }
                cur=cur.next;
            }
            return nodeList;
        }

        /**
         * 根据key 获取对应的node
         * @param key
         * @return
         */
        public Node<K,V> get(K key){
            if(head==null){
                return null;
            }
            //链表有课程 遍历输出 创建一个辅助指针
            Node<K,V> cur=head;
            while (!cur.key.equals(key)) {
                cur = cur.next;
            }
            return cur;
        }

        /**
         * 修改链表中的对应key的值
         * @param key
         * @param value
         */
        public void update(K key, V value) {
            //如果说 head==null,直接返回
            if(head==null){
                return;
            }
            //添加的不是第一门课程 遍历添加
            Node<K,V> cur=head;
            do {
                if(cur.key.equals(key)){
                    cur.value = value;
                    break;
                }
                cur = cur.next;
            }while(cur.next != null);
        }

        /**
         * 删除对应key的node
         * @param key
         * @return
         */
        public void remove(K key) {
            //如果说 head==null,直接返回
            if(head==null){
                return;
            }
            //遍历查找到对应key的node节点
            int x = 0;
            Node<K,V> cur = head;
            while(cur != null) {
                x++;
                if(cur.key.equals(key)){
                    break;
                }
                cur = cur.next;
            }
            //当 cur 为空或这cur.next为空时
            if(cur==null||cur.next==null){
                //判断寻找节点是不是只有一个元素
                if(x == 1){
                    //只有一个元素时,将头节点改成null
                    head = null;
                }else{
                    //当是最后一个元素时,找到当前节点的前一个元素,将前一个元素的next改成null
                    Node<K,V> prev = head;
                    while(prev.next!=null){
                        if (prev.next.key.equals(key)){
                            break;
                        }
                        prev = prev.next;
                    }
                    prev.next=null;
                }
                return;
            }
            cur.key = cur.next.key;
            cur.value = cur.next.value;
            cur.next = cur.next.next;
        }
    }

    class Node<K,V>{

        /**
         * 定义数据
         */
        public V value;
        /**
         * 定义key
         */
        public K key;

        /**
         * 定义下一个节点的
         */
        public Node<K,V> next;

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

(2)代码测试
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

机械设备行业ERP在企业中如何发挥作用?

对机械设备制造企业而言&#xff0c;一方面&#xff0c;大部分销售额都集中在少数几个客户&#xff0c;很难实时了解市场和用户真实需求&#xff0c;订单修改、取消&#xff0c;销售、生产预测不准&#xff0c;原料积压、作废等是常有的事&#xff0c;日积月累给企业造成极大的…

【官方 | 计算机二级Python教程】第一章:程序设计基本方法

【官方 | 计算机二级Python教程】第一章&#xff1a;程序设计基本方法参考书目第一章&#xff1a;程序设计基本方法本章知识导图1.1 程序设计语言1.1.1 程序设计语言概述1.1.2 编译和解释1.2 Python语言概述1.2.1 Python语言的发展1.2.2 Python最小程序1.3 Python开发环境配置1…

iptables端口复用_远程操控

目录 方式一&#xff1a;利用 ICMP 做遥控开关 一、创建端口复用链 二、创建端口复用规则 三、设置开启开关 四、设置关闭开关 五、将发现的数据包转到HTTP_SSH_PORT链上进行处理 六、开启复用 七、关闭复用 方式二&#xff1a;利用tcp数据包中的关键字做遥控开关 一…

【Java】GET 和 POST 请求的区别

GET 和 POST 请求的区别 GET 和 POST请求是最常用的两种请求方法&#xff0c;写了几个Servlet项目&#xff0c;发现这两种请求用的实在是多&#xff0c;给我的感觉就是这两个请求仿佛只有一个名字不同而已。但是通过查询资料发现&#xff0c;里面大有文章。HTTP协议定义的方法…

从0开始学python -18

Python3 元组 Python 的元组与列表类似&#xff0c;不同之处在于元组的元素不能修改。 元组使用小括号 ( )&#xff0c;列表使用方括号 [ ]。 元组创建很简单&#xff0c;只需要在括号中添加元素&#xff0c;并使用逗号隔开即可。 实例(Python 3.0) >>> tup1 (Go…

研发能力加码!维视智造团队入选“科学家+工程师”队伍!

一、维视智造成功入选2023年度秦创原“科学家工程师”队伍近日&#xff0c;陕西省科学技术厅公布了2023年度秦创原“科学家工程师”队伍入选名单&#xff0c;维视智造旗下欣维视觉工程师团队联合西北工业大学马志强副教授团队&#xff0c;申报的“大口径光学元件形性误差检测方…

第9章 Idea集成gitee(码云)

第一节 码云简介 众所周知&#xff0c;GitHub服务器在国外&#xff0c;使用GitHub作为项目托管网站&#xff0c;如果网速不好的话&#xff0c;严重影响使用体验&#xff0c;甚至会出现登录不上的情况。针对这个情况&#xff0c;大家也可以使用国内的项目托管网站-码云。 码云…

golang 协程关闭——谁敢说没踩过坑

Go语言中&#xff0c;协程创建和启动非常简单&#xff0c;但是如何才能正确关闭协程呢&#xff0c;和开车一样&#xff0c;前进总是很容易&#xff0c;但是如何正确的把车停在指定的地方总是不容易的。生产实践中&#xff0c;go常常遇到未能正确关闭协程而影响程序运行的场景&a…

Unity - TextMeshPro

TextMeshPro TextMeshPro 是 Unity 的终极文本解决方案。它是 Unity 的 UI 文本和旧版文本网格的完美替代品。 TextMeshPro&#xff08;也称为 TMP&#xff09;功能强大且易于使用&#xff0c;它使用高级文本渲染技术以及一组自定义着色器&#xff1b;提供显着的视觉质量改进&…

C++分文件编写VS Code和CMakeLists使用详解

目录一、示例代码1.1 主函数main.cpp1.2 子函数源文件1.3 子函数头文件二、VS Code编译2.1 报错2.2解决方法三、CMakeLists编译Windows 10 Ubuntu 20.04 VS Code 一、示例代码 1.1 主函数main.cpp 要用双引号包含子函数的头文件&#xff0c;第二行 #include<iostream&g…

项目经理必备的5种项目管理工具,让你的项目迅速上手

做项目管理是一条漫漫长路&#xff0c;所有的本事&#xff0c;都是靠一个个项目&#xff0c;一点点积累而来的&#xff0c;并不存在“迅速上手”的方法。 一名普通项目经理的成长&#xff0c;都要经过一定时间的修炼&#xff0c;并且要灵活使用项目管理工具&#xff0c;这里给…

跑步的人如何选择耳机、最好的跑步蓝牙耳机排名清单

相信很多人和小编一样&#xff0c;在跑步健身的时候也喜欢听点音乐&#xff0c;特别是节奏感强的音乐能让运动更加有激情。但是如果佩戴传统的有线耳机容易扯到线&#xff0c;在现代化的今天&#xff0c;当然要选择蓝牙耳机。今天就为大家介绍一下跑步用什么蓝牙耳机好&#xf…

看完这篇文章,我再也不用担心线上出现CPU性能问题了(下)

目录平均负载CPU 使用率进程上下文切换补充总结在 《看完这篇文章&#xff0c;我再也不用担心线上出现CPU性能问题了&#xff08;上&#xff09;》中&#xff0c;咸鱼给大家介绍了 CPU 常见的性能指标&#xff0c;当生产环境出现 CPU 性能瓶颈的时候&#xff0c;优先观察这些指…

论文投稿指南——中文核心期刊推荐(食品工业 2)

【前言】 &#x1f680; 想发论文怎么办&#xff1f;手把手教你论文如何投稿&#xff01;那么&#xff0c;首先要搞懂投稿目标——论文期刊 &#x1f384; 在期刊论文的分布中&#xff0c;存在一种普遍现象&#xff1a;即对于某一特定的学科或专业来说&#xff0c;少数期刊所含…

写IB EE(Extended Essay)时最容易犯的五大错误

【第一大忌】用随意找来的文章做Sources&#xff01; 同学们都知道EE写作一定要做好citation&#xff0c;在文中、文末都要列出参考资料。但不是什么文章都”有资格“成为bibliography的一部分&#xff0c;选取质量高的sources是很重要的。那些百度上搜到的作者不详、自己都没有…

教你使用 Petalinux 定制 Linux

测试平台&#xff1a;黑金 Zynq7035 开发板 芯片型号&#xff1a;XC7Z035-2FFG676I 开发环境&#xff1a;Ubuntu 16.04 开发工具&#xff1a;Petalinux 2017.4 Step1 创建 Petalinux 工程 1.1 将 Vivado 工程目录下*.sdk文件夹中的*.hdf文件复制到新建的proj文件夹中 1.2 …

串级PID控制原理-2

按串级控制的基本原理&#xff0c;采用Simulink进行编程&#xff0c;在连续方式下进行仿真。在串级控制中&#xff0c;主调节器采用PI控制&#xff0c;取kp 50&#xff0c;k i5&#xff0c;副调节器采用Р控制&#xff0c;kp 200。外加干扰为正弦信号sin(50t)&#xff0c;通过切…

报表控件Stimulsoft技术答疑:如何在二维码中编码数据?

Stimulsoft Reports是一款报告编写器&#xff0c;主要用于在桌面和Web上从头开始创建任何复杂的报告。可以在大多数平台上轻松实现部署&#xff0c;如ASP.NET, WinForms, .NET Core, JavaScript, WPF, Angular, Blazor, PHP, Java等&#xff0c;在你的应用程序中嵌入报告设计器…

深度学习网络各种激活函数 Sigmoid、Tanh、ReLU、Leaky_ReLU、SiLU、Mish

激活函数的目的就是为网络提供非线性化 梯度消失&#xff1a;梯度为0&#xff0c; 无法反向传播&#xff0c;导致参数得不到更新 梯度饱和&#xff1a;随着数据的变化&#xff0c;梯度没有明显变化 梯度爆炸&#xff1a;梯度越来越大&#xff0c;无法收敛 梯度消失问题&#…

JavaWeb1-计算机是如何工作的?

目录 1.计算机的构成 1.1.计算机二进制 1.2.冯诺依曼体系结构 1.2.1.CPU&#xff08;加工厂&#xff09; 1.2.2.存储器&#xff08;仓库&#xff09; 1.2.3.输⼊设备&#xff08;原材料&#xff09; 1.2.4.输出设备&#xff08;产品&#xff09; PS&#xff1a;关于存…