哈希表 | 哈希查找 | 哈希函数 | 数据结构 | 大话数据结构 | Java

🙋大家好!我是毛毛张!
🌈个人首页: 神马都会亿点点的毛毛张

📌毛毛张今天分享的内容🖆是数据结构中的哈希表,毛毛张主要是依据《大话数据结构📖》的内容来进行整理,不同之处是使用Java语言来介绍其中的代码实现,同时也为后面介绍Java中的集合做铺垫🛤️

文章目录

  • 1.散列表查找(哈希表)概述
    • 1.1 散列表查找定义
    • 1.2 散列表查找步骤
  • 2.散列函数的构造方法
    • 2.1 直接定址法(了解)
    • 2.2 数字分析法(了解)
    • 2.3 平方取中法(了解)
    • 2.4 折叠法(了解)
    • 2.5 除留余数法(掌握)
    • 2.6 随机数法(了解)
  • 3.处理散列冲突
    • 3.1 开放定址法
    • 3.2 链地址法
    • 3.3 公共溢出区法
  • 4.散列表查找实现
    • 4.1 散列表查找的算法Java实现
    • 4.2 性能分析
  • 参考文献

1.散列表查找(哈希表)概述

1.1 散列表查找定义

  • 散列技术: 是在记录的存储位置和它的关键字之间建立一个确定的对应关系 f f f,使得每个关键字key对应一个存储位置 f ( k e y ) f(key) f(key)。查找时,根据这个确定的对应关系找到给定值key的映射 f ( k e y ) f(key) f(key),若查找集合中存在这个记录,则必定在 f ( k e y ) f(key) f(key)的位置上。我们把这种对应关系 f f f称为散列函数,又称哈希(Hash)函数
    存储位置 = f ( 关键字 ) 存储位置 = f(关键字) 存储位置=f(关键字)
  • 散列表/哈希表(Hash table): 采用散列技术将记录存储在一块连续的存储空间中,这块连续存储空间称为散列表哈希表(Hash table)。而关键字对应的记录存储位置我们称为散列地址
  • 总结:
    • 散列表通过建立关键字和存储地址之间的一种直接映射关系,是一种根据关键字而直接进行访问的数据结构
    • 散列技术使得我们可以通过查找关键字不需要比较就可获得需要的记录的存储位置

1.2 散列表查找步骤

  • 散列过程步骤: 如下图
    • 第一步: 通过散列函数计算记录的散列地址,并按此散列地址存储记录
    • 第二步: 当查找记录时,我们通过同样的散列函数计算记录的散列地址,按此散列地址访问该记录,由于存取用的是同一个散列函数,因此结果当然也是相同的
      image-20240627154931051
  • 散列技术既是一种存储方法, 也是一种查找方法,而散列技术的记录之间不存在什么逻辑关系,它只与关键字有关联,因此散列主要是面向查找的存储结构
  • 散列技术最合适的求解问题是查找与给定值相等的记录
    • 不适合那种同样关键字,它能对应很多记录的情况,就不适合用散列技术
    • 散列表也不适合范围查找,比如查找一个班级18~22岁的同学,在散列表中没法进行
  • 冲突(collision): 散列函数可能会把两个或两个以上的不同关键字映射到同一地址,称这种情况为冲突,这些发生碰撞的不同关键字称为同义词
    • 一方面,设计得好的散列函数应尽量减少这样的冲突
    • 另一方面,由于这样的冲突总是不可避免的,所以还要设计好处理冲突的方法
  • 设计一个简单、均匀、存储利用率高的散列函数是散列技术中最关键的问题,一个好的散列表在理想情况下,对散列表进行查找的时间复杂度为$O(1) $,即与表中元素的个数无关

2.散列函数的构造方法

  • 好的散列函数设计原则:
    • 计算简单: 散列函数的计算应尽量简单,能够在较短的时间内计算出任一关键字对应的散列地址
    • 散列地址分布均匀: 散列函数计算出来的地址应该能等概率、均匀地分布在整个地址空间中,从而减少冲突的发生
    • 散列函数的定义域必须包含全部需要存储的关键字,而值域的范围则依赖于散列表的大小或地址范围
  • 下面介绍接种常用的散列函数构造方法

2.1 直接定址法(了解)

  • 直接取关键字的某个线性函数值为散列地址,散列函数为:其中a、b为常数
    f ( k e y ) = a × k e y + b f(key)=a×key + b f(key)=a×key+b
    image-20240627160121771
  • 这种方法计算最简单,且不会产生冲突,但并不常用。它适合关键字的分布基本连续的情况,若关键字分布不连续,空位较多,则会造成存储空间的浪费。

2.2 数字分析法(了解)

  • 例如当手机号码为关键字时,其11位数字是有规则的,此时是无需把11位数值全部当做散列地址,这时我们给关键词抽取, 抽取方法是使用关键字的一部分来计算散列存储位置的方法,这在散列函数中是常常用到的手段
  • 数字分析法通常适合处理关键字位数比较大的情况,如果事先知道关键字的分布且关键字的若干位分布较均匀,就可以考虑用这个方法。

2.3 平方取中法(了解)

  • 这个方法计算很简单,假设关键字是1234,那么它的平方就是1522756,再抽取中间的3位就是227,用做散列地址。
  • 再比如关键字是4321,那么它的平方就是18671041,抽取中间的3位就可以是671,也可以是710,用做散列地址。
  • 平方取中法比较适合于不知道关键字的分布,而位数又不是很大的情况

2.4 折叠法(了解)

  • 折叠法 是将关键字从左到右分割成位数相等的几部分(最后一部分位数可以短些),然后将这几部分叠加求和,并按散列表表长,取后几位作为散列地址。
  • 折叠法适合事先不需要知道关键字的分布,适合关键字位数比较多的情况

2.5 除留余数法(掌握)

  • 除留取余法对于散列表长为m的散列函数公式为:f(key)= key mod p (p ≤ m)mod是取模(求余数)的意思,这方法不仅可以对关键字直接取模,也可在折叠、平方取中后再取模。
  • 除留余数法的关键是就在于选择合适的 p p p p p p如果选择不好,就有可能产生同义词,根据前辈门的经验,若散列表表长为 m m m,通常 p p p为小于或等于表长(最好接近 m m m)的最小质数或不包含小于20质因子的合数
  • 图例:
    image-20240627161944061
  • 此方法是最常用的构造散列函数的方法

2.6 随机数法(了解)

  • 选择一个随机数,取关键字的随机函数值为它的散列地址。也就是 f ( k e y ) = r a n d o m ( k e y ) f(key)=random(key) f(key)=random(key)这里 r a n d o m random random是随机函数。
  • 当关键字的长度不等时,采用这个方法构造散列函数是比较合适的

3.处理散列冲突

  • 任何设计出来的散列函数都不可能绝对地避免冲突,因此,必须考虑在发生冲突时应该如何处理,即为产生冲突的关键字寻找下一个空的Hash地址
  • H i H_i Hi表示处理冲突中第 i i i次探测得到的散列地址,假设得到的另一个散列地址 H 1 H_1 H1仍然发生冲突,只得继续求下一个地址 H 2 H_2 H2,以此类推,直到 H k H_k Hk不发生冲突为止,则 H k H_k Hk为关键字在表中的地址

3.1 开放定址法

  • 开放定址法就是一旦发生了冲突,就去寻找下一个空的散列地址,只要散列表足够大,空的散列地址总能找到,并将记录存入
  • 计算公式: H i ( k e y ) = ( f ( k e y ) + d i ) % m ( d i = 1 , 2 , 3 , . . . , m − 1 ) H_i(key)=(f(key)+d_i) \% m\ (d_i=1,2,3,...,m-1) Hi(key)=(f(key)+di)%m (di=1,2,3,...,m1),式中, H ( k e y ) H(key) H(key)为散列函数 i = 0 , 1 , 2 , . . . , k ( k < = m − 1 ) i=0,1,2,...,k\ (k<=m-1) i=0,1,2,...,k (k<=m1) m m m表示散列列表表长; d i d_i di​​为增量序列
  • 取定某一增量序列后,对应的处理方法就是确定的,通常有以下4种方法:
  • 线性探测法:当 d i = 0 , 1 , 2 , . . . , m − 1 d_i=0,1,2,..., m-1 di=0,1,2,...,m1时,称为线性探测法
    • 特点: 当冲突发生时,顺序查看表中下一个单元(探测到表尾地址 m − 1 m-1 m1时,下一个探测地址是表首地址 0 0 0),直到找出一个空闲单元(当表未填满时一定能找到一个空闲单元)或查遍全表
    • 缺点: 线性探测法可能使第 i i i个散列地址的同义词存入第 i + 1 i+1 i+1个散列地址,这使得本应存入第 i + 1 i+1 i+1个散列地址的元素就争夺第 i + 2 i + 2 i+2个散列地址的元素的地址,从而造成大量元素在相邻的散列地址上堆积,大大降低了查找效率。
  • 平方探测法: d i = 0 2 , 1 2 , − 1 2 , 2 2 , − 2 2 , . . , k 2 , − k 2 d_i=0^2,1^2,-1^2,2^2,-2^2,..,k^2, -k^2 di=02,12,12,22,22,..,k2,k2时,称为平方探测法,其中 k < m / 2 k<m/2 k<m/2,散列表长度 m m m必须是一个可以表示成 4 k + 3 4k+ 3 4k+3​的素数,又称二次探测法
    • 平方探测法是一种较好的处理冲突的方法,可以避免出现堆积问题
    • 缺点: 不能探测到散列表上的所有单元,但至少能探测到一半单元
  • 再散列法: d i = H a s h 2 ( k e y ) d_i= Hash_2(key) di=Hash2(key)时,称为再散列法,又称双散列法,需要使用两个或者多个散列函数,当通过第一个散列函数 H ( k e y ) H(key) H(key)得到的地址发生冲突时,则利用第二个散列函数 H a s h 2 ( k e y ) Hash_2(key) Hash2(key)计算该关键字的地址增量
    • 具体散列函数形式为: H i = ( H ( k e y ) + i ∗ H a s h 2 ( k e y ) ) % m H_i= (H(key) + i*Hash_2(key)) \% m Hi=(H(key)+iHash2(key))%m,初始探测位置 H 0 = H ( k e y ) H_0 = H(key)% m H0=H(key) i i i是冲突的次数,初始为 0 0 0
    • 使用该方法,最多经过 m − 1 m-1 m1次探测就会遍历表中所有位置,回到 H 0 H_0 H0​位置
    • 这种方法使得关键字不产生剧集,相应地也增加了计算的时间
  • 伪随机数探测法: 在发生冲突时,对于位移量 d i d_i di​采用随机函数计算得到,称为伪随机探测法
    • 伪随机数是说,如果我们设置随机种子相同,则不断调用随机函数可以生成不会重复的数列,我们在查找时,用同样的随机种子,它每次得到的数列是相同的,相同的 d i d_i di当然可以得到相同的散列地址

注意:

  • 在开放定址的情形下,不能随便物理删除表中的已有元素,因为若删除元素,则会截断其他具有相同散列地址的元素的查找地址。
  • 因此,要删除一个元素时,可给它做一个删除标记,进行逻辑删除。
  • 但这样做的副作用是:执行多次删除后,表面上看起来散列表很满,实际上有许多位置未利用,因此需要定期维护散列表,要把删除标记的元素物理删除。

3.2 链地址法

  • 将所有关键字为同义词的记录存储在一个单链表中,我们称这种表为同义词子表,在散列表中只存储所有同义词子表的头指针
  • 示例: 有关键字序列为 { 12 , 67 , 56 , 16 , 25 , 37 , 22 , 29 , 15 , 47 , 48 , 34 } \{12, 67, 56, 16, 25, 37, 22, 29,15, 47,48, 34\} {12,67,56,16,25,37,22,29,15,47,48,34},我们用除留余数法构造散列函数 H ( k e y ) = k e y % 12 H(key)=key\%12 H(key)=key%12,用链地址法处理冲突,建立的表如下图所示
    image-20240627173927852
  • 链地址法对于可能会造成很多冲突的散列函数来说,提供了绝不会出现找不到地址的保障。当然,这也就带来了查找时需要遍历单链表的性能损耗

3.3 公共溢出区法

  • 这个方法其实就更加好理解,就是把凡是冲突的家伙额外找个公共场所待着,我们为所有冲突的关键字建立了一个公共的溢出区来存放
  • 示例:我们共有三个关键字 37 , 48 , 34 {37,48,34} 37,48,34与之前的关键字位置有冲突,那么就将它们存储到溢出表中,如下图所示
    在这里插入图片描述
  • 查找步骤:
    • 在查找时,对给定值通过散列函数计算出散列地址后,先与基本表的相应位置进行对比,如果相等,则查找成功;
    • 如果不相等,则到溢出表去进行顺序查找
  • 如果相对于基本表而言,有冲突的数据很少的情况下,公共溢出区的结构对查找性能来说还是非常高的

4.散列表查找实现

4.1 散列表查找的算法Java实现

  • 首先是需要定义一个散列表的结构以及一些相关的常数,其中HashTable就是散列表类,结构当中的elem为一个动态数组
public class HashTable {public static final int SUCCESS = 1;public static final int UNSUCCESS = 0;public static final int HASHSIZE = 12;    // 定义散列表表长为数组的长度public static final int NULLKEY = -32768; // 代表空地址private int[] elem;  // 数组元素存储基址,动态分配数组private int count;   // 当前数据元素个数private static int m = HASHSIZE; // 散列表表长,全局变量
}
  • 利用构造函数对散列表进行初始化
 public HashTable() {m = HASHSIZE;this.count = m;this.elem = new int[m];for (int i = 0; i < m; i++) {this.elem[i] = NULLKEY; // 初始化为空地址}
}
  • 为了插入时计算地址,我们需要定义散列函数,散列函数可以根据不同情况更改算法
// 散列函数
public int hash(int key) {return key % m;  // 除留余数法
}
  • 初始化完成后,我们可以对散列表进行插入操作,假设我们插入的关键字集合为 12 , 67 , 56 , 16 , 25 , 37 , 22 , 29 , 15 , 47 , 48 , 34 {12,67,56,16,25,37,22,29,15,47, 48,34} 12,67,56,16,25,37,22,29,15,47,48,34
 // 插入关键字进散列表
public void insertHash(int key) {int addr = hash(key);  // 通过散列函数求散列地址// 如果不为空,则冲突while (this.elem[addr] != NULLKEY) {addr = (addr + 1) % m;  // 开放定址法的线性探测}this.elem[addr] = key;  // 直到有空位后插入关键字
}
  • 代码中插入关键字时,首先算出散列地址,如果当前地址不为空关键字,则说明有冲突。此时我们应用开放定址法的线性探测进行重新寻址,此处也可更改为链地址法等其他解决冲突的办法。
  • 散列表存在后,我们在需要时就可以通过散列表查找要的记录
// 散列表查找关键字,找到后用addr保存地址
public boolean searchHash(int key, int[] addr) {addr[0] = hash(key);  // 通过散列函数求得散列地址// 如果不为空,则有同义词冲突while (this.elem[addr[0]] != key) {addr[0] = (addr[0] + 1) % m;  // 开放地址法的线性探测if (this.elem[addr[0]] == NULLKEY || addr[0] == hash(key)) {// 如果循环到空址或回到原点return false;  // 则说明关键字不存在}}return true;
}
  • 查找的代码与插入的代码非常类似,只需做一个不存在关键字的判断而已
  • 完整的测试代码如下:
public class HashTable {public static final int SUCCESS = 1;public static final int UNSUCCESS = 0;public static final int HASHSIZE = 12;    // 定义散列表表长为数组的长度public static final int NULLKEY = -32768; // 代表空地址private int[] elem;  // 数组元素存储基址,动态分配数组private int count;   // 当前数据元素个数private static int m = HASHSIZE; // 散列表表长,全局变量public HashTable() {this.elem = new int[HASHSIZE];this.count = HASHSIZE;for (int i = 0; i < HASHSIZE; i++) {this.elem[i] = NULLKEY; // 初始化为空地址}}// 初始化散列表public boolean initHashTable() {m = HASHSIZE;this.count = m;this.elem = new int[m];for (int i = 0; i < m; i++) {this.elem[i] = NULLKEY; // 初始化为空地址}return true;}// 散列函数public int hash(int key) {return key % m;  // 除留余数法}// 插入关键字进散列表public void insertHash(int key) {int addr = hash(key);  // 通过散列函数求散列地址// 如果不为空,则冲突while (this.elem[addr] != NULLKEY) {addr = (addr + 1) % m;  // 开放定址法的线性探测}this.elem[addr] = key;  // 直到有空位后插入关键字}// 散列表查找关键字,找到后用addr保存地址public boolean searchHash(int key, int[] addr) {addr[0] = hash(key);  // 通过散列函数求得散列地址// 如果不为空,则有同义词冲突while (this.elem[addr[0]] != key) {addr[0] = (addr[0] + 1) % m;  // 开放地址法的线性探测if (this.elem[addr[0]] == NULLKEY || addr[0] == hash(key)) {// 如果循环到空址或回到原点return false;  // 则说明关键字不存在}}return true;}public static void main(String[] args) {HashTable hashTable = new HashTable();boolean isInitialized = hashTable.initHashTable();System.out.println("HashTable initialized: " + isInitialized);// 插入一些关键字hashTable.insertHash(10);hashTable.insertHash(22);hashTable.insertHash(31);hashTable.insertHash(4);// 查找关键字int[] addr = new int[1];boolean found = hashTable.searchHash(22, addr);System.out.println("Key 22 found: " + found + ", at address: " + addr[0]);found = hashTable.searchHash(15, addr);System.out.println("Key 15 found: " + found);}
}

4.2 性能分析

  • 虽然散列表在关键字与记录的存储位置之间建立了直接映像,但由于冲突的产生,使得散列表的查找过程仍然是一个给定值和关键字进行比较的过程,因此,仍需要以平均查找长度作为衡量散列表的查找效率的度量。如果没有冲突,散列查找的时间复杂度是最高的,时间复杂度为 O ( 1 ) O(1) O(1)
  • 若用 c i c_i ci表示每一个关键字查找的次数,则平均查找次数可表示为: A S L = ( ∑ i = 0 m c i ) / m ASL=(\displaystyle\sum_{i=0}^{m}c_i)/m ASL=(i=0mci)/m
  • 散列表的查找效率取决于三个因素:
    • 散列函数是否均匀:散列函数的好坏直接影响着出现冲突的频繁程度,不过,由于不同的散列函数对同一组随机的关键字,产生冲突的可能性是相同的,因此我们可以不考虑它对平均查找长度的影响
    • 处理冲突的方法:相同的关键字、相同的散列函数,但处理冲突的方法不同,会使得平均查找长度不同。比如线性探测处理冲突可能会产生堆积,显然就没有二次探测法好,而链地址法处理冲突不会产生任何堆积,因而具有更佳的平均查找性能
    • 散列表的装填因子
  • 装填因子: 散列表的装填因子一般记为 α \alpha α,它用来表征散列表的装满程度,其计算公式为: α = n ( 表中填入的记录个数 ) / m ( 散列表长度 ) \alpha=n_{(表中填入的记录个数)}/m_{(散列表长度)} α=n(表中填入的记录个数)/m(散列表长度)
  • 散列表的平均查找长度取决于散列表的装填因子 α \alpha α,而不直接依赖于 n n n m m m
    • 直观地看, α \alpha α越大,表示装填的记录越,发生冲突的可能性越大,反之发生冲突的可能性越小
  • 不管记录个数 n n n有多大,我们总可以选择一个合适的装填因子以便将平均查找长度限定在一个范围之内,此时我们散列查找的时间复杂度就真的是 O ( 1 ) O(1) O(1)了。 为了做到这一点,通常我们都是将散列表的空间设置得比查找集合大,此时虽然是浪费了一定的空间,但换来的是查找效率的大大提升,总的来说,还是非常值得的。

参考文献

  • 《大话数据结构》
  • https://blog.csdn.net/Real_Fool_/article/details/114359564
  • https://blog.csdn.net/qq_56884023/article/details/123072671

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

程序化交易广告及其应用

什么是程序化交易广告&#xff1f; 程序化交易广告是以实时竞价技术即RTB&#xff08;real-time bidding&#xff09;为核心的广告交易方式。说到这里&#xff0c;你可能会有疑问&#xff1a;像百度搜索关键词广告还有百度网盟的广告&#xff0c;不也是CPC实时竞价的吗&#x…

创建kset

1、kset介绍 2、相关结构体和api介绍 2.1 struct kset 2.2 kset_create_and_add kset_create_and_addkset_createkset_registerkobject_add_internalkobject_add_internal2.3 kset_unregister kset_unregisterkobject_delkobject_put3、实验操作 #include<linux/module.…

kafka(一)原理(2)组件

一、broker 1、介绍 kafka服务器的官方名字&#xff0c;一个集群由多个broker组成&#xff0c;一个broker可以容纳多个topic。 2、工作流程 3、重要参数 参数名称 描述 replica.lag.time.max.ms ISR中&#xff0c;如果Follower长时间未向Leader发送通信请求或同步数据&a…

【分布式数据仓库Hive】HivQL的使用

目录 一、Hive的基本操作 1. 使用Hive创建数据库test 2. 检索数据库&#xff08;模糊查看&#xff09;&#xff0c;检索形如’te*’的数据库 3. 查看数据库test详情 4. 删除数据库test 5. 创建一个学生数据库Stus&#xff0c;在其中创建一个内部表Student&#xff0c;表格…

开源自动化热键映射工具autohotkey十大用法及精选脚本

AutoHotkey&#xff08;AHK&#xff09;是一款功能强大的热键脚本语言工具&#xff0c;它允许用户通过编写脚本来自动化键盘、鼠标等设备的操作&#xff0c;从而极大地提高工作效率。以下是AutoHotkey的十大经典用法&#xff0c;这些用法不仅解放了用户的双手&#xff0c;还展示…

OpenGL3.3_C++_Windows(27)

法线/凹凸贴图 如何让纹理产生更细节的效果&#xff0c;产生凹凸视觉感&#xff1f;解决思路之一&#xff1a;镜面贴图(黑—白&#xff09;&#xff08;&#xff08;diffuse贴图&#xff08;rgba&#xff09;&#xff09;&#xff0c;阻止部分表面被照的更亮&#xff0c;但这并…

不是大厂云用不起,而是五洛云更有性价比

明月代维的一个客户的大厂云境外云服务器再有几天就到期了&#xff0c;续费提醒那是提前一周准时到来&#xff0c;但是看到客户发来的续费价格截图&#xff0c;我是真的没忍住。这不就是在杀熟吗&#xff1f;就这配置续费竟然如此昂贵&#xff1f;说实话这个客户的服务器代维是…

ForkJoin框架与工作窃取算法详解

文章目录 一、ForkJoin框架概述1_核心概念2_主要类和方法1_ForkJoinPool2_ForkJoinTask 二、启用异步模式与否的区别三、ForkJoinPool的三种任务提交方式四、执行逻辑及使用示例1_示例&#xff1a;并行计算数组元素和2_forkJoinPool.submit3_ForkJoinTask<?>中任务的执行…

Web3 前端攻击:原因、影响及经验教训

DeFi的崛起引领了一个创新和金融自由的新时代。然而&#xff0c;这种快速增长也吸引了恶意行为者的注意&#xff0c;他们试图利用漏洞进行攻击。尽管很多焦点都集中在智能合约安全上&#xff0c;但前端攻击也正在成为一个重要的威胁向量。 前端攻击的剖析 理解攻击者利用前端漏…

uniapp标题水平对齐微信小程序胶囊按钮及适配

uniapp标题水平对齐微信小程序胶囊按钮及适配 状态栏高度胶囊按钮的信息计算顶部边距模板样式 标签加样式加动态计算实现效果 t是胶囊按钮距离的top h是胶囊按钮的高度 s是状态栏高度 大概是这样 状态栏高度 获取系统信息里的状态栏高度 const statusBarHeight uni.getSy…

使用CubeIDE调试项目现stm32 no source available for “main() at 0x800337c:

使用CubeIDE调试项目现stm32 no source available for "main() at 0x800337c&#xff1a; 问题描述 使用CubeIDE编译工程代码和下载都没有任何问题&#xff0c;点击Debug调试工程时&#xff0c;出现stm32 no source available for "main() at 0x800337c 原因分析&a…

数据结构与算法笔记:实战篇 - 剖析微服务接口鉴权限流背后的数据结构和算法

概述 微服务是最近几年才兴起的概念。简单点将&#xff0c;就是把复杂的大应用&#xff0c;解耦成几个小的应用 。这样做的好处有很多。比如&#xff0c;这样有利于团队组织架构的拆分&#xff0c;比较团队越大协作的难度越大&#xff1b;再比如&#xff0c;每个应用都可以独立…

nginx优化和防盗链

1、隐藏版本号 [roottest1 conf]# vim nginx.conf ​ server_tokens off; ​ 2、防盗链 修改用户和所在组 [roottest1 conf]# vim nginx.conf ​ #user nginx nginx; #表示主进程master会有root创建&#xff0c;子进程会有nginx用户来创建。 3、设置页面的缓存时间 主要是…

2024-2025年本田维修电路图线路图接线图资料更新

此次更新了2024-2025年本田车系电路图资料&#xff0c;覆盖市面上99%车型&#xff0c;包括维修手册、电路图、新车特征、车身钣金维修数据、全车拆装、扭力、发动机大修、发动机正时、保养、电路图、针脚定义、模块传感器、保险丝盒图解对照表位置等等&#xff01; 汽修帮手汽…

15- 22题聚合函数 - 高频 SQL 50 题基础版

目录 1. 相关知识点2. 例子2.15 - 有趣的电影2.16 - 平均售价2.17 - 项目员工 I2.18 - 各赛事的用户注册率2.19 - 查询结果的质量和占比2.20 - 每月交易 I2.21 - 即时食物配送 II2.22 - 游戏玩法分析 IV 1. 相关知识点 函数 函数含义order by排序group by分组between 小值 an…

Sping源码(九)—— Bean的初始化(非懒加载)—mergeBeanDefinitionPostProcessor

序言 前几篇文章详细介绍了Spring中实例化Bean的各种方式&#xff0c;其中包括采用FactoryBean的方式创建对象、使用反射创建对象、自定义BeanFactoryPostProcessor以及构造器方式创建对象。 创建对象 这里再来简单回顾一下对象的创建&#xff0c;不知道大家有没有这样一个疑…

边缘混合计算智慧矿山视频智能综合管理方案:矿山安全生产智能转型升级之路

一、智慧矿山方案介绍 智慧矿山是以矿山数字化、信息化为前提和基础&#xff0c;通过物联网、人工智能等技术进行主动感知、自动分析、快速处理&#xff0c;实现安全矿山、高效矿山的矿山智能化建设。旭帆科技TSINGSEE青犀基于图像的前端计算、边缘计算技术&#xff0c;结合煤…

【原创实现 设计模式】Spring+策略+模版+工厂模式去掉if-else,实现开闭原则,优雅扩展

1 定义与优点 1.1 定义 策略模式&#xff08;Strategy Pattern&#xff09;属于对象的⾏为模式。他主要是用于针对同一个抽象行为&#xff0c;在程序运行时根据客户端不同的参数或者上下文&#xff0c;动态的选择不同的具体实现方式&#xff0c;即类的行为可以在运行时更改。…

WIN32核心编程 - 数据类型 错误处理 字符处理

公开视频 -> 链接点击跳转公开课程博客首页 -> 链接点击跳转博客主页 目录 数据类型 基本数据类型 Win32基本数据类型 错误处理 C语言中的错误处理 C中的错误处理 Win32中的错误处理 字符处理 C/C WIN32 字符处理 数据类型 基本数据类型 C/C语言定义了一系列…

双指针系列第 8 篇:盛水最多的容器。几句话讲明白!

Leetcode 题目链接 思路 取首尾双指针和水量如下所示&#xff0c;设高度函数为 h ( i ) h(i) h(i)&#xff0c;在下图中 h ( l ) < h ( r ) h(l) < h(r) h(l)<h(r)。 观察以 l l l 为左边界所能构成的其他水量&#xff0c;与矮的右边界搭配结果如下。 与高的…