Map Set(Java篇详解)

  • 🍁 个人主页:爱编程的Tom
  • 💫 本篇博文收录专栏:Java专栏
  • 👉 目前其它专栏:c系列小游戏     c语言系列--万物的开始_ 等              
  • 🎉 欢迎 👍点赞✍评论⭐收藏💖三连支持一下博主🤞
  • 🧨现在的沉淀就是对未来的铺垫🎨 

目录

前言 

1. 搜索树 

概念

搜索树的部分操作 

查找 

插入 

删除 

性能分析 

2. 搜索  

概念及场景 

模型 

3. Map 的使用 

Map的常用方法 

TreeMap和HashMap的区别: 

4. Set 的说明 

常见方法说明 

TreeSet和HashSet的区别 

5. 哈希表 

什么是哈希表

6. 哈希冲突 

什么是哈希冲突 

如何避免 

避免方法1-哈希函数设计 

避免方法2-负载因子调节 

哈希冲突的解决方法 

哈希冲突解决-闭散列 

哈希冲突解决-开散列/哈希桶 

冲突严重时的解决办法 

性能分析 



​​​​​​​

前言 

本篇博客将描述Java当中十分重要的一个概念,即Map和Set的深入知识,帮助大家理解其中内涵,从而更深入的了解到关于HashMap和HashSet的相关内容……

1. 搜索树 

  • 概念

二叉搜索树又称二叉排序树,它或者是一棵空树,或者是具有以下性质的二叉树:

  • 若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
  • 若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
  • 它的左右子树也分别为二叉搜索树 
  • 搜索树的部分操作 

  • 查找 

此处从根节点开始查找,然后是根的左子树,根的右子树。 

  • 插入 

 1. 如果树为空树,即根 == null,直接插入

、2. 如果树不是空树,按照查找逻辑确定插入位置,插入新结点

  • 删除 

 设待删除结点为 cur, 待删除结点的双亲结点为 parent

1. cur.left == null

  •          cur 是 root,则 root = cur.right
  •          cur 不是 root,cur 是 parent.left,则 parent.left = cur.right
  •          cur 不是 root,cur 是 parent.right,则 parent.right = cur.right

2. cur.right == null

  •         cur 是 root,则 root = cur.left
  •         cur 不是 root,cur 是 parent.left,则 parent.left = cur.left
  •         cur 不是 root,cur 是 parent.right,则 parent.right = cur.left

3. cur.left != null && cur.right != null

        需要使用替换法进行删除,即在它的右子树中寻找中序下的第一个结点(关键码最小),用它的值填补到被删除节点中,再来处理该结点的删除问题。

下面给出其参考代码: 

public class BinarySearchTree {
    public static class Node {
        int key;
        Node left;
        Node right;
 
        public Node(int key) {
            this.key = key;
       }
   }
 
    private Node root = null;
 
    /**
     * 在搜索树中查找 key,如果找到,返回 key 所在的结点,否则返回 null
     * @param key
     * @return
     */
    public Node search(int key) {
        Node cur = root;
        while (cur != null) {
            if (key == cur.key) {
                return cur;
           } else if (key < cur.key) {
                cur = cur.left;
           } else {
                cur = cur.right;
           }
       }
 
        return null;
   }
 
    /**
     * 插入
     * @param key
     * @return true 表示插入成功, false 表示插入失败
     */
    public boolean insert(int key) {
        if (root == null) {
            root = new Node(key);
            return true;
       }
 
        Node cur = root;
        Node parent = null;
        while (cur != null) {
            if (key == cur.key) {
                return false;
           } else if (key < cur.key) {
                parent = cur;
                cur = cur.left;
           } else {
                parent = cur;
                cur = cur.right;
           }
       }
 
        Node node = new Node(key);
        if (key < parent.key) {
            parent.left = node;
       } else {
            parent.right = node;
       }
        return true;
   }
 
    /**
     * 删除成功返回 true,失败返回 false
     * @param key
     * @return
     */
    public boolean remove(int key) {
        Node cur = root;
        Node parent = null;
        while (cur != null) {
            if (key == cur.key) {
                break;
           } else if (key < cur.key) {
                parent = cur;
                cur = cur.left;
           } else {
                parent = cur;
                cur = cur.right;
           }
       }
        
        // 该元素不在二叉搜索树中
        if(null == cur){
            return false;
       }
 
        /*
        根据cur的孩子是否存在分四种情况
        1. cur左右孩子均不存在
        2. cur只有左孩子
        3. cur只有右孩子
        4. cur左右孩子均存在
        看起来有四种情况,实际情况1可以与情况2或者3进行合并,只需要处理是那种情况即可
        除了情况4之外,其他情况可以直接删除
        情况4不能直接删除,需要在其子树中找一个替代节点进行删除
        
        return true;
   }
}
  • 性能分析 

插入和删除操作都必须先查找,查找效率代表了二叉搜索树中各个操作的性能

对有n个结点的二叉搜索树,若每个元素查找的概率相等,则二叉搜索树平均查找长度是结点在二叉搜索树的深度的函数,即结点越深,则比较次数越多。

但对于同一个关键码集合,如果各关键码插入的次序不同,可能得到不同结构的二叉搜索树:

 

最优情况下,二叉搜索树为完全二叉树,其平均比较次数为:log(2)N

最差情况下,二叉搜索树退化为单支树,其平均比较次数为:N/2

问题:如果退化成单支树,二叉搜索树的性能就失去了。那能否进行改进,不论按照什么次序插入关键码,都可以是二叉搜索树的性能最佳? 

答:可以使用AVL树

AVL树是一种严格平衡的二叉搜索树,通过在每次插入和删除节点后进行旋转操作,来确保每个节点的左右子树高度差不超过1。

  • 和 java 类集的关系 

TreeMap 和 TreeSet 即 java 中利用搜索树实现的 Map 和 Set;实际上用的是红黑树,而红黑树是一棵近似平衡的二叉搜索树,即在二叉搜索树的基础之上 + 颜色以及红黑树性质验证, 

2. 搜索  

  • 概念及场景 

Map和set是一种专门用来进行搜索的容器或者数据结构,其搜索的效率与其具体的实例化子类有关。以前常见的搜索方式有:

  • 直接遍历,时间复杂度为O(N),元素如果比较多效率会非常慢
  • 二分查找,时间复杂度为 ,但搜索前必须要求序列是有序的

上述排序比较适合静态类型的查找,即一般不会对区间进行插入和删除操作了,而现实中的查找比如:

  • 根据姓名查询考试成绩
  • 通讯录,即根据姓名查询联系方式
  • 不重复集合,即需要先搜索关键字是否已经在集合中

可能在查找时进行一些插入和删除的操作,即动态查找,那上述两种方式就不太适合了

本文介绍的Map和Set是一种适合动态查找的集合容器。 

  • 模型 

一般把搜索的数据称为关键字(Key),和关键字对应的称为值(Value),将其称之为Key-value的键值对,所以模型会有两种:

        1. 纯 key 模型,比如:

  • 有一个英文词典,快速查找一个单词是否在词典中
  • 快速查找某个名字在不在通讯录中

        2. Key-Value 模型,比如:

  • 统计文件中每个单词出现的次数,统计结果是每个单词都有与其对应的次数:单词,单词出现的次数>
  • 梁山好汉的江湖绰号:每个好汉都有自己的江湖绰号

Map中存储的就是key-value的键值对,Set中只存储了Key。 

3. Map 的使用 

 

说明:Map是一个接口类,该类没有继承自Collection,该类中存储的是结构的键值对,并且K一定是唯一的,不能重复。  

  • Map.Entry<K,V> 

说明:Map.Entry<K,V> 是Map内部实现的用来存放键值对映射关系的内部类,该内部类中主要提供了的获取<key,value>的设置以及Key的比较方式。 

此处需注意:Map.Entry并没有提供设置Key的方法  

  • Map的常用方法 

注意

1. Map是一个接口,不能直接实例化对象,如果要实例化对象只能实例化其实现类TreeMap或者HashMap

2. Map中存放键值对的Key是唯一的,value是可以重复

3. 在TreeMap中插入键值对时,key不能为空,否则就会抛NullPointerException异常,value可以为空。但是HashMap的key和value都可以为空。

4. Map中的Key可以全部分离出来,存储到Set中来进行访问(因为Key不能重复)。

5. Map中的value可以全部分离出来,存储在Collection的任何一个子集合中(value可能有重复)。

6. Map中键值对的Key不能直接修改,value可以修改,如果要修改key,只能先将该key删除掉,然后再来进行重新插入。

7. TreeMap和HashMap的区别

  • TreeMap和HashMap的区别: 

 

4. Set 的说明 

Set与Map主要的不同有两点:Set是继承自Collection的接口类,Set中只存储了Key。  

  • 常见方法说明 

 

  • 注意:

1. Set是继承自Collection的一个接口类

2. Set中只存储了key,并且要求key一定要唯一

3. TreeSet的底层是使用Map来实现的,其使用key与Object的一个默认对象作为键值对插入到Map中的

4. Set最大的功能就是对集合中的元素进行去重

5. 实现Set接口的常用类有TreeSet和HashSet,还有一个LinkedHashSet,LinkedHashSet是在HashSet的基础 上维护了一个双向链表来记录元素的插入次序。

6. Set中的Key不能修改,如果要修改,先将原来的删除掉,然后再重新插入

7. TreeSet中不能插入null的key,HashSet可以。

8. TreeSet和HashSet的区别【如下图】  

  • TreeSet和HashSet的区别 

 

5. 哈希表 

  • 什么是哈希表

顺序结构以及平衡树中,元素关键码与其存储位置之间没有对应的关系,因此在查找一个元素时,必须要经过关键码的多次比较。顺序查找时间复杂度为O(N),平衡树中为树的高度,即O(log(2)N ),搜索的效率取决于搜索过程中元素的比较次数。

理想的搜索方法:可以不经过任何比较,一次直接从表中得到要搜索的元素。 如果构造一种存储结构,通过某种函 数(hashFunc)使元素的存储位置与它的关键码之间能够建立一一映射的关系,那么在查找时通过该函数可以很快找到该元素。

当向该结构中:

  • 插入元素

        根据待插入元素的关键码,以此函数计算出该元素的存储位置并按此位置进行存放

  • 搜索元素

        对元素的关键码进行同样的计算,把求得的函数值当做元素的存储位置,在结构中按此位置取元素比较,若关键码相等,则搜索成功

该方式即为哈希(散列)方法,哈希方法中使用的转换函数称为哈希(散列)函数,构造出来的结构称为哈希表(Hash Table)(或者称散列表)

例如:数据集合{1,7,6,4,5,9};

哈希函数设置为:hash(key) = key % capacity; (capacity为存储元素底层空间总的大小。)  

具体图示:

 

用该方法进行搜索不必进行多次关键码的比较,因此搜索的速度比较快  

问题:按照上述哈希方式,向集合中插入元 素44,会出现什么问题? 

答:会出现冲突现象,4的地址位置已有元素。 

6. 哈希冲突 

  • 什么是哈希冲突 

对于两个数据元素的关键字ki和kj(i != j),有ki != kj,但有:Hash(ki ) == Hash(kj ),

即:不同关键字通过相同哈希哈数计算出相同的哈希地址,该种现象称为哈希冲突或哈希碰撞。 把具有不同关键码而具有相同哈希地址的数据元素称为“同义词”。 

  • 如何避免 

首先,我们需要明确一点,由于我们哈希表底层数组的容量往往是小于实际要存储的关键字的数量的,这就导致一个问题,冲突的发生是必然的,但我们能做的应该是尽量的降低冲突率。 

  • 避免方法1-哈希函数设计 

引起哈希冲突的一个原因可能是:哈希函数设计不够合理

哈希函数设计原则

  • 哈希函数的定义域必须包括需要存储的全部关键码,而如果散列表允许有m个地址时,其值域必须在0到m-1 之间
  • 哈希函数计算出来的地址能均匀分布在整个空间中
  • 哈希函数应该比较简单  
  • 常见哈希函数 

1. 直接定制法--(常用)

取关键字的某个线性函数为散列地址:Hash(Key)= A*Key + B

优点:简单、均匀

缺点:需要事先知道关 键字的分布情况

使用场景:适合查找比较小且连续的情况

2. 除留余数法--(常用)

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

3. 平方取中法--(了解)

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

4. 折叠法--(了解) 

折叠法是将关键字从左到右分割成位数相等的几部分(最后一部分位数可以短些),然后将这几部分叠加求和, 并按散列表表长,取后几位作为散列地址。

折叠法适合事先不需要知道关键字的分布,适合关键字位数比较多的情况

5. 随机数法--(了解)

选择一个随机函数,取关键字的随机函数值为它的哈希地址,即H(key) = random(key),其中random为随机数 函数。 通常应用于关键字长度不等时采用此法

6. 数学分析法--(了解)

设有n个d位数,每一位可能有r种不同的符号,这r种不同的符号在各位上出现的频率不一定相同,可能在某 些位上分布比较均匀,每种符号出现的机会均等,在某些位上分布不均匀只有某几种符号经常出现。可根据 散列表的大小,选择其中各种符号分布均匀的若干位作为散列地址。

数字分析法通常适合处理关键字位数比较大的情况,如果事先知道关键字的分布且关键字的若干位分布较均匀的情况

注意:哈希函数设计的越精妙,产生哈希冲突的可能性就越低,但是无法避免哈希冲突

  • 避免方法2-负载因子调节 

  • 负载因子和冲突率的关系粗略演示 

 

所以当冲突率达到一个无法忍受的程度时,我们需要通过降低负载因子来变相的降低冲突率

已知哈希表中已有的关键字个数是不可变的,那我们能调整的就只有哈希表中的数组的大小。 

当负载因子过大时,需要进行扩容,重新哈希,参考代码如下:

  private void reSize() {//大于0.75处理
        Node[] newArray = new  Node[array.length*2];// 重新哈希
        for (int i = 0; i < array.length; i++) {
            Node cur = array[i];
            while (cur != null) {
                int index = cur.key % newArray.length;
                //记录之前的cur.next
                Node curNext = cur.next;
                //实施头插法,插入到新数组
                cur.next = newArray[index];
                newArray[index] = cur;
                cur = curNext;
            }
        }
        //把数据给到原数组array
        array = newArray;
    }
  • 哈希冲突的解决方法 

解决哈希冲突两种常见的方法是:闭散列和开散列  

  • 哈希冲突解决-闭散列 

闭散列:也叫开放定址法,当发生哈希冲突时,如果哈希表未被装满,说明在哈希表中必然还有空位置,那么可以 把key存放到冲突位置中的“下一个” 空位置中去。

那如何寻找下一个空位置呢? 

1. 线性探测

比如之前的场景,现在需要插入元素44,先通过哈希函数计算哈希地址,下标为4,因此44理论上应该插在该位置,但是该位置已经放了值为4的元素,即发生哈希冲突。

    线性探测:从发生冲突的位置开始,依次向后探测,直到寻找到下一个空位置为止。

    插入         

  •         通过哈希函数获取待插入元素在哈希表中的位置
  •         如果该位置中没有元素则直接插入新元素,如果该位置中有元素发生哈希冲突,使用线性探测找到 下一个空位置,插入新元素 

 

采用闭散列处理哈希冲突时,不能随便物理删除哈希表中已有的元素,若直接删除元素会影响其他 元素的搜索。比如删除元素4,如果直接删除掉,44查找起来可能会受影响。因此线性探测采用标记的伪删除法来删除一个元素。 

2. 二次探测

线性探测的缺陷产生冲突的数据堆积在一块,这与其找下一个空位置有关系,因为找空位置的方式就是挨着往后逐个去找,因此二次探测为了避免该问题,找下一个空位置的方法为:Hi = ( H0+i^2 )% m, 或者:Hi = ( H0 - i^2)% m。其中:i = 1,2,3…, 是通过散列函数Hash(x)对元素的关键码 key 进行计算得到的位置, m是表的大小。 对于2.1中如果要插入44,产生冲突,使用解决后的情况为:  

 

研究表明:当表的长度为质数且表装载因子a不超过0.5时,新的表项一定能够插入,而且任何一个位置都不 会被探查两次。因此只要表中有一半的空位置,就不会存在表满的问题。在搜索时可以不考虑表装满的情况,但在插入时必须确保表的装载因子a不超过0.5,如果超出必须考虑增容。  

因此:闭散列最大的缺陷就是空间利用率比较低,这也是哈希的缺陷 

  • 哈希冲突解决-开散列/哈希桶 

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

 

从上图可以看出,开散列中每个桶中放的都是发生哈希冲突的元素

开散列,可以认为是把一个在大集合中的搜索问题转化为在小集合中做搜索了。 

  • 冲突严重时的解决办法 

刚才我们提到了,哈希桶其实可以看作将大集合的搜索问题转化为小集合的搜索问题了,那如果冲突严重,就意味着小集合的搜索性能其实也时不佳的,这个时候我们就可以将这个所谓的小集合搜索问题继续进行转化,

例如:

        1. 每个桶的背后是另一个哈希表

        2. 每个桶的背后是一棵搜索树  

这里给出参考实现代码:

 // key-value 模型
public class HashBucket {
    private static class Node {
        private int key;
        private int value;
        Node next;
 
        public Node(int key, int value) {
            this.key = key;
            this.value = value;
       }
}
 
    private Node[]  array;
    private int size;   // 当前的数据个数
    private static final double LOAD_FACTOR = 0.75;
 
    public int put(int key, int value) {
        int index = key % array.length;
 
        // 在链表中查找 key 所在的结点
        // 如果找到了,更新
        // 所有结点都不是 key,插入一个新的结点
        for (Node cur = array[index]; cur != null; cur = cur.next) {
            if (key == cur.key) {
                int oldValue = cur.value;
                cur.value = value;
                return oldValue;
           }
       }
        Node node = new Node(key, value);
        node.next = array[index];
        array[index] = node;
        size++;
 
        if (loadFactor() >= LOAD_FACTOR) {
            resize();
       }
 
        return -1;
   }
 
    private void resize() {
        Node[] newArray = new Node[array.length * 2];
        for (int i = 0; i < array.length; i++) {
            Node next;
            for (Node cur = array[i]; cur != null; cur = next) {
                next = cur.next;
                int index = cur.key % newArray.length;
                cur.next = newArray[index];
                newArray[index] = cur;
           }
       }
        array = newArray;
   }
 
    private double loadFactor() {
        return size * 1.0 / array.length;
   }
 
    public HashBucket() {
        array = new Node[8];
        size = 0;
   }
public int get(int key) {
        int index = key % array.length;
 
        Node head = array[index];
        for (Node cur = head; cur != null; cur = cur.next) {
            if (key == cur.key) {
                return cur.value;
           }
       }
 
        return -1;
   }
}
  • 性能分析 

虽然哈希表一直在和冲突做斗争,但在实际使用过程中,我们认为哈希表的冲突率是不高的,冲突个数是可控的, 也就是每个桶中的链表的长度是一个常数,所以,通常意义下,我们认为哈希表的插入/删除/查找时间复杂度是 O(1) 。 

  • 和 java 类集的关系 

1. HashMap 和 HashSet 即 java 中利用哈希表实现的 Map 和 Set

2. java 中使用的是哈希桶方式解决冲突

3. java 会在冲突链表长度大于一定阈值后,将链表转变为搜索树(红黑树)

4. java 中计算哈希值实际上是调用的类的 hashCode 方法,进行 key 的相等性比较是调用 key 的 equals 方 法。所以如果要用自定义类作为 HashMap 的 key 或者 HashSet 的值,必须覆写 hashCode 和 equals 方法,而且要做到 equals 相等的对象,hashCode 一定是一致的。  

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/770739.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

设备的智能决策AI大模型与ESP32-S3芯片应用,启明云端乐鑫代理商

在数字化浪潮的推动下&#xff0c;智能家居、智能安防、智能设备等领域对AI技术的需求日益增长&#xff0c;人工智能&#xff08;AI&#xff09;正迅速成为推动各行各业创新的核心力量。 AI大模型技术以其强大的数据处理能力和深度学习能力&#xff0c;正在成为智能时代的新动…

图书电商引入实在Agent:自动化运营提效80%,节省人天1000+

某知名教辅图书品牌深耕中小学教辅图书领域&#xff0c;是中国最具影响力的教育出版策划与发行集团之一&#xff0c;以丰富的图书品类&#xff0c;满足了小学、初中、高中各年龄段读者多元化的阅读需求。 2023年&#xff0c;该品牌在运营、客服等多部门超60个场景中部署实在Ag…

维护el-table列,循环生成el-table

1、lib/setting.js&#xff08;维护table列&#xff09; const columns[{ label: 类型, prop: energyName, width: 150, isText: true },{ label: 消耗量(t或10⁴m), prop: inputNum, isInput: true },{label: CO₂,children: [// { label: 核算因子, prop: co2FactorValue, w…

IC烧录员-带着工程师的梦想远航!

如果说软件工程师是代码程序的创造者&#xff0c;那么IC烧录员就是把工程师们辛苦敲代码&#xff0c;日夜辛劳的成果烧录到芯片里面的实践者&#xff0c;是他们&#xff0c;让工程师们的梦想运用到实践中&#xff0c;是他们带着工程师的梦想远航&#xff0c;他们的薪酬或许没有…

SprongBoot3整合Knife4j实现在线接口文档

大家好&#xff0c;我是晓凡。 写在前面 在上一篇文章&#xff0c;我们详细介绍了SpringBoot3 怎么整合SpringDoc实现在线接口文档。但是&#xff0c;有不少小伙伴 都觉得接口界面太丑了。有没有什么更美观一点的UI界面呢&#xff1f; 当然是有的了&#xff0c;毕竟这是一个…

temu a4接口 逆向

声明(lianxi a15018601872) 本文章中所有内容仅供学习交流使用&#xff0c;不用于其他任何目的&#xff0c;抓包内容、敏感网址、数据接口等均已做脱敏处理&#xff0c;严禁用于商业用途和非法用途&#xff0c;否则由此产生的一切后果均与作者无关&#xff01; 前言(lianxi …

c++习题09-分离整数的各个数

目录 一&#xff0c;题目 二&#xff0c;思路 三&#xff0c;代码 一&#xff0c;题目 二&#xff0c;思路 一开始我想到的是将简单容易输出的1000以内的数先进行相应的运算&#xff0c;再输出之后再对1000以上的数字进行判断&#xff08;主要还是想先将很大的数变小&#x…

每日一题——Python实现PAT乙级1026 程序运行时间(举一反三+思想解读+逐步优化)五千字好文

一个认为一切根源都是“自己不够强”的INTJ 个人主页&#xff1a;用哲学编程-CSDN博客专栏&#xff1a;每日一题——举一反三Python编程学习Python内置函数 Python-3.12.0文档解读 目录 我的写法 代码结构和逻辑 时间复杂度 空间复杂度 代码优化建议 总结 我要更强 …

生物分子生物学实验过程的自动化与智能监控系统设计

开题报告&#xff1a;生物分子生物学实验过程的自动化与智能监控系统设计 一、引言 随着生物科学技术的飞速发展&#xff0c;生物分子生物学实验在科研、医疗、农业等领域的应用日益广泛。然而&#xff0c;传统的生物分子生物学实验过程大多依赖于人工操作&#xff0c;存在操…

组件丰富、支持2/3D数据可视化的编辑器平台软件?

数据可视化编辑器通常用于创建交互式的图表和模型&#xff0c;可以帮助用户以更直观的方式展示数据。一些在线平台软件提供了丰富的组件&#xff0c;支持2D和3D数据可视化&#xff1a; 1、Plotly - 提供了多种语言的库&#xff0c;支持在线创建交互式图表&#xff0c;包括2D和…

【2023ICPC网络赛I 】E. Magical Pair

当时在做洛谷U389682 最大公约数合并的时候我就想到把每个质因子分解出来然后跑高维前缀和&#xff0c;但是那一道题不是用这个方法&#xff0c;所有我也一直在思考这种做法是不是真的有用。因为昨天通过2024上海大学生程序设计竞赛I-六元组计数这道题我了解到了不少关于原根的…

平安养老险安徽分公司:助力乡村振兴 保险知识进农村

为深入宣传普及保险理念&#xff0c;进一步提升服务品质&#xff0c;营造“78全国保险公众宣传日”活动氛围&#xff0c;助力保险业健康稳定发展&#xff0c;近日&#xff0c;平安养老保险股份有限公司&#xff08;以下简称“平安养老险”&#xff09;安徽分公司走进安庆市宿松…

智能座舱相关问答

一、基本概念与理解 智能座舱的定义 回答&#xff1a;智能座舱是指在现代交通工具中&#xff0c;通过应用智能技术&#xff0c;实现对乘客座舱环境和服务进行智能化管理和优化的系统。它不仅提供更加舒适的乘坐体验&#xff0c;还能通过精确的数据分析和实时监控&#xff0c;提…

教育行业的网络安全:保护学生数据与防范网络欺凌

在数字化的春风中&#xff0c;教育行业迎来了知识的繁花似锦&#xff0c;然而&#xff0c;随之而来的网络安全风暴也悄然逼近。学生数据的脆弱性与网络欺凌的阴影交织成一幅复杂的画卷&#xff0c;呼唤着教育工作者与技术专家共同编织一张密不透风的网络安全之网。本文深入探讨…

A*——AcWing 178. 第K短路

A* 定义 A算法是一种广泛应用于路径搜索和图遍历的启发式搜索算法&#xff0c;它结合了最好优先搜索和Dijkstra算法的优点&#xff0c;旨在找到从初始节点到目标节点的最短路径。A算法在游戏AI、机器人导航、地图路线规划等领域有广泛应用。 A*算法的核心在于使用一个评估函…

React+TS前台项目实战(二十四)-- 全局常用绘制组件Qrcode封装

文章目录 前言Qrcode组件1. 功能分析2. 代码详细注释3. 使用方式4. 效果展示(pc端 / 移动端) 总结 前言 今天要封装的Qrcode 组件&#xff0c;是通过传入的信息&#xff0c;绘制在二维码上&#xff0c;可用于很多场景&#xff0c;如区块链项目中的区块显示交易地址时就可以用到…

顶顶通语音信箱手机助手拦截方案(mod_cti基于FreeSWITCH)

文章目录 前言联系我们拦截方案方案一&#xff1a;空号识别拦截拦截时间原理能够识别出的状态 方案二&#xff1a;实时质检拦截拦截时间原理拦截效果展示 服务器配置要求 前言 顶顶通拥有两种语音信箱手机助手拦截方案&#xff1a; 方案一&#xff1a;空号识别拦截&#xff0…

springboot接口防抖【防重复提交】

什么是防抖 所谓防抖&#xff0c;一是防用户手抖&#xff0c;二是防网络抖动。在Web系统中&#xff0c;表单提交是一个非常常见的功能&#xff0c;如果不加控制&#xff0c;容易因为用户的误操作或网络延迟导致同一请求被发送多次&#xff0c;进而生成重复的数据记录。要针对用…

成都百洲文化传媒有限公司电商服务的佼佼者

在当今这个数字化时代&#xff0c;电商已成为商业发展的核心动力之一。成都百洲文化传媒有限公司&#xff0c;作为一家专注于电商服务的领先企业&#xff0c;正以其卓越的服务质量和前瞻性的战略眼光&#xff0c;引领着电商行业的新潮流。 一、公司简介 成都百洲文化传媒有限公…

sssssssssssssssshare_ptrrrrrrrrrrrrrrrrrrrrrrrrr

智能指针——shared_ptr的原理及仿写 shared_ptr的原理及仿写 共享指针允许多个指针指向同一份数据&#xff0c;因为它使用了引用计数&#xff0c;每多一个指针指向这个数据&#xff0c;引用技术加一&#xff0c;每销毁一个指针&#xff0c;引用技术减一&#xff0c;如果引用计…