基于搜索的数据结构——哈希表(进阶)

搜索

1.什么是搜索?
在这里插入图片描述
2.搜索面临的问题?
1)尽可能快的查到结果
2)数据集合还需要适应变化(数据集合的数据是可能实时变化着的)
3.搜索中存在的模型
在这里插入图片描述
3.设计专门的数据结构来解决搜索(查找)问题
在这里插入图片描述

哈希表背后的机制

1.hash表就是利用了数组的访问下标是O(1)的性能,把在大数据集中查找的问题,转化为在多个小数据集中查找。如下图,要在500万个数中查找,数据集明显很大,此时我们可以转换为小数据集,然后根据下面的步骤进行查找
在这里插入图片描述
2、所以接下来要做的就是:
1)拿着key确定应该是哪个小集合的问题:O(1)
首先利用key求出Key对应的hash值,然后利用hash值得到数组中的合法下标
2)根据下标,找到小集合(利用数组下标访问O(1)特性
为了提升空间利用率,数据的个数N是远远大于L的,所以必然会后一个下标处出现多个Key
不同的key对应相同下标的情况叫做哈希冲突

哈希冲突在这里插入图片描述

所以要想降低冲突发生的的概率,就要降低负载因子LF(N/L),但数据N的数量是不能改变的,所以只有通过提升N来降低冲突率,也就是对数组进行扩容,即扩容的目的就是为了降低冲突率。
在这里插入图片描述
3、遇到哈希冲突的解决办法
之前的文章也提到过:哈希表初识 这里再简单描述一下
1)在本数组内解决(开放定址法),事先制定一个原则,如果冲突了,就按照这个规则找下一个下标即可,直到找到可有位置。
2)在数组下标位置处挂一个链表,把所有冲突数据全部放到这个链表中(拉链法),这个方法比较常用,基于key的个数不是很多

HashMap的插入过程实现

在这里插入图片描述
如果一个链表中,插入的结点过多了(在控制了1负载因子的情况下,仍然发生这样的情况,说明1)key的分布严重不均匀了 2)key的hashCode设计的不好
在这里插入图片描述
3、哈希表本质上是把大数据集的查找问题,转化为n多个小数据集的查找问题
1)当小数据集长度不大时,链表足以
2)当小数据集长度太大时,如果解决一个数据集中查找的问题:
哈希表 搜索平衡树(红黑树)
转化为红黑树的阈值时是8,也就是说当某个链表上的数值超过8个时,就转化为红黑树在这里插入图片描述
4、一个自定义的key作为hashMap的key,需要重写hashCode和equals方法,因为过程中需要用到这两个方法
在这里插入图片描述
5、哈希的用处:1)哈希表 2)验证源数据是否是正确的
在这里插入图片描述

HashMap的实现

查找过程

 public V get(K key) {
           //时间复杂度是O(1)
            //1.根据key得到下标
            //1.1求出key的hashcode()
            /**
             * 这里使用了hashCode()
             * 如果要使用自定义的类作为hashMap的k类型
             * 就必须要在类中覆写hashCode()且相同的key的hashCode()相等
             * 否则去发根据相同的key找到唯一的下标
             */
            int hash=key.hashCode();
            //1.2利用hash得到合法的下标
            /**
             * 这个的作用是让hash中的每一位都参与到找下标的过程
             * 使得找到的下标尽可能均匀
             */
            hash=(hash>>>16)^hash;
            /**
             * 前提:这里利用了table.length一定是2的幂次方
             * 利用位运算使得下标大于0并且小于table.length
             */
            int index=hash&(table.length-1);
            //2.使用下标找到链表的头结点的引用
            Entry<K,V> head=table[index];
            //3.在链表中找到包含key的节点,返回节点中的value
            //遍历链表 去查找
            Entry<K,V> node=head;
            while(node!=null){
                /**
                 * 如果使用自定义类作为hashMap的key
                 * 必须覆写equals()方法,保证你认为相同的Key,equals返回值也相同
                 * 否则无法正确找到Value
                 */
                if(key.equals(node.key)) {
                    return node.value;
                }
                node=node.next;
            }
            //遍历结束,没有找到
            return null;
        }

插入过程

  public V put(K key, V value) {
       int hash=key.hashCode();
       hash=(hash>>>16)^hash;
       int index=hash&(table.length-1);
       Entry<K,V> head=table[index];
       Entry<K,V> node=head;
       while(node!=null){
           if(key.equals(node.key)){
               //使用新的value,替换原来的value
               //并且返回原来的value
               V oldValue=node.value;
               node.value=value;
               return oldValue;
           }
           node=node.next;
       }
       //没有找到使用key-value新建节点,并且把节点插入到链表中
        Entry<K,V> entry=new Entry<>(key,value);
       //头插OR尾插 这里使用尾插
        Entry<K,V> la=head;
        if(la==null){
            table[index]=entry;
        }else{
            Entry<K,V> la=head;
            while(la.next!=null){
                la=la.next;
            }
            la.next=entry;
        }
        size++;
        //为了减少冲突率,所以需要考虑降低size/table.length
        //设定一个阈值,当size/table.length高于某个值时,进行扩容
        //所谓的扩容,就是保证size不变,让table。length变大
        //进而使得size/table.length降低,进而降低冲突率
        if(size*1.0/table.length>LOAD_FACTOR){
            resize();
        }

       return null;
    }

扩容过程

/**
     * 所谓的扩容,必须保证数组长度是2的幂次方
     * 因为table.length变化了,所以key对象的下标一定会变化
     * 所以,需要把所有的key重新计算下标重新插入
     */
    private void resize() {
        Entry<K,V>[] newTable=new Entry[table.length*2];
        for(int i=0;i<table.length;i++){
            Entry<K,V> head=table[i];
            Entry<K,V> node=head;
            while(node!=null){
                K key=node.key;
                V value=node.value;
                int hash=key.hashCode();
                hash=(hash>>>16)^hash;
                int index=hash&(newTable.length-1);

                Entry<K,V> entry=new Entry<>(key,value);
                entry.next=newTable[index];
                newTable[index]=entry;

                node=node.next;
            }
        }
        table=newTable;
    }

哈希、压缩与加解密

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值