奥利给!搞定JVM就靠它了!不看感觉错过一个亿!吐血整理两个月,原创!全网第一份超详细JVM知识点导图!!!

一、本人原创整理,基于尚硅谷教学视频以及深入理解Java虚拟机

   导图地址请查看评论

二、导图总体如下,每一章节,每一个细节点,都有超级详细的记录。以及一些图示,代码等。

1、总体概图,已经有6.8k浏览,近1000的拷贝了:
在这里插入图片描述

2、部分知识点截图
在这里插入图片描述

3、部分图示截图在这里插入图片描述

如果大家看了觉得有帮助,还请动动手指点赞转发,

整理这个导图,来之不易,还请大家多多支持!

三、最后说下怎么学习Java虚拟机,以及我为什么做这个导图

我一直觉得Java虚拟机应该很难,也没有学的打算。家里有一本深入理解Java虚拟机,看了2章就没在看了,感觉都是枯燥,难理解的概念。

直到今年2月份左右,我的心态彻底改变了(具体原因可以私聊)。为了能进一步提升专业知识,决心啃下JVM。

首先的想法是看书,肯定要找经典推荐的书籍,于是重新开始看深入理解Java 虚拟机,这里注意一定要看第三版,内容更新很多。对于初学来说,书上的内容显得晦涩难懂一些,而且模块之间关联逻辑有些强,各个章节之间知识点互相关联。这个时候,先不要放弃,能看懂多少记住多少,理解多少。坚持看完第一遍。

看完第一遍之后,我又继续去B站搜索java虚拟机的教学视频,这里首先看到了尚硅谷的,203集上篇教学,宋老师思路清晰,讲的很好,这里点赞支持下。于是三联操作之后,开始每天抽时间学习,由于有了书中第一遍的基础理解,视频中讲解的内容理解起来迅速很多。

这里要注意有些知识点必须要刻意的去背下来。比如最基本的JVM运行时数据区详细图。

刷完一遍视频之后,总觉的很多知识点我也懂了,但是不是那么扎实。一些问题也总是说一个大概的意思,不能完整的描述清楚。

重新刷视频的时候,我发现没有PPT,或者没有笔记,很难快速的去把知识点串起来。原本想记录成一篇篇的博客,但是实际发布2篇以后,发现效率太低,最终决定做导图。

java虚拟机细节的知识点还很多,我不推荐大家为了面试,而去背答案,这样对于知识体系的搭建其实没有帮助。

为了能在大脑中形成知识体系,我在看第二遍视频的时候,按照视频教学的课件思路,整理成了导图。给自己加深印象。也为了以后复习提供帮助。

process on 是一个挺好的整理图像的工具,本着为了能让更多人更好的学习Jvm,所以我将自己整理的成果免费发布出来,地址请查看留言,能给大家带来一些帮助,也是我的收获了。

最后感谢大家关注,回复jvm可以查看更多jvm资料,后面会整理更多原创资料免费分享给大家,奥利给!!!!

在这里插入图片描述

<think>好的,我现在需要解决用户的问题:找到一个算法,对于数组中的每个元素,在移除该元素后,确定剩余数组中最小的缺失非负整数。首先,我得仔细理解这个问题。 用户的需求是,对于数组中的每一个元素,当移除该元素之后,剩下的数组中最小的非负整数是什么。例如,假设原数组是[0,1,3],当移除第一个元素0后,剩下的数组是[1,3],此时最小的缺失非负整数是0;如果移除第二个元素1,剩下的数组是[0,3],此时最小的缺失是1;移除第三个元素3的话,剩下的是[0,1],最小缺失是2。所以对于每个元素,都要计算对应的结果。 首先,我需要考虑如何高效地计算每个元素被移除后的最小缺失数。最直接的思路是,对于数组中的每个元素,遍历数组,排除当前元素后,生成一个新数组,然后计算这个新数组的最小缺失非负整数。但这样的时间复杂度是O(n^2),当数组很大时,这样的方法效率会很低,可能适用。 所以,我需要想有没有更高效的方法。最小缺失非负整数的计算通常可以通过哈希集合或者排序来处理。例如,对于原数组的最小缺失数,可以将数组元素存入集合,然后从0开始逐个检查,找到第一个在集合中的数。或者,先对数组排序,然后遍历排序后的数组,找到第一个在正确位置的非负整数。 但在这个问题中,每个元素被移除后的数组都是同的,所以需要针对每个可能的子数组计算最小缺失数。如果每次都重新计算,时间复杂度确实很高。有没有办法预处理一些信息,从而减少重复计算? 比如,假设原数组的最小缺失数是M。当移除一个元素时,如果这个元素的值大于等于M,那么移除它会影响当前的最小缺失数,所以新的最小缺失数仍然是M。但如果移除的元素是小于M且存在于原数组中的某个数,那么有可能致新的缺失数变小。例如,原数组是[0,1,3],原最小缺失数是2。如果移除0,那么剩下的数组是[1,3],此时0就变成了缺失的,所以新的最小缺失数是0,比原来的M更小。这种情况下,移除的元素是小于原M的,并且这个元素是否是原数组中唯一的存在于0到M-1之间的某个数? 所以,这个问题的关键在于,当移除一个元素x时,如果x是原数组中在0到M-1之间的元素,并且x在原数组中只出现一次,那么移除x之后,x所在的这个位置可能成为新的缺失数。否则,如果x在原数组中出现了多次,或者x在0到M-1之间,那么移除它可能会改变最小缺失数。 那如何高效地处理每个元素的移除情况呢? 首先,我需要计算原数组的最小缺失数M。然后,对于每个元素x: - 如果x >= M,那么移除x后的最小缺失数仍然是M。 - 如果x < M,并且原数组中x出现的次数大于1,那么移除一个x后,剩下的数组中仍然存在x,所以此时最小缺失数仍然是M。 - 如果x < M,并且原数组中x出现的次数等于1,那么移除x之后,x这个数就存在了,所以需要检查此时新的最小缺失数是否变成了x。此时需要重新计算新的最小缺失数。 但如何快速确定在移除x后的数组中是否包含所有0到M-1的数? 或者,可以预处理数组中每个数的出现次数,然后当移除某个x时,如果x的出现次数是1并且x < M,那么新的最小缺失数可能是x,或者需要重新计算。 这似乎有些复杂。或许可以优化计算M,并记录哪些数在0到M-1之间是唯一的。 另一个思路是,预先计算数组中每个数的出现次数,然后找到原数组的最小缺失数M。然后,对于每个元素x: - 如果x >= M,那么结果就是M。 - 如果x < M,并且出现次数大于1,结果还是M。 - 如果x < M,并且出现次数等于1,那么需要检查移除x后的数组中是否0到M-1的每个数都存在。如果存在,则结果仍然是M;否则,结果为x,或者更小的某个数。 这种情况下,当移除一个唯一的x(x < M)时,需要确认数组中是否还包含所有0到M-1之间的数,除了x。此时,最小缺失数可能变成x,或者如果原来数组中有比x更小的缺失数吗?例如,原数组的M是2,那么0和1都存在。如果移除其中一个,比如移除0,那么此时M变为0。如果原数组的M是3,移除1的话,那么新的数组是否包含0、1、2?如果移除1之后,数组中还有0和2吗? 可能需要重新计算在这种情况下的最小缺失数。但如何高效地处理这种情况? 比如,原数组的最小缺失数M是3,即0、1、2都存在。当移除一个元素x=1,且x的出现次数是1,那么移除后,数组中0存在吗?2存在吗?假设存在的话,此时的最小缺失数变成1。否则,如果比如移除的是0,但原数组中的0出现次数是1,那么移除后的数组中0存在,所以最小缺失数是0。 所以在这种情况下,当x < M且x的出现次数是1时,移除后的最小缺失数是x,因为原来的M是基于0到M-1都存在的条件。但此时,移除x后,x存在,所以x一定小于M,所以新的最小缺失数是x吗? 例如,原数组的M是3,x=1,且出现次数为1。移除后,数组中的0、2都存在,但1存在。此时,最小缺失数是1,因为0存在,所以检查从0开始,0存在,下一个是1,存在,所以答案是1。 那是否当原数组的M是k,而x在0到k-1之间且出现次数是1时,移除后的最小缺失数就是x? 是的。因为原数组的最小缺失数是k,说明0到k-1都至少出现一次。当移除一个x属于0到k-1之间,并且出现次数为1,那么移除后x再存在,因此此时的最小缺失数就是x。比如,原数组包含0,1,2,M=3。移除1之后,数组包含0,2,此时最小缺失是1。所以这种情况下,移除后的最小缺失数就是x。但是如果原数组中的某些数在0到k-1之间出现多次,那移除其中一个可能影响。例如,原数组是0,0,1,2,M=3。当移除其中一个0时,剩下的数组中还有0存在,所以此时的最小缺失数仍然是3。所以这时候需要判断该元素x的出现次数是否为1。 综上,算法的大致步骤可能是: 1. 预处理原数组的最小缺失数M,并记录每个数出现的次数。 2. 对于数组中的每个元素x: a. 如果x >= M,那么移除后的最小缺失数是M。 b. 如果x < M,并且出现次数>1,那么移除后的最小缺失数是M。 c. 如果x < M,并且出现次数=1,那么移除后的最小缺失数是x。 这样是否正确? 那这样的话,问题就变得高效了,因为只需要计算原数组的M,统计各数的出现次数,然后对每个元素进行判断即可。时间复杂度是O(n)预处理,然后每个元素处理是O(1),总时间复杂度O(n)。 但需要验证这个逻辑是否正确。 例如,原数组为[0,1,3],M=2。每个元素的情况: - 移除0:出现次数为1,x=0 <2,所以结果应为0。正确。 - 移除1:出现次数为1,x=1 <2,所以结果应为1。正确。 - 移除3:x>=2,所以结果仍然是2。正确。 另一个例子,原数组是[0,0,1,2],M=3。每个元素: - 移除第一个0:出现次数是2,所以x=0 <3,出现次数>1,所以结果还是3。 - 移除第二个0:同上,结果3。 - 移除1:出现次数1,所以结果1。 - 移除2:出现次数1,结果2。 这样是否正确? 对于移除后的数组,比如移除第一个0后的数组是[0,1,2],M=3,正确。移除后的最小缺失数是3。 所以,这样的逻辑是正确的。那这样的话,这个算法的时间复杂度可以做到O(n),只需要预处理原数组的最小缺失数,并统计每个数的出现次数。 那问题转化为如何计算原数组的最小缺失数M,以及如何统计每个数的出现次数。 计算M的方法可以是: 将数组元素存入一个集合,然后从0开始依次检查,直到找到第一个在集合中的数。这种方法的时间复杂度是O(n),因为集合的查找是O(1)。 或者,可以使用排序的方法。先排序,然后遍历数组,找到第一个位置i,使得数组中的元素包含i。例如,排序后的数组,跳过负数,然后检查0、1、2等是否存在。例如,排序后的数组是[0,1,3],遍历到i=0,存在;i=1,存在;i=2,检查下一个元素是否是>=2。此时,第三个元素是3,所以中间缺少2。此时,M=2。 排序方法的时间复杂度是O(n log n),而集合方法的时间复杂度是O(n),所以更优的是集合方法。 所以,步骤: 1. 计算原数组的M,即最小缺失的非负整数。 2. 统计每个数在原数组中的出现次数,可以使用字典或者哈希表。 3. 对于数组中的每个元素x: a. 如果x >= M,则结果M。 b. 如果x < M,且出现次数>1,则结果M。 c. 否则(x < M且出现次数=1),结果是x。 那这样的话,算法的时间复杂度是O(n),因为预处理步骤是O(n)的,然后每个元素的处理是O(1)。 现在需要验证这个逻辑是否正确。比如,原数组是[1,2,3],那么M是0。对于每个元素x,当移除x时: 例如,原数组的M是0,所以管移除哪一个元素,结果都是0吗? 是的。例如,移除1后的数组是[2,3],最小缺失是0。所以无论x是否>=M(这里M=0,x>=0都是true),所以结果都是M=0。这种情况下,逻辑正确。 另一个例子,数组是[0,2,2,1]。原数组的最小缺失数是3。统计次数:0出现1次,1出现1次,2出现2次。所以: 移除0时,出现次数是1,且x=0 <3,所以结果为0。 移除2时,出现次数是2>1,所以结果3。 移除1时,出现次数是1,所以结果1。 这样是否正确? 原数组是0、22、1,包含0、1、2。所以M=3。当移除0后,剩下的数组是[2,2,1],包含1和2,所以最小缺失是0。正确。同理,移除1后的数组是[0,2,2],包含0、2,所以最小缺失是1。正确。 那这个逻辑是正确的。 所以,算法的大体思路是正确的。现在需要将这个思路转化为具体的实现步骤。 具体步骤: 1. 找出数组中的最小缺失非负整数M。 - 遍历数组,将所有元素存入集合。 - 从0开始递增,直到找到第一个在集合中的数,即为M。 2. 统计每个数的出现次数,可以使用哈希表(字典)来记录每个数出现的次数。 3. 遍历数组中的每个元素x: a. 如果x >= M,那么结果就是M。 b. 否则,检查x的出现次数: i. 如果次数>1,则结果还是M。 ii. 如果次数==1,则结果是x。 这样,每个元素的结果可以快速计算出来。 现在需要处理一些特殊情况,例如数组中有负数的情况。但根据问题描述,我们寻找的是非负整数的最小缺失数,所以负数可以忽略。例如,假设数组中有-1,在计算M时,集合中的元素包含负数,但我们在寻找M的时候会从0开始检查,所以负数会影响。例如,数组是[-1, 0, 2],M是1,因为0存在,但1存在。 现在编写代码: 例如,用Python: def find_smallest_missing(arr): # 计算原数组的最小缺失数M s = set(arr) m = 0 while m in s: m +=1 # 统计出现次数 freq = {} for num in arr: if num in freq: freq[num] +=1 else: freq[num] =1 # 遍历每个元素,计算结果 result = [] for x in arr: if x >= m: result.append(m) else: if freq[x] >1: result.append(m) else: result.append(x) return result 测试这个函数: 测试用例1:输入[0,1,3] 原数组的M是2。每个元素: 0出现次数1:结果0。 1出现次数1:结果1. 3 >=2:结果2. 所以输出应该是[0,1,2],正确。 测试用例2:输入[0,0,1,2] M是3。各元素: 0出现次数2:结果3. 0出现次数2:结果3. 1出现次数1:结果1. 2出现次数1:结果2. 所以输出是[3,3,1,2]. 测试用例3:输入[1,2,3] M是0。所有元素>=0,所以结果都是0。输出是[0,0,0]. 测试用例4:输入[-1,0,1,3] M是2。移除-1时,x=-1 <2,但原数组中-1的出现次数是1,所以结果是-1?但是问题要求的是非负整数的最小缺失,所以即使移除-1后的数组中的元素包含0、1、3,此时最小缺失的非负整数是2。所以这里逻辑有问题? 哦,这时候出现一个问题。我的逻辑是否有错误? 因为原数组中的元素可能包含负数,比如数组是[-1,0,1,3]。原数组的最小缺失非负整数是2。此时,当移除元素x=-1时,x < M=2,所以进入判断条件。此时,原数组中x的出现次数是1,所以结果应该是x=-1?但问题要求的是最小的缺失非负整数,所以结果应该还是0吗? 这说明我的逻辑存在错误。我的算法在计算时,如果x是负数的话,那么当移除它时,可能x < M,但x是负数,此时在移除后的数组中,如果原来的M的计算方式是正确的,那么此时是否会影响结果? 比如,原数组是[-1,0,1,3],原集合包含-1,0,1,3。所以M是2。当移除-1之后,剩下的数组是[0,1,3],此时的最小缺失非负整数是2。所以按照之前的逻辑,x=-1 < M=2,且出现次数是1,所以结果应该是x=-1,但实际正确的结果是2。这说明我的逻辑有问题。 那问题出在哪里? 原来我的逻辑假设当x < M且出现次数为1时,结果就是x。但这种情况仅在x是非负的情况下成立。当x是负数时,移除它会影响非负整数的最小缺失数,因为原数组的M是计算非负的。 比如,原数组的M是2,当移除一个负数元素x,此时剩下的数组中的非负元素是否仍然包含0到M-1(即0和1)?假设原数组包含0和1,那么移除负数x之后,0和1仍然存在,所以M还是2。所以在这种情况下,当x是负数且x < M,但x是负数,此时虽然满足x < M,但由于x是负数,所以移除它会影响非负整数的集合,所以结果还是原来的M。 所以我的算法在这种情况下会错误地将结果设为x(负数),而正确答案应该是M。 这说明之前的逻辑需要调整。必须确保x是非负数的情况下,才会致结果变化。因为问题的最小缺失数是针对非负整数的,所以当x是负数时,即使移除它,对非负数的存在与否没有影响。因此,当x是负数时,管是否被移除,原数组的M保持变,所以此时的结果还是原来的M。 因此,需要修改算法,将条件改为:只有当x是非负数且x < M,并且出现次数为1时,结果才是x。否则,结果还是M。 所以,正确的逻辑应该是: 对于每个元素x: 如果x >= M → 结果M 否则,如果x是非负数: 如果出现次数>1 → 结果M else → 结果x 否则(x是负数): 结果M 这样就能处理负数的情况。 所以,原来的算法需要修正,在判断的时候,要区分x是否为非负数。例如: 修改后的步骤: 1. 计算原数组的最小缺失非负整数M。 2. 统计每个数的出现次数。 3. 对于数组中的每个元素x: a. 如果x >= M → 结果M b. 否则,如果x >=0: i. 如果出现次数>1 → 结果M ii. 否则 → 结果x c. 否则(x <0)→ 结果M 这样,当x是负数时,无论出现次数如何,移除它会影响非负整数的最小缺失数。例如,原数组是[-1,0,1,3],当移除-1时,x是负数,所以结果M=2。原数组的M是2,结果正确。 另一个例子,原数组是[-5, -3, 0,1,3]。原M是2。当移除-5时,结果应为2。 再测试之前的错误情况:原数组是[-1,0,1,3]。当移除-1,结果为2。而按之前的错误逻辑,结果会变成-1,但正确结果应该是2。修正后的逻辑下,x=-1 < M=2,但x是负数,所以结果M=2。 这说明修正后的逻辑是正确的。 因此,算法需要调整,将x为非负数的条件加入进去。 现在修改代码: def find_smallest_missing(arr): s = set(arr) m = 0 while m in s: m +=1 freq = {} for num in arr: if num in freq: freq[num] +=1 else: freq[num] =1 result = [] for x in arr: if x >= m: result.append(m) else: if x >=0: # 只有x是非负数的情况下才可能影响结果 if freq[x] >1: result.append(m) else: result.append(x) else: # x是负数,影响非负数的集合 result.append(m) return result 测试这个修正后的代码: 测试用例4:输入[-1,0,1,3],原M是2。当处理元素-1时,x是负数,所以结果追加m=2。其他元素: 0是非负,出现次数1 → 结果0. 1出现次数1 → 结果1. 3 >=2 → 结果2. 所以输出是[2,0,1,2]。正确,因为当移除-1后的数组的最小缺失数是2,所以结果正确。 另一个测试用例:输入[0, -2, 1,3]。原M是2。对于元素0: x=0 <2,非负,出现次数1 → 结果0. 元素-2:负数 → 结果2. 元素1:非负,出现次数1 → 结果1. 元素3≥2 → 结果2. 所以输出是[0,2,1,2]。正确。 现在另一个情况:原数组是[0,0,1,2]。M是3。当移除0(出现次数2),所以结果应为3。当移除1(出现次数1),结果1。 这说明修正后的代码是正确的。 综上,算法的正确性现在得到了保证。该算法的时间复杂度是O(n),空间复杂度是O(n)(存储集合和频率字典)。对于每个元素,只需要常数时间的判断,因此非常高效。 接下来,用户可能需要具体的代码实现,例如Python代码。同时,可能需要解释算法的时间复杂度,以及可能的优化点。 总结步骤: 1. 找出原数组的最小缺失非负整数M。 2. 统计每个数的出现次数。 3. 对于每个元素x,判断: - 如果x >=M → 结果M - 如果x是负数 → 结果M - 否则(x是0~M-1): - 出现次数>1 → 结果M - 出现次数=1 → 结果x 因此,代码实现正确。</think>要解决数组中每个元素被移除后剩余数组的最小缺失非负整数问题,可以采用以下高效算法: ### 算法步骤 1. **计算原数组的最小缺失非负整数** 遍历数组,将所有元素存入集合。从$0$开始递增,找到第一个在集合中的数,记为$M$。例如,数组$[0,1,3]$的最小缺失数是$2$。 2. **统计元素出现次数** 使用哈希表记录每个元素的出现次数。例如,数组$[0,0,1]$的统计结果为$\{0:2, 1:1\}$。 3. **遍历数组元素,确定移除后的最小缺失数** 对于每个元素$x$: - 若$x \geq M$或$x$为负数,移除后的最小缺失数仍为$M$。 - 若$x < M$且为非负数: - 若$x$出现次数$>1$,移除后最小缺失数为$M$。 - 若$x$出现次数$=1$,移除后最小缺失数为$x$。 ### 代码实现 ```python def find_smallest_missing(arr): num_set = set(arr) m = 0 while m in num_set: m += 1 freq = {} for num in arr: freq[num] = freq.get(num, 0) + 1 result = [] for x in arr: if x >= m or x < 0: result.append(m) else: if freq[x] > 1: result.append(m) else: result.append(x) return result ``` ### 示例分析 以数组$[0,1,3]$为例: - 移除$0$后,数组变为$[1,3]$,最小缺失数为$0$。 - 移除$1$后,数组变为$[0,3]$,最小缺失数为$1$。 - 移除$3$后,数组变为$[0,1]$,最小缺失数为$2$。 代码输出结果为$[0,1,2]$,与预期一致。 ### 复杂度分析 - **时间复杂度**:$O(n)$,其中$n$为数组长度。计算$M$和统计频率各需一次遍历。 - **空间复杂度**:$O(n)$,用于存储集合和哈希表。 ### 应用场景 此算法适用于需要快速分析动态数组状态的场景,如实时数据处理或大规模数据预处理。例如,在数据库查询优化中,快速判断索引的连续性缺失[^1]。
评论 29
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值