• Java 哈希表


    目录

    1.概念

    1.1 原理

    1.2 哈希函数

    1.3 冲突-解决-负载因子

    2.哈希表的实现

    2.1 定义节点类以及字段

    2.2 新增元素

    2.3 获取与key对应的value值

    2.4 总代码


    1.概念

    1.1 原理

    哈希表是一种时间复杂度只有O(1)的查找数据结构

    原理(个人理解):取出一块内存,将插入的数据按照某种函数(哈希函数)去计算,得到一个内存地址,然后将数据存放到该地址,再取出时,可以根据这个函数直接找到该地址的数据

    哈希表的难点主要为以下两点:

    (1)设计哈希函数

    (2)解决哈希冲突

    1.2 哈希函数

    一个好的哈希表,一定要对应一个好的哈希函数.

    哈希函数的设置主要遵循以下几个条件:

    (1)函数的结果要在可取空间范围内,并且计算的结果可以取到空间内的任意一块地址

    (2)哈希函数计算出来的地址应均匀分布在空间内

    常用的哈希函数:

    (1)直接定制法: 比如 hash = A*key + B

    (2)除留余数法:比如 hash = key % M(M要小于最大可取空间)

    1.3 冲突-解决-负载因子

    无论哈希函数设计的多么巧妙,发生冲突也是不可避免的(同一块地址,有两个数据符合),这个时候就要去解决冲突.

    解决冲突的方法主要为闭散列开散列

    闭散列的方法为:

    将冲突的元素放在其解析地址的后一个地址,或者通过一个函数去进行二次计算,相对于开散列比较简单

    我们这里主要描述 开散列 也是java库中的使用方法

    开散列(也叫链地址法):

    将每块地址都设置成一个链表,当发生冲突时,将其进行 头插或者尾插 用来解决冲突问题

    但是这样会导致效率的下降,所以需要不断的去扩容调节数组的容量(用数组实现的哈希表)

    我们设置一个负载因子n,n = 存储数据个数/数组长度

    假设我们设置的负载因子峰值为0.75,那么当实际的计算结果高于这个峰值时就要进行扩容操作,降低冲突发生的概率

    2.哈希表的实现

    2.1 定义节点类以及字段

    1. private static class Node {
    2. private int key;
    3. private int value;
    4. Node next;
    5. public Node(int key, int value) {
    6. this.key = key;
    7. this.value = value;
    8. }
    9. }
    10. private Node[] array;
    11. private int size; // 当前的数据个数
    12. private static final double LOAD_FACTOR = 0.75;//负载因子最大值
    13. private static final int DEFAULT_SIZE = 8;//默认桶的大小
    14. public HashBucket() {
    15. array = new Node[DEFAULT_SIZE];
    16. }

    2.2 新增元素

    这里使用的哈希函数方法为除留余数法.

    新增/更新:

    首先通过哈希函数找到与插入元素相匹配的链表

    然后查找该链表中是否有与插入的节点的key相同的节点,如果有则只更新该节点的values值

    若没有相同的key,则进行下一步,将新节点用头插法插入该链表

    最后对哈希表进行负载因子的判断,如果表中的负载因子大于设置的最大值,则对链表进行扩容.

    扩容:

    首先创建一个新的节点数组,长度为原数组的2倍

    然后将原数组的每个节点,重新插入新数组

    最后将新数组赋给原数组

    1. //新增
    2. public void put(int key, int value) {
    3. int index = key % array.length;
    4. Node cur = array[index];
    5. while(cur != null) {
    6. if(cur.key == key) {
    7. cur.value = value;
    8. return;
    9. }
    10. cur = cur.next;
    11. }
    12. Node node = new Node(key,value);
    13. node.next = array[index];
    14. array[index] = node;
    15. size++;
    16. if(loadFactor() >= 0.75) {
    17. resize();
    18. }
    19. }
    20. //判断负载因子是否 达到/超过 最大值
    21. private double loadFactor() {
    22. return size * 1.0 / array.length;
    23. }
    24. //扩容
    25. private void resize() {
    26. Node[] newArray = new Node[array.length * 2];
    27. for(int i = 0;i < array.length;i++) {
    28. Node cur = array[i];
    29. while(cur != null) {
    30. Node curNext = cur.next;
    31. int index = cur.key % newArray.length;
    32. cur.next = newArray[index];
    33. newArray[index] = cur;
    34. cur = curNext;
    35. }
    36. }
    37. array = newArray;
    38. }

    2.3 获取与key对应的value值

    首先用哈希函数对key进行判断,找到相对应的链表

    然后寻找与key相对应的节点,最后返回value

    如果没有则返回-1

    1. public int get(int key) {
    2. int index = key % array.length;
    3. Node cur = array[index];
    4. while(cur != null) {
    5. if(cur.key == key) {
    6. return cur.value;
    7. }
    8. cur = cur.next;
    9. }
    10. return -1;
    11. }

    2.4 总代码

    1. public class HashBucket {
    2. private static class Node {
    3. private int key;
    4. private int value;
    5. Node next;
    6. public Node(int key, int value) {
    7. this.key = key;
    8. this.value = value;
    9. }
    10. }
    11. private Node[] array;
    12. private int size; // 当前的数据个数
    13. private static final double LOAD_FACTOR = 0.75;
    14. private static final int DEFAULT_SIZE = 8;//默认桶的大小
    15. public HashBucket() {
    16. array = new Node[DEFAULT_SIZE];
    17. }
    18. public void put(int key, int value) {
    19. int index = key % array.length;
    20. Node cur = array[index];
    21. while(cur != null) {
    22. if(cur.key == key) {
    23. cur.value = value;
    24. return;
    25. }
    26. cur = cur.next;
    27. }
    28. Node node = new Node(key,value);
    29. node.next = array[index];
    30. array[index] = node;
    31. size++;
    32. if(loadFactor() >= 0.75) {
    33. resize();
    34. }
    35. }
    36. //扩容
    37. private void resize() {
    38. Node[] newArray = new Node[array.length * 2];
    39. for(int i = 0;i < array.length;i++) {
    40. Node cur = array[i];
    41. while(cur != null) {
    42. Node curNext = cur.next;
    43. int index = cur.key % newArray.length;
    44. cur.next = newArray[index];
    45. newArray[index] = cur;
    46. cur = curNext;
    47. }
    48. }
    49. array = newArray;
    50. }
    51. private double loadFactor() {
    52. return size * 1.0 / array.length;
    53. }
    54. public int get(int key) {
    55. int index = key % array.length;
    56. Node cur = array[index];
    57. while(cur != null) {
    58. if(cur.key == key) {
    59. return cur.value;
    60. }
    61. cur = cur.next;
    62. }
    63. return -1;
    64. }
    65. }

  • 相关阅读:
    VUE element Tree 没有子级时隐藏展开图标
    【python】python中字典的用法记录
    Week2:包含 min 函数的栈
    Java学习--File
    Mac 下如何查看 Homebrew 安装的软件位置
    P1~P2 模板起源
    融合柯西变异和自适应莱维飞行的布谷鸟优化算法,改进布谷鸟,MATLAB代码
    【尚硅谷 Vue学习笔记】p31列表过滤 p32列表排序
    苹果对高通说:我4.45亿美元买下一个新园区,可能计划加快基带芯片自研
    【JavaWeb】XML
  • 原文地址:https://blog.csdn.net/m0_64318128/article/details/127777915