数据结构之哈希表

目录

1.内部类

1.1实例内部类

​ 1.2内部类的应用场景

 1.3静态内部类

 1.4匿名内部类

 2.哈希表

2.1避免哈希冲突

2.1.1直接定制法

2.1.2除留余数法

2.2调解负载因子

3.解决哈希冲突方法


1.内部类

1.1实例内部类

如果非要定义一个静态变量在内部类中,则要将其变为静态常量即:

        public static final int data6 = 6;

那么我们如何让来实例内部类对象呢?

 1.2内部类的应用场景

内部类一般在链表的应用比较多,比如:

class MyLinkedList {
    class Node{
        
    }
}

通过后台我们可以看见,内部类的字节码文件是这样的:

 那么如果实例内部类当中和外部类有同名的成员变量,那么如何在实例内部类中访问?

即:

 因此实例内部类中一般包含两个this,一个是外部类的this,一个是内部类的this

 1.3静态内部类

如何实例静态内部类对象:

 如何访问外部类的普通的成员变量

这里并不能直接访问

正确的访问格式:

 

 1.4匿名内部类

这个匿名内部类就像我们之前学过的构建一个大堆或者小堆一样:

PriorityQueue<Integer> priorityQueue = new PriorityQueue<>(new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return 0;
            }
        });

 2.哈希表

        假设我们有这样一种搜索方法,在一个数组中可以不经过任何的比较,直接找到这个元素。如果构造一种存储结构,通过某种函 数(hashFunc)使元素的存储位置与它的关键码之间能够建立一一映射的关系,那么在查找时通过该函数可以很快 找到该元素。这个函数就叫做哈希函数。

哈希表的大致过程:

 可是当我们有14这个值应该怎么办呢?14 / 10 = 4,和之前的4重复,那么我们应该怎么放置呢

此时不同的关键字通过哈希函数,有可能找到相同的位置,此时的情况我们称为哈希冲突。

2.1避免哈希冲突

2.1.1直接定制法

       取关键字的某个线性函数为散列地址:Hash(Key)= A*Key + B 优点:简单、均匀 缺点:需要事先知道关 键字的分布情况 使用场景:适合查找比较小且连续的情况 。

2.1.2除留余数法

       设散列表中允许的地址数为m,取一个不大于m,但最接近或者等于m的质数p作为除数,按照哈希函数: Hash(key) = key% p(p<=m),将关键码转换成哈希地址。

2.2调解负载因子

负载因子 = 存储散列表的元素的个数  /  散列表的长度

因为负载因子在一定程度上和冲突率成正比,因此我们降低负载因子即(增加散列表的长度)可以降低冲突率。

以上就是我们避免哈希冲突的两种方法,那么冲突发生了,我们该如何解决呢?

3.解决哈希冲突方法

        开散列 / 哈希桶也叫链地址法,首先对关键码集合用散列函数计算散列地址,具有相同地址的关键码归于同一子 集合,每一个子集合称为一个桶,各个桶中的元素通过一个单链表链接起来,各链表的头结点存储在哈希表中。

代码展示:

public class HashBuck {
    static class Node{
        public int key;
        public int val;
        public Node next;

        public Node(int key,int val){
            this.key = key;
            this.val = val;
        }
    }
    public Node[] array;
    public int usedSize;
    public static final double DEFAULT_LOAD_FACTOR = 0.75;//负载因子
    public HashBuck(){
        this.array = new Node[10];
    }

    /**
     *put函数
     * @param key
     * @param val
     */
    public void put(int key,int val){
      //1.找到key所在的位置
        int index = key % this.array.length;

        //2.遍历这个下标的链表,看是不是有相同的val值,有的话要更新val值
        Node cur = array[index];
        while (cur != null){
            if(cur.key == key){
                cur.val = val;//更新val值
                return;
            }
            cur = cur.next;
        }
        //3.没有key这个元素,头插法
        Node node = new Node(key,val);
        node.next = array[index];
        array[index] = node;
        this.usedSize++;
        //4.插入元素成功之后,检查当前散列表的负载因子
        if(loadFactor() >= DEFAULT_LOAD_FACTOR){
           //扩容
            resize();
        }
    }
    //扩容
    private void resize(){
        //如果数组进行扩容,那么数组里面的每个链表的每个元素都要进行重新哈希
      Node[] newArray = new Node[array.length * 2];
        for (int i = 0; i <this.array.length ; i++) {
            Node cur = array[i];
            while (cur != null){
                int index = cur.key % newArray.length;//获取新的下标
                 //把cur这个节点,以头插的形式插入到新的数组对应下标的链表中
                Node curNext = cur.next;//用curNext来保存cur的下一个节点地址
                cur.next = newArray[index];//先绑定后面
                newArray[index] = cur;//再绑定前面
                cur = curNext;//cur继续向下遍历
            }
        }
        array = newArray;
    }
    private double loadFactor(){
        return 1.0 * usedSize / array.length;
    }

    /**
     *根据key获取val值
     * @param key
     * @return
     */
    public int get(int key){
        //1.找到key所在的位置
        int index = key % this.array.length;
        //2.遍历这个下标的链表,看是不是有相同的val值,有的话要更新val值
        Node cur = array[index];
        while (cur != null){
            if(cur.key == key){
                return cur.val;
            }
            cur = cur.next;
        }
        return -1;
    }

 

 可是如果数组里面存储得不是整数或者类型是引用类型得数据,怎么办呢?

假设接下来得key是一个person,如果身份证号一样,我们认为是同一个人,hashcode函数是将不规则的数变为整数:

那么在这个代码里面,hashcode 就是帮助我们找到index下标,然后用equals方法来比较key值是否相同,因此,以后在HashMap中放入自定义的类,那么hashcode和equals方法一定要重写

class Person{
    public String ID;

    public Person(String ID) {
        this.ID = ID;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return Objects.equals(ID, person.ID);
    }

    @Override
    public int hashCode() {
        return Objects.hash(ID);
    }

    @Override
    public String toString() {
        return "Person{" +
                "ID='" + ID + '\'' +
                '}';
    }
}
public class HashBuck2<K,V> {

    static class Node<K,V>{
        public K key;
        public V val;
        public Node<K,V> next;

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

    }
    public Node<K,V>[] array = (Node<K, V>[]) new Node[10];
    public int usedSize;

    public void put(K key,V val){
        int hash = key.hashCode();
        int index = hash % array.length;
        Node<K,V> cur = array[index];
        while (cur != null){
            if(cur.key.equals(key)){
                cur.val = val;//更新val值
                return;
            }
            cur = cur.next;
        }
        Node<K,V> node = new Node<>(key,val);
        node.next = array[index];
        array[index] = node;
        this.usedSize++;
    }
    public V get(K key){
        int hash = key.hashCode();
        int index = hash % array.length;
        Node<K,V> cur = array[index];
        while (cur != null){
            if(cur.key.equals(key)){
                return cur.val;
            }
            cur = cur.next;
        }
        return null;
    }

题1:

hashcode一样,equals一定一样吗?

答案是不一定一样,因为hashcode确定的是哈希的位置,一个位置可能有多个key值。

equals一样,hashcode一定一样吗?

答案是一定一样。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值