当前位置: 首页 > ai >正文

数据结构学习篇——哈希

哈希的概念与结构

  • 若关键字为k,则其值存放在f(k)的存储位置上。由此,不需比较便可直接取得所查记录。称这个对应关系f为散列函数,按这个思想建立的表为散列表。
  • 对不同的关键字可能得到同一散列地址,即 k 1 ≠ k 2 k1≠k2 k1=k2,而 f ( k 1 ) = f ( k 2 ) f(k1)=f(k2) f(k1)=f(k2),这种现象称为冲突(英语:Colision)。具有相同函数值的关键字对该散列函数来说称做同义词。综上所述,根据散列函数 f ( k ) f(k) f(k)和处理冲突的方法将一组关键字映射到一个有限的连续的地址集(区间)上,并以关键字在地址集中的“像”作为记录在表中的存储位置,这种表便称为散列表,这一映射过程称为散列造表或散列,所得的存储位置称散列地址。
  • 在这里插入图片描述

哈希表的两个关键

  • 散列函数
    直接定址法
    数字分析法
    平方取中法
    折叠法
    随机数法
    除留余数法
  • 冲突解决
    开放定址法
    拉链法
    双散列
    再散列

实现一个HashMap

思路:实现HashMap首先确定结构为数组+链表,桶排序用的就是这个结构。其次保存数据时需要先对桶的下标做一个哈希运算,这里采用除留余数法。然后遇到哈希冲突时,采用了拉链法形成一个列表结构依次往下新增。整体来说,里面基于链表的处理比较多。

public class MyHashMap<K,V> implements IMap<K,V>{Node<K,V>[] buckets;int capacity;int size;public MyHashMap() {this.capacity = 16;this.buckets = new Node[capacity];this.size = 0;}@Overridepublic void clear() {for (int i = 0; i < buckets.length; i++) {buckets[i] = null;}}@Overridepublic boolean containsKey(K key) {int index = hash(key);if (buckets[index] == null) {return false;} else {Node<K, V> p = buckets[index];//相当于在链表中找keywhile (p != null) {K k1 = p.key;//借用java机制,hashcode和equals都来自于Object,用户可以改写这两个方法——制定对象相等的规则if (k1 == key || (k1.hashCode() == key.hashCode() && k1.equals(key))) {return true;}p = p.next;}}return false;}@Overridepublic boolean containsValue(V value) {for (int i = 0; i < buckets.length; i++) {Node<K, V> p = buckets[i];while (p != null) {if (Objects.equals(p.value, value))return true;p = p.next;}}return false;}@Overridepublic V get(K key) {int index = hash(key);if (buckets[index] != null){Node<K,V> p = buckets[index];while (p != null){K pkey = p.key;if (key == pkey || (key != null && pkey != null &&key.hashCode() == pkey.hashCode() && Objects.equals(key,pkey))){return p.value;}if (p.next == null){break;}p = p.next;}}return null;}@Overridepublic boolean isEmpty() {return size == 0;}@Overridepublic void put(K key, V value) {int index = hash(key);Node<K, V> node = new Node<>(key, value);if (buckets[index] == null){buckets[index] = node;}else {Node<K,V> p = buckets[index];while (p != null){K pkey = p.key;if (key == pkey || (key != null && pkey != null &&key.hashCode() == pkey.hashCode() && Objects.equals(key,pkey))){p.value = node.value;break;}if (p.next == null){p.next = node;size++;break;}p = p.next;}}}private int hash(K key) {if (key == null) return 0; // 或者根据设计决定如何处理 null 键//位运算正数return  (key.hashCode() & 0x7FFFFFFF) % capacity;}@Overridepublic void putAll(IMap<? extends K, ? extends V> map) {}@Overridepublic V remove(K key) {int index = hash(key);if (buckets[index] != null){Node<K,V> p = buckets[index];Node<K,V> pre = p;while (p != null){K pkey = p.key;if (key == pkey || (key != null && pkey != null &&key.hashCode() == pkey.hashCode() && Objects.equals(key,pkey))){//移除if (p == pre) {buckets[index] = pre.next;} else {pre.next = p.next;}size--;return p.value;}pre = p;p = p.next;}}return null;}@Overridepublic int size() {return size;}@Overridepublic V[] values() {return null;}private class MapInterator implements Iterator<Node> {int i = 0;Node p = buckets[0];@Overridepublic boolean hasNext() {while (this.i < capacity && p == null) {this.i++;if (this.i == capacity)p = null;elsep = buckets[this.i];}//i是一个非空的桶,p是链表头return p != null;}@Overridepublic Node next() {Node res = p;p = p.next;return res;}}public class Node<K,V>{K key;V value;Node next;public Node(K key, V value) {this.key = key;this.value = value;}@Overridepublic String toString(){return "BSTNode{" + "key=" + key + ", value=" + value + '}';}}@Overridepublic Iterator<Node> iterator() {return new MapInterator();}
}

以上实现基于固定长度的数组,没有实现扩容。以下为JAVA对HashMap的优化:

  1. HashMap的负载因子为0.75,桶中元素超过这个值时会进行扩容,且元素重新计算哈希值。
  2. 链表元素超过8个会变成红黑树。
  3. 哈希函数的优化,key.hashCode()是一个32位的int值,做了位运算后再取模。

常见的哈希算法

常见的如:取模、加每个字符、位运算二进制、乘素数等。目的都是让桶中的值分散更均匀,尽量避免冲突影响性能

public class HashFunctions {/*取余法*/static int hash1(Object x, int prime) {return x.hashCode() % prime;}/*加法*/static int additiveHash(Object key, int prime) {String objStr = key.toString();int hash = objStr.length(), i = 0;//遍历每个字符for (; i < objStr.length(); i++)hash += objStr.charAt(i);return (hash % prime);}/*利用位运算*/static int rotatingHash(String key, int prime) {int hash = key.length(), i = 0;for (; i < key.length(); ++i)hash = (hash << 4) ^ (hash >> 28) ^ key.charAt(i);return (hash % prime);}/*乘法*/static long bernstein(String key, int prime) {long h = 0;long seed = 31;//素数for (int i = 0; i != key.length(); ++i) {h = seed * h + key.charAt(i);}return h % prime;}
}
http://www.xdnf.cn/news/3204.html

相关文章:

  • 第六章 进阶07 莹姐做产品
  • 瑞昱点屏芯片RTD2785T带旋转功能
  • select,poll,epoll区别联系
  • Verilog仿真模块--真随机数生成器
  • 【dify—5】Dify关联Ollama
  • GD32F407单片机开发入门(十九)DMA详解及ADC-DMA方式采集含源码
  • 3d-bat安装
  • 智能体-CyberTask Orchestrator设计概要(V4.1超长版)
  • Kubernetes 与 Service Mesh 的集成
  • 黑苹果显卡免驱教程:蓝宝石RX580刷成RX570详细步骤(附工具与注意事项)
  • 2025华东杯A/B/C题解题思路+可运行代码参考
  • 关于我的算法文章学习路线
  • 虚实结合赋能嵌入式教育:基于嵌入式仿真实验教学平台的智能门禁系统实验深度解析
  • 巧用虚拟现实技术,解锁模拟训练高效密码
  • 关于杰理ac791切换版本, git clone下来仍然是最新版本问题
  • 分享一款免费的AI IDE Trae,全新支持DeepSeek R1/V3、豆包大模型1.5自由切换,更可自定义专属AI模型
  • Python 刷题记录(持续更新)
  • Inno Setup 打包工具使用手册
  • Kubernetes Service 详解
  • 耘想WinNAS:企业级NAS解决方案的终极选择
  • sora 文本生图能力体验
  • 柔性生产是什么?怎样能实现柔性生产?
  • Java Class类文件结构
  • sql server 2008 r2 下载安装
  • 【前缀和】二维前缀和(模板题)
  • 动态规划降低空间复杂度例题及简化
  • Android Studio下载安装教程
  • pybind11 使用指南+示例
  • kibana重建es索引
  • 【Python学习路线】零基础到项目实战