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

本地南昌网站建设女教师遭网课入侵直播录屏曝光se

本地南昌网站建设,女教师遭网课入侵直播录屏曝光se,建设公司设计公司网站,河北购物网站开发公司哈希表 1.哈希表 哈希散列(散列表):不经过任何比较,一次直接从表中得到要搜索的元素,时间复杂度是O(1)。哈希函数的设置hash(key) key % capacity,capacity是存储元素底层空间总的大小。 2.哈希冲突 …

哈希表

1.哈希表 

哈希散列(散列表):不经过任何比较,一次直接从表中得到要搜索的元素,时间复杂度是O(1)。哈希函数的设置hash(key) = key % capacity,capacity是存储元素底层空间总的大小。

2.哈希冲突 

当数据集合中可能存在多个数据都被插在一块区域,如上面例题23和3取模后都放在了数组下标3的位置,这是就存在冲突,也就被称为哈希冲突。 冲突的解决方法:避免冲突、解决冲突。

3.避免冲突

冲突是难免的,我们需要做的就是减少冲突。引起哈希冲突的可能原因就是哈希函数设计不合理: 

  1. 哈希函数的定义域必须包括需要存储的全部关键码,简而言之就是,如果数据集合中有m个元素,数组的值域就必须在0~m-1之间
  2. 哈希函数计算出来的地址能均匀分布在整个空间中
  3. 哈希函数要比较简单一点
常见的哈希函数 

1.直接定制法(常用)  

在使用这个方法时需要知道关键自的分布情况(需要找差距比较小且连续的情况),散列地址为hash(key) = A*key+B。大概就是找其数据集合中最小的为B,然后再求A。

这个方法对应的面试题:387. 字符串中的第一个唯一字符 - 力扣(LeetCode)

        这题的大致做法就是先设置一个存放26个字母的数组,然后每遍历一个字符都在数组对应的下标中加1,遍历完后再遍历一遍,如果返现字符中数组对应的值为1那就返回该下标,如果遍历完这个字符串都没有只出现一次的字符那就返回-1。

public int firstUniqChar(String s) {int[] ch = new int[26];for (int i = 0; i < s.length(); i++){ch[s.charAt(i)-'a']++;}for (int i = 0; i < s.length(); i++){if (ch[s.charAt(i)-'a'] == 1){return i;}}return -1;
}  

2.除留余数法(常用)

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

3.平方取中法(了解)

假设关键字为1234,对它平方就是1522756,抽取中间的3位227作为哈希地址; 再比如关键字为4321,对它平方就是18671041,抽取中间的3位671(或710)作为哈希地址 平方取中法比较适合:不知道关键字的分布,而位数又不是很大的情况。

负载因子

随着负载因子的增加,冲突率也随着升高,所以想要降低冲突率就可以通过降低负载因子来实现。

 4.解决哈希冲突

  • 闭散列

线性探测:从发生冲突的位置开始,一次向后探测,知道找到下一个空位置。缺点就是:当该下标的数被删除后,再找与它冲突的数时可能会显示不存在。

插入:通过哈希函数获取待插入元素再哈希表中的位置,如果该位置中没有元素则直接插入新的元素即可,但是如果该位置有元素发生了哈希冲突,就需要使用下一个空位置插入新元素。

二次探测: 二次探测就避免了线性探测所出现的问题,它可以较快的找到冲突的数。:Hi = (H0 +i^2 )% m,(i = 1,2,3……),再使用哈希函数。。

  • 开散列 

 开散列就是在一个数组中每个位置加了一个链表(尾插法,也可以使用头插法)。 

 下面我们具体实现以下链表+数组的这类代码

首先需要先创建结点信息、创建数组以及计数器这些初步信息。

    static class Node {public int key;public int value;public Node next;public Node(int key, int value) {this.key = key;this.value = value;}}public Node[] arr;public int size;public HashBuck(){arr = new Node[10];}

然后在数组中添加结点使其构成链表结构,这个是步骤最多的操作。首先需要知道这个键的数组下标index(这个通过取模求得),然后就是找到数组对应的下标进行插入操作,但是在开始插入前需要知道一些信息,像数组该下标是否为空,那就需要找到其末尾并将其插入进去;又或者像该数组下标中已经存在与之相同的键值那就更新其值、最后返回即可。最后就是插入了,先要创建这个新的结点,然后将其连接到链表的尾端,最后计数器加一就好啦!

public void put(int key, int value){//头插法
//        int index = key %arr.length;
//        Node cur = arr[index];
//        //判断链表中是否存在相同的key,存在就更新value
//        while (cur != null) {
//            if (cur.key == key) {
//                cur.value = value;
//                return;
//            }
//            cur = cur.next;
//        }
//        //如果不存在就 头插法 插入
//        Node node = new Node(key, value);
//        node.next = arr[index];
//        arr[index] = node;
//        size++;//尾插法int index = key %arr.length;Node cur = arr[index];//判断链表中是否存在相同的key,存在就更新valuewhile (cur != null) {if (cur.key == key) {cur.value = value;return;}cur = cur.next;}//如果不存在就 尾插法 插入Node cur1 = arr[index];if (cur1 == null){Node node = new Node (key, value);arr[index] = node;size++;}else {while (cur1.next != null) {cur1 = cur1.next;}//尾插法Node node = new Node(key, value);cur1.next = node;size++;}

还有一点就是如果当负载因子很高时就需要将数组扩容,但是有一点不太好的就是需要将之前数组中所有元素重新哈希一遍也就是重新插入新的数组中。这个可以写到添加完结点后进行判断,所以我就接着上面继续写。

扩容一个新的数组tempArr,容量是原先数组的的两倍(看情况可以改),再遍历一遍原先的数组arr,将其每个位置的下标都重新哈希一次,由于是链表结构所以需要先保存当前结点的next值curNext,然后记录当前节点在新数组中的新位置,然后进行尾插法。

在进行尾插法时需要注意新位置中是否为空,为空时:将新数组中新位置修改为当前节点;不为空时:需要先找找链表中的尾节点,然后将结点加入进行。但是不管为不为空都需要将当前节点的next置为空(不然下次找时会存在之前数组的next地址),在继续遍历当前结点的下一个结点(前一步已经置为空了,这就是为什么要保存下一个节点的意义)【如果这两步顺序换一下结果是不是一样的?也行,但是需要保存一下cur,第一步已经将cur改变了,所以再对cur的next进行置空就与结果不一致了】。

            if (loadFactor() >= 0.75){//超载了需要扩容,然而扩容时需要把所有的元素都重新哈希(因为数组的容量变了对应取模的标准也变了)resize();}}private void resize(){Node[] tempArr = new Node[arr.length*2];for (int i = 0; i < arr.length; i++) {//头插
//            Node cur = arr[i];
//            while (cur != null){
//                //记录当前结点的下一个结点位置
//                Node curNext =cur.next;
//                //结点新的位置
//                int newIndex = cur.key%tempArr.length;
//                //头插法
//                cur.next = tempArr[newIndex];
//                tempArr[newIndex] = cur;
//                cur = curNext;
//          }//尾插法Node cur = arr[i];while (cur != null) {Node curNext = cur.next;int newIndex = cur.key % tempArr.length;Node newCur = tempArr[newIndex];if (newCur == null) {tempArr[newIndex] = cur;} else {while (newCur.next != null) {newCur = newCur.next;}newCur.next = cur;}cur.next = null;cur = curNext;}}arr = tempArr;}private double loadFactor(){return size*1.0/arr.length;}

还有一个简单的get方法(获取对应key的value值)。

    public int get(int key){int index = key %arr.length;Node cur = arr[index];//判断链表中是否存在相同的key,并返回该节点的value值while (cur != null) {if (cur.key == key) {return cur.value;}cur = cur.next;}return -1;}
小知识:
  1. 键(Key)
    • 键是用于在哈希表(或类似的数据结构)中定位值的唯一标识符。
    • 在Java的HashMap中,键是用来计算哈希码的,这个哈希码决定了值存储的位置。
    • 键需要实现hashCode()方法,并且应该与equals()方法保持一致,以确保哈希表的正确性和效率。
  2. 值(Value)
    • 值是与键相关联的数据。
    • 在HashMap中,值是你实际存储和检索的数据。
    • 值不需要实现任何特定的方法,因为它们是通过键来访问的。

为什么需要键和值?

  • 快速查找:哈希表利用键的哈希码快速定位相应的值,这使得插入和查找操作在平均情况下非常高效(接近O(1)复杂度)。
  • 处理冲突:当多个键产生相同的哈希码时(称为冲突),哈希表需要有机制来处理这些冲突,例如使用链表、红黑树等来存储多个值。
  • 灵活性:键值对的结构允许你根据键来更新或删除特定的值,而不需要重新组织整个数据结构。

键和值的区别

  • 唯一性:在大多数哈希表实现中(如HashMap),键必须是唯一的。这意味着你不能有两个相同的键存储不同的值。然而,值不需要是唯一的,多个键可以指向相同的值。
  • 不可变性:键通常是不可变的(如String、Integer等),以确保哈希码的一致性。如果键在插入后被修改,可能导致无法找到原来的值。

使用哈希表可以将时间复杂度转成空间复杂度(通过空间浪费来换取时间的过程)。

如果面对的key是引用类型时,对key进行取模就不适用了,那么就需要一个新的put方法进行插入,但是大概的操作还是一样的,需要注意的就是引用类型中key不能取模需要先获取它的哈希值(通过hashCode方法实现)然后再取模剩下的差不多一样了,当然引用类型比较时需要用到equals方法。

class Person{public String name;public Person (String name){this.name = name;}//hashCode和equals的区别//hashCode是为了定位数组下标的,equals是遍历下标比较key是否存在相同的key}public class HashBuckPerson<K, V> {static class Node <K, V>{public K key;public V value;public Node<K, V> next;public Node(K key, V value){this.key = key;this.value = value;}}public Node<K, V>[] arr;public int size;public HashBuckPerson(){arr = new Node[10];}public void put(K key, V value){//头插法int hash = key.hashCode();int index = hash %arr.length;Node<K, V> cur = arr[index];//判断链表中是否存在相同的key,存在就更新valuewhile (cur != null) {if (cur.key.equals(key)) {cur.value = value;return;}cur = cur.next;}//如果不存在就 头插法 插入Node<K, V>  node = new Node<>(key, value);node.next = arr[index];arr[index] = node;size++;}public V get(K key){int hash = key.hashCode();int index = hash % arr.length;Node<K, V> cur = arr[index];//判断链表中是否存在相同的key,存在就更新valuewhile (cur != null) {if (cur.key.equals(key)) {return cur.value;}cur = cur.next;}return null;}public static void main(String[] args) {HashBuckPerson<Person, Integer> hash = new HashBuckPerson<>();Person person1 = new Person("zhangsan");Person person2 = new Person("lisi");hash.put(person1, 12);hash.put(person2, 1);System.out.println(hash.get(person2));}
}
http://www.dtcms.com/wzjs/128818.html

相关文章:

  • 长沙企业网站开发微联讯点营销推广方案范文
  • 中国vpswindows野外农民工seo的主要工作内容
  • 深圳宝安区怎么样搜索引擎优化的流程
  • 自己公司做公益网站怎么弄商品推广软文范例200字
  • 只做瓶子包装设计的创意网站潍坊百度关键词优化
  • 抚州市建设局网站查询分销系统
  • 那个网站做视频能挣钱怎么推广软件
  • 手机网站制作教程视频教程搭建网站步骤
  • oracle数据库网站开发软文网站名称
  • qq空间的网站seo什么意思
  • 怎么给客户推广自己的产品山西seo优化公司
  • 中国物流网站重庆关键词优化
  • wordpress post_format广州网站seo
  • wordpress 怎么登录地址网站关键词优化排名推荐
  • 太原网站建设哪家好广告大全
  • 软件开发可以做网站么seo短视频网页入口引流网站
  • 网站建设微信商城开发欧洲网站服务器
  • 网站建设的维护工作有哪些鹤壁网站推广公司
  • 网站装修用什么软件做产品推广计划
  • 商标logo设计免费生成软件百度seo怎么提高排名
  • 烟台做网站优化新手怎么做电商运营
  • 尚未设置自定义缩略图wordpress深圳专业seo外包
  • 做网站的竞品分析google play store
  • 去哪找网站建设公司开网站需要多少钱
  • 海外推广网站关键词优化快速排名
  • 武汉建云网站网站seo哪家做的好
  • 企业网站建设报价上海最专业的seo公司
  • 企业做淘宝网站需要多少钱郑州seo方案
  • 建设产品网站课程设计石家庄百度搜索引擎优化
  • 贸易公司网站模板网站推广软件费用是多少