Java算法之哈希算法

Java算法之哈希算法

哈希表

哈希表(Hash Table),也称为散列表,是一种根据关键码值(Key Value)直接进行访问的数据结构。它通过哈希函数(Hash Function)将关键码值映射到哈希表中的一个位置,以实现数据的快速插入、删除和查询操作。

哈希表主要由一个数组构成,数组的每个元素被称为哈希桶(Bucket)或槽(Slot),其中存放着键-值对。哈希函数是哈希表的核心部分,它将任意长度的输入(Key)映射为固定长度的输出(Hash值)。通过这个映射,哈希表能够快速定位到存储特定键值对的位置,从而实现高效的查找操作。

哈希表具有非常高的空间效率和时间效率。在空间效率方面,哈希表不需要为每个键都保存一个位置,而是通过哈希函数将键映射为一个哈希值,然后将键放置在对应的位置上,因此其空间利用率通常很高。在时间效率方面,哈希表的插入、查找和删除操作的时间复杂度均为O(1),即与元素数量多少无关,因此能够实现非常快速的查找。

然而,哈希表也存在一些潜在的问题,如哈希冲突。哈希冲突是指不同的键经过哈希函数后映射到了同一个哈希值的情况。为了处理这种情况,哈希表通常使用一些冲突解决策略,如链地址法或开放地址法等。

总的来说,哈希表是一种高效、灵活的数据结构,广泛应用于各种需要快速查找的场景,如数据库索引、缓存系统等。

接下来,就介绍一下哈希表的实现。

定义一个哈希表

static class Entry{int hash;//哈希码Object key;//键Object value;//值Entry next;public Entry(int hash, Object key, Object value) {this.hash = hash;this.key = key;this.value = value;}}
  1. 类定义:
static class Entry {

这里定义了一个静态内部类Entry。静态内部类意味着这个类不需要外部类的实例就可以被实例化。在HashMap的实现中,这样的设计有助于减少内存占用,因为每个Entry对象不需要持有对外部HashMap对象的引用。

  1. 成员变量:
int hash;//存储键的哈希码。当向哈希表中插入一个键值对时,会先计算键的哈希码,然后根据这个哈希码确定键应该放在哈希表的哪个位置。  
Object key;// 存储键。键用于唯一标识一个键值对。  
Object value;//存储值。与键相关联的值。  
Entry next; //指向下一个`Entry`的引用。这是链地址法解决哈希冲突的一种常见方式。当两个或多个键的哈希码相同(即产生了哈希冲突)			//时,这些键对应的`Entry`对象会形成一个链表,通过`next`字段链接在一起。
  1. 构造函数:
public Entry(int hash, Object key, Object value) {  this.hash = hash;  this.key = key;  this.value = value;  
}

这是Entry类的构造函数,它接受一个哈希码、一个键和一个值作为参数,并将它们分别赋值给类的成员变量。

HashMap的实际实现中,这个Entry类(或其类似的形式)会被用来在内部存储键值对。当向HashMap中插入一个键值对时,会先计算键的哈希码,然后根据这个哈希码确定键应该放在哈希表的哪个位置(通常是一个数组的索引)。如果那个位置已经有一个或多个Entry(即发生了哈希冲突),新的Entry会被添加到链表的末尾。

当从HashMap中查找一个键时,会再次计算该键的哈希码,然后定位到哈希表中的相应位置。然后,会遍历该位置上的链表,直到找到匹配的键或遍历完整个链表(即键不存在)。

总的来说,这个Entry类是实现哈希表数据结构的关键部分,它负责存储键值对并处理哈希冲突。

哈希表的get方法

Object get(int hash,Object key){int index = hash&(table.length-1);if(table[index]==null){return null;}Entry p = table[index];while(p!=null){if(p.key.equals(key)){return p.value;}p = p.next;}return null;}

你提供的get方法是一个典型的从哈希表中根据键(Key)获取值(Value)的实现。这里我为你详细解释这个方法的每个部分:

方法定义
Object get(int hash, Object key) {

这个方法接受两个参数:hash是键的哈希码,key是你要查找的键。方法的返回类型是Object,这意味着它可以返回任何类型的值。

计算索引
int index = hash & (table.length - 1);

这行代码计算了键在哈希表中的索引位置。这里使用了位与操作(&)和哈希表长度减一(table.length - 1)来确保索引在哈希表的有效范围内。这种计算方式通常用于实现哈希表的开放寻址法中的线性探测或二次探测,但在这里它更像是用于数组形式的哈希表,其中数组的每个位置可能包含一个链表来处理哈希冲突。

检查索引位置的链表头
if (table[index] == null) {  return null;  
}

这里首先检查哈希表中指定索引位置的链表头是否为null。如果是null,说明该位置没有链表,因此键肯定不在哈希表中,方法返回null

遍历链表
Entry p = table[index];  
while (p != null) {  if (p.key.equals(key)) {  return p.value;  }  p = p.next;  
}

如果链表头不为null,代码会遍历链表以查找键。遍历过程中,使用equals方法比较当前节点的键与要查找的键是否相等。如果找到匹配的键,则返回对应的值。如果遍历完整个链表都没有找到匹配的键,方法会返回null

返回结果
return null;

如果在哈希表中没有找到与给定键匹配的项,方法最终会返回null

总结

这个get方法实现了在哈希表中根据键查找值的基本逻辑。它首先计算键的哈希码对应的索引位置,然后检查该位置是否有链表,并遍历链表以查找匹配的键。如果找到,返回对应的值;否则,返回null。这种方法是哈希表实现中常见的查找逻辑,特别是在处理哈希冲突时使用链表作为解决方案时。

哈希表的put方法

void put(int hash,Object key,Object value){int index = hash & (table.length-1);if(table[index] == null) {//如果此处有空位直接新增table[index] = new Entry(hash, key, value);}else {//如果没有空位,遍历下面的元素Entry p = table[index];while (true) {//如果此元素下的key与新增的key相同,则更新元素if (p.key.equals(key)) {p.value = value;return;}//如果p的下一个元素为空,则需要在下一个空位上新增,此时跳出循环,执行新增操作if(p.next==null){break;}p=p.next;}//进行新增操作p.next = new Entry(hash,key,value);}//数组大小变大size++;}

你提供的put方法是一个用于向哈希表中插入键值对的实现。下面我将为你详细解释这个方法的每个部分:

方法定义
void put(int hash, Object key, Object value) {

这个方法接受三个参数:hash是键的哈希码,key是要插入的键,value是与键相关联的值。

计算索引
int index = hash & (table.length - 1);

这行代码与get方法中的计算索引的方式相同,用于确定键应该插入到哈希表的哪个位置。

检查索引位置的链表头
if (table[index] == null) {  // 如果此处有空位直接新增  table[index] = new Entry(hash, key, value);  
} else {  // 如果没有空位,遍历下面的元素  Entry p = table[index];  ...  
}

首先检查哈希表中指定索引位置的链表头是否为null。如果是null,说明该位置还没有链表,直接创建一个新的Entry对象并将其放在该位置。

遍历链表

如果链表头不为null,则开始遍历链表:

while (true) {  // 如果此元素下的key与新增的key相同,则更新元素  if (p.key.equals(key)) {  p.value = value;  return;  }  // 如果p的下一个元素为空,则需要在下一个空位上新增,此时跳出循环,执行新增操作  if (p.next == null) {  break;  }  p = p.next;  
}

遍历链表时,会检查每个Entry的键是否与要插入的键相等。如果找到相等的键,则更新该Entry的值并返回。如果遍历到链表的末尾(即p.nextnull),则跳出循环,准备在链表末尾插入新的Entry

在链表末尾插入新的Entry
// 进行新增操作  
p.next = new Entry(hash, key, value);

在链表的末尾插入一个新的Entry对象,将其next字段设置为null

更新哈希表大小
// 数组大小变大  
size++;
总结

这个put方法实现了向哈希表中插入键值对的基本逻辑。它首先计算键的哈希码对应的索引位置,然后检查该位置是否有链表。如果没有链表,则直接在该位置创建一个新的Entry。如果有链表,则遍历链表以查找是否有相同键的Entry。如果找到,则更新其值;如果没找到,则在链表末尾插入新的Entry。最后,更新哈希表中键值对的数量。

哈希表的remove方法

    Object remove(int hash,Object key){//根据hash码的值找出索引int index = hash & (table.length-1);//如果当前索引为空,那么直接返回空if(table[index]==null){return null;}//定义两个指针,一个为前驱一个为后继Entry p = table[index];//前驱初始化为nullEntry prve = null;//当p不为null时,执行循环while(p!=null) {//如果找到了key的值相同,那么执行删除操作if (p.key.equals(key)) {//当前驱为null时,说明只有一个结点,直接让索引处的值指向p.nextif (prve==null){table[index] = p.next;}//如果前驱不为null,那就让前驱的next指向p的nextelse {prve.next = p.next;}//执行完删除操作,size减一size--;//返回删除的值return p.value;}//更新prve和p的值prve = p;p=p.next;}//如果没有找到相对应的值,返回空return null;}
方法定义
java复制代码Object remove(int hash, Object key) {

该方法接受两个参数:hash是键的哈希码,key是要删除的键。方法返回被删除的值,如果键不存在则返回null

计算索引
java复制代码int index = hash & (table.length - 1);

通过哈希码和哈希表长度计算键在哈希表中的索引位置。

检查索引位置的链表头
if (table[index] == null) {  return null;  
}

如果索引位置的链表头为null,说明没有链表存在,直接返回null

初始化前驱和后继指针
Entry p = table[index];  
Entry prve = null;

p用于遍历链表,prve用于记录p的前一个节点,初始化为null

遍历链表
while (p != null) {  if (p.key.equals(key)) {  ...  }  prve = p;  p = p.next;  
}

使用while循环遍历链表。如果找到键与要删除的键相同的Entry,则执行删除操作;否则,更新前驱和后继指针,继续遍历。

执行删除操作
if (prve == null) {  table[index] = p.next;  
} else {  prve.next = p.next;  
}  
size--;  
return p.value;

当找到要删除的Entry时,根据前驱是否为null来判断是删除链表头还是链表中间的节点。如果前驱为null,说明p是链表头,直接让索引处的链表头指向p的下一个节点;否则,让前驱的next指向p的下一个节点。无论哪种情况,都需要将size减一,并返回被删除的值。

返回结果
return null;

如果遍历完整个链表都没有找到要删除的键,则返回null

总结

这个remove方法实现了从哈希表中删除指定键及其对应的值的功能。它首先根据哈希码计算键的索引位置,然后遍历该位置的链表来查找要删除的键。找到后,根据前驱节点是否为null来执行不同的删除操作,并更新哈希表的大小。如果遍历完链表都没有找到要删除的键,则返回null

哈希表的resize方法

在哈希表中,当键值对数量达到某个阈值时,会进行数组的扩容(即创建一个新的更大的数组,并重新计算所有键值对的哈希码和索引位置),但在上面的代码中,还暂时没有实现这个方法,接下来就是实现这个方法,在put方法中,如果达到了这个阈值,就直接调用resize方法进行扩容。

private void resize(){Entry[] newtable = new Entry[table.length<<1];for(int i = 0;i< table.length;i++){//拿到每个链表头Entry p = table[i];if(p != null){/** 拆分链表,移动到新数组,拆分规律* 一个链表最多拆分成两个* hash & table.length == 0的一组* hash & table.length != 0的一组* 例如对于长度为8的table,现在有哈希值为0,8,16,24* 32,40,48的七个数据* 那么拆分后,0,16,32,48就为一组,剩下的为另一组* */Entry a = null;Entry b = null;Entry ahead = null;Entry bhead = null;while(p != null){if((p.hash&table.length)==0){if(a!=null){a.next = p;}else{ahead = p;}//分配到aa = p;}else{if(b!=null){b.next=p;}else{bhead = p;}b = p;}p = p.next;}if(a!=null){a.next = null;newtable[i] = ahead;}if(b!=null){b.next = null;newtable[i+table.length] = bhead;}}}
}

几个问题

/*为什么计算索引位置用式子:hash & (数组长度-1)
为什么旧链表会拆分成两条,一条hash & 旧数组长度==0,另一条hash & 旧数组长度!=0
为什么拆分后的两条链表,一个原索引不变,另一个是原索引+旧数组长度
它们都有个共同的前提:数组长度是2的n次方
1.hash % 数组长度等价于hash & (数组长度-1)
在十进制中,对于求10,100,1000这些数作为除数的余数,只需要看最后几位就可以了
因为前面的都被整除掉了。这是十进制求余的一个小规律,二进制也有类似的鬼绿
30 % 2 = 0,0011110 % 0000010 = 0000000
30 % 4 = 2, 0011110 % 0000100 = 0000010
30 % 8 = 2, 0011110 % 0001000 = 0000110
30 % 16 = 2, 0011110 % 0010000 = 0001110
30 % 32 = 2, 0011110 % 0100000 = 0011110
对于二进制的数,2用二进制表示为10,4为100,8为1000,以此类推通过上面式子可知,对于二进制来说
求2,4,8这种是2的n次方的数的余数,也可以只看后几位,比如2,只需要看被除数二进制的最后一位
那么4就要看被除数的后两位。
那么求余数,我们只需要保留后几位就可以了,对于2,保留后一位,4,保留后两位。根据按位与运算的规律
与0进行按位与运算,结果还是0,与1进行按位与运算,结果仍是原数,那保留后三位,只需要与0000111进行按位与运算即可
而111就是十进制中的7,所以计算索引位置时,我们可以用式子hash & (数组长度-1),因为数组长度是2的n次方是前提
2.一条hash & 旧数组长度==0,另一条hash & 旧数组长度 != 0
进行与运算其实就是检查更高位上的数字是否为1,如果为1,那么就应该是新索引
3.理解了第二个问题,那么第三个问题就迎刃而解了*/

Object.hashCode()方法

Object.hashCode() 是 Java 中所有对象的基类 Object 类的一个方法。该方法用于返回对象的哈希码值,这个哈希码通常用于数据结构,如哈希表(如 HashMapHashSet)。因为上面的方法中,哈希值是由人为输入的,但是人为输入哈希值,很容易出现冲突的情况,所以可以在此使用hashCode方法进行哈希值的获取。

基本概念

  • 哈希码(Hash Code):哈希码是一个整数,它是通过对象的内部信息(通常是对象的字段)计算出来的。如果两个对象根据 equals(Object) 方法是相等的,那么调用这两个对象的 hashCode 方法必须产生相同的整数结果。
  • 哈希表:哈希表是一种数据结构,它允许我们以平均常数时间复杂度进行插入、删除和查找操作。哈希表通过计算键(key)的哈希码来确定元素在表中的位置。

使用注意事项

  1. 一致性:如果两个对象根据 equals(java.lang.Object) 方法是相等的,那么调用这两个对象的 hashCode 方法必须产生相同的整数结果。
  2. 性能:哈希码的计算应该尽可能快,因为哈希表在插入、删除和查找元素时都会使用哈希码。
  3. 分布:理想情况下,哈希码应该均匀地分布在整个整数范围内,以减少哈希冲突并提高哈希表的性能。

String.hashCode()方法

在Java中,String 类的 hashCode() 方法返回该字符串的哈希码值,该值是根据字符串内容计算得出的。哈希码通常用于在哈希表中快速查找键,或者在其他数据结构(如哈希集合)中确定元素的唯一性。

String 类的 hashCode() 方法的设计保证了:

  1. 对于两个不同的字符串,只要它们的内容不同,它们的哈希码也很可能不同。这有助于在哈希表中区分不同的键。
  2. 对于相同的字符串(即内容完全相同的字符串),无论它们是在何时创建的,或者在程序中的哪个位置,它们的哈希码都是相同的。这保证了哈希表能够正确地识别相同的键。

注意,虽然哈希码冲突(即不同的字符串具有相同的哈希码)在理论上是可能的,但在实际使用中,String 类的 hashCode() 方法设计得非常出色,使得哈希码冲突的概率非常低。

Object的hashCode方法只能生成数字,而String类的hashCode方法可以生成字符串等,使用起来更为广泛。

力扣算法题

两数之和

首先是力扣题库的第一题

在这里插入图片描述
)

两数之和,第一次做的时候我是直接使用暴力循环来做的,居然没有超时,时间复杂度为O(n^2)

class Solution {public int[] twoSum(int[] nums, int target) {for(int i = 0;i<nums.length;i++){for(int j = i+1;j<nums.length;j++){if(nums[i]+nums[j]==target){return  new int[] {i,j};}}}return new int [0];}
}

那么既然现在学习了哈希表,就可以使用哈希表来进行时间复杂度为O(1)的算法

使用hash表进行的算法大概思路就是,遍历数组,然后确定与他相匹配的数字,在map中查找这个数字,如果能查到,返回这两个数字的索引,如果不能查到,那就把这个数字插入到map中。

class Solution {public int[] twoSum(int[] nums, int target) {HashMap<Integer,Integer> h = new HashMap<>();for(int i = 0;i< nums.length;i++){int x = target-nums[i];if(h.containsKey(x)){return new int[]{h.get(x),i};}else{h.put(nums[i],i);}}return null;}
}

在这里插入图片描述

执行用时击败了99.35%的用户,比使用暴力循环快了很多。

无重复字符的最长子串

在这里插入图片描述

这一题也可以用哈希表来进行求解

class Solution {  public int lengthOfLongestSubstring(String s) {  HashMap<Character, Integer> map = new HashMap<>();  int maxlength = 0;  int start = 0; int length = s.length();  for (int i = 0; i < length; i++) {  char c = s.charAt(i);  if (map.containsKey(c)) {//start = map.get(c)+1;start = Math.max(start, map.get(c) + 1);  }  map.put(c, i);  maxlength = Math.max(maxlength, i - start + 1);  }  return maxlength;  }  
}

基本思想是先定义两个指针,一个起始指针,一个末尾指针,对字符串进行遍历,检查字符串中是否存在这个字符,如果存在,就让起始指针向后移,在这里有一个点要注意,按照正常思维来说,应该是把起始指针向重复的那一位向后再移动一位,但是当中间有重复的时,有可能会出现start指针回退的情况,所以代码应该修改为

start = Math.max(start, map.get(c) + 1);  

字母异位词分组

在这里插入图片描述

class Solution {public List<List<String>> groupAnagrams(String[] strs) {HashMap<String,List<String>> map = new HashMap<>();for (String str:strs){//把字符串转化为数组,方便排序char[] c = str.toCharArray();//排序Arrays.sort(c);//再把数组转化为字符串String s = new String(c);//确定集合的位置,因为每一个映射中都含有一个集合List<String> list = map.get(s);//如果集合为空,就新建一个集合,并插入到哈希表中if(list == null){list = new ArrayList<>();map.put(s,list);}//在集合中插入数据list.add(str);}return new ArrayList<>(map.values());}
}

基本思路

  1. 遍历字符串数组,每个字符串中的字符重新排序后作为key值
  2. 所谓分组就是准备一个集合,把这些单词加入到key相同的集合中
  3. 返回分组结果

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

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

相关文章

【Linux实践室】Linux用户管理实战指南:用户权限切换操作详解

&#x1f308;个人主页&#xff1a;聆风吟_ &#x1f525;系列专栏&#xff1a;Linux实践室、网络奇遇记 &#x1f516;少年有梦不应止于心动&#xff0c;更要付诸行动。 文章目录 一. ⛳️任务描述二. ⛳️相关知识2.1 &#x1f514;图形化界面登录2.2 &#x1f514;使用login…

Collection与数据结构 链表与LinkedList (一):链表概述与单向无头非循环链表实现

1.ArrayList的缺点 上篇文章我们已经对顺序表进行了实现,并且对ArrayList进行了使用,我们知道ArrayList底层是使用数组实现的. 由于其底层是一段连续空间&#xff0c;当在ArrayList任意位置插入或者删除元素时&#xff0c;就需要将后序元素整体往前或者往后搬移&#xff0c;时…

mysql 条件/系统/加密/其它函数

学习了日期时间函数&#xff0c;接着学习条件、系统、加密和其它函数。 3&#xff0c;条件判断函数 条件判断函数也称为控制流程函数&#xff0c;根据满足的条件的不同&#xff0c;执行相应的流程。MySQL中进行条件判断的函数有IF、IFNULL和 CASE。 函数 说明 IF(expr,v1,v2…

二叉树寻找祖先问题-算法通关村

二叉树寻找祖先问题-算法通关村 1 最近公共祖先问题 LeetCode236&#xff1a;给定一个二叉树&#xff0c;找到该树中两个指定节点的最近公共祖先。 最近公共祖先的定义为&#xff1a;“对于有根树T 的两个节点 p、q&#xff0c;最近公共祖先表示为一个节点 x&#xff0c;满足是…

代码随想录笔记|C++数据结构与算法学习笔记-动态规划(〇)|

本文是简单的视频总结&#xff1a;从此再也不怕动态规划了&#xff0c;动态规划解题方法论大曝光 &#xff01;详细信息还请看代码随想录讲解视频 文章目录 动态规划的常见类型动态规划的误区动规五步曲DP数组以及下标的含义递推公式DP数组如何初始化DP数组遍历顺序打印DP数组…

GeoLite2 geoip数据库下载和使用

GeoLite2 数据库是免费的 IP 地理定位数据库&#xff0c;与MaxMind 的 GeoIP2 数据库相当&#xff0c;但准确度较低 。GeoLite2 国家、城市和 ASN 数据库 每周更新两次&#xff0c;即每周二和周五。GeoLite2 数据还可作为 GeoLite2 Country 和 GeoLite2 City Web 服务中的 Web …

Kotlin 中的类和构造方法

Kotlin 中的类与接口和 Java 中的类与接口还是有区别的。例如&#xff0c;Koltin 中的接口可以包含属性声明&#xff0c;与 Java 不同的是。Kotlin 的声明默认是 final 和 public 的。此外&#xff0c;嵌套的类默认并不是内部类&#xff1a;它们并没有包含对其它外部类的隐式引…

实施阶段(2024年3月)

【项目活动1】需求分析 学生&#xff1a;在系统中可以账号登陆&#xff0c;查看今日菜谱&#xff0c;点餐反馈。 食堂管理人员&#xff1a;对原始数据整合&#xff0c;显示菜品结果统计&#xff0c;并根据统计结果对菜品供应量进行调整反馈&#xff0c;避免浪费。 【项目活动…

MySQL开窗函数

测试环境&#xff1a;mysql8.0.18 官方文档&#xff1a;https://dev.mysql.com/doc/refman/8.0/en/window-functions.html 一、窗口函数介绍二、语法结构三、自定义窗口1.rows&#xff08;重点&#xff09;2.range3.默认窗口 四、常用窗口函数示例1.row_number & rank &…

C++之红黑树插入+查找功能实例(二百六十三)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a;多媒…

SeaTunnel 与 DataX 、Sqoop、Flume、Flink CDC 对比

产品概述 Apache SeaTunnel 是一个非常易用的超高性能分布式数据集成产品&#xff0c;支持海量数据的离线及实时同步。每天可稳定高效同步万亿级数据&#xff0c;已应用于数百家企业生产&#xff0c;也是首个由国人主导贡献到 Apache 基金会的数据集成顶级项目。 SeaTunnel 主…

当代深度学习模型介绍--循环神经网络(RNNs)

AI大模型学习 方向一&#xff1a;AI大模型学习的理论基础 模型和应用的多样化&#xff1a;随着研究的深入&#xff0c;深度学习领域出现了多种创新的模型架构&#xff1a; 卷积神经网络&#xff08;CNNs&#xff09;专门针对图像处理任务进行了优化&#xff0c;通过模拟生物视…

『Apisix系列』破局传统架构:探索新一代微服务体系下的API管理新范式与最佳实践

文章目录 『Apisix基石篇』『Apisix入门篇』『Apisix进阶篇』『Apisix安全篇』 『Apisix基石篇』 &#x1f680; 手把手教你从零部署APISIX高性能API网关 利用Docker-compose快速部署Apache APISIX及其依赖组件&#xff0c;实现高效的API网关搭建通过编写RPM安装脚本来自动化安…

IDEA MyBatisCodeHelper Pro最新版(持续更新)

目录 0. 你想要的0.1 包下载0.2 使用jh 1. 功能介绍2. 下载安装2.1 在idea中插件市场安装2.2 在jetbrains插件市场下载安装 3. 简单使用3.1 创建一个SpringBoot项目3.2 配置数据库3.3 一键生成实体类、mapper 0. 你想要的 0.1 包下载 测试系统&#xff1a;Windows&#xff08…

ERP与MES系统深度对接详细解决方案

此方案适合绝大部分生产型企业&#xff0c;企业如果有用到ERP&#xff0c;MES&#xff0c;WMS等其他系统的。可以借鉴以下的对接方案。 在ERP与MES系统对接的具体内容中&#xff0c;物料管理、出入库信息、物料清单&#xff08;BOM&#xff09;、生产计划、生产领料、生产用料的…

【吊打面试官系列】Redis篇 -Redis 回收进程如何工作的?

大家好&#xff0c;我是锋哥。今天分享关于 【Redis 回收进程如何工作的&#xff1f;】面试题&#xff0c;希望对大家有帮助&#xff1b; ​Redis 回收进程如何工作的&#xff1f; 一个客户端运行了新的命令&#xff0c;添加了新的数据。Redi 检查内存使用情况&#xff0c;如 果…

浅析AI大模型当前存在的技术瓶颈和限制及解决方案

方向五&#xff1a;未来发展趋势与挑战 提示&#xff1a;展望AI大模型学习的未来发展趋势&#xff0c;并讨论当前面临的主要挑战。可以关注新技术、新方法的出现&#xff0c;以及它们对AI大模型学习的影响&#xff1b;同时&#xff0c;也可以分析当前存在的技术瓶颈和限制&…

P2802 回家

P2802 回家 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 虽然是普及-难度的题&#xff0c;但是感觉细节有很多。 细节&#xff1a; bfs第一次到 ( i , j ) (i, j) (i,j)&#xff0c;但是距离不一定是最小的 鼠标是一次性物品 血量到达 ( x x , y y ) (xx, yy) (xx,yy)为…

【文献分享】 机器学习 + 分子动力学 + 第一性原理计算 + 热力学性质(熔化温度 热导率 热膨胀系数)

分享一篇关于机器学习 分子动力学 第一性原理 熔化温度&#xff08;熔化温度 & 热导率 & 热膨胀系数&#xff09;的文章。 感谢论文的原作者&#xff01; 关键词&#xff1a; 1. Al−Li alloy 2. Neural network potential 3. Molecular dynamics 4. Thermal pr…

wireshark 使用

wireshark介绍 wireshak可以抓取经过主机网卡的所有数据包&#xff08;包括虚拟机使用的虚拟网卡的数据包&#xff09;。 环境安装 安装wireshark: https://blog.csdn.net/Eoning/article/details/132141665 安装网络助手工具&#xff1a;https://soft.3dmgame.com/down/213…