自定义博客皮肤VIP专享

*博客头图:

格式为PNG、JPG,宽度*高度大于1920*100像素,不超过2MB,主视觉建议放在右侧,请参照线上博客头图

请上传大于1920*100像素的图片!

博客底图:

图片格式为PNG、JPG,不超过1MB,可上下左右平铺至整个背景

栏目图:

图片格式为PNG、JPG,图片宽度*高度为300*38像素,不超过0.5MB

主标题颜色:

RGB颜色,例如:#AFAFAF

Hover:

RGB颜色,例如:#AFAFAF

副标题颜色:

RGB颜色,例如:#AFAFAF

自定义博客皮肤

-+
  • 博客(32)
  • 问答 (2)
  • 收藏
  • 关注

原创 代码随想录算法跟练 | Day20 | 二叉树 Part05

今天的题目都比较简单,都可以用简单的递归来实现,重点是熟练掌握树的性质和相关的应用。

2024-07-03 15:58:58 497

原创 代码随想录算法跟练 | Day17 | 二叉树 Part04

使用递归进行层层切割即可,以后序数组的最后一个元素为切割点,先切中序数组,根据中序数组,反过来再切后序数组。一层一层切下去,每次后序数组最后一个元素就是节点元素。可以用递归实现,优先选取最左侧节点,仅当左为空时,才考虑右子树,同时维护两个个变量存储最大深度和结果的值即可。简单使用递归实现即可,不断传入新的目标值,当当前节点时叶子节点时,判断目标值是否等于节点值即可。的路径,这条路径上所有节点值相加等于目标和。是同一棵树的后序遍历,请你构造并返回这颗。是指没有子节点的节点。是二叉树的中序遍历,

2024-07-02 01:25:53 472

原创 代码随想录算法跟练 | Day16 | 二叉树 Part03

的定义如下:在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。本题也是简单题,通过递归返回所有左叶子节点的值即可,但需要注意,虽然是返回左节点的值,依然需要对右节点进行遍历,右节点有可能也存在左叶子节点。题目中平衡二叉树指左右子树最大深度差不超过 1,因此按之前的思路,求出树的最大深度和最小深度,最后判断差值绝对值是否小于等于 1 即可。,返回所有从根节点到叶子节点的路径。是指没有子节点的节点。,求出该树的节点个数。

2024-06-30 21:34:41 458

原创 代码随想录算法跟练 | Day15 | 二叉树 Part02

本题也是可以使用递归来实现,当根节点为空时,可以判断这个二叉树是对称的,接下来,就是如果左右子树也满足对称,当前二叉树就是对称的。本题可以使用递归的思路,我们可以注意到,翻转一个二叉树后,二叉树中的所有子树其实都经过过翻转,因此可以通过递归让根节点的左右子树先进行翻转操作,然后将根节点的左指针指向右子树根节点,右指针指向左子树根节点,完成当前树的翻转。当当前节点不为空时,该树的最大深度就是左子树的最大深度和右子树最大深度的最大值加一,至此,完成递归。最小深度是从根节点到最近叶子节点的最短路径上的节点数量。

2024-06-29 10:00:34 361

原创 代码随想录算法跟练 | Day14 | 二叉树 Part01

二叉树顾名思义,就是由一个或多个节点组成的树形结构,树形结构中起始的节点被称为根节点,而二叉则表示每个节点有两个子节点,通常将一个节点的左右节点为根节点构成的树称为这个节点的左子树和右子树,如果一个节点的左子树和右子树都为空,它被称为二叉树的叶子结点。这是我们平时最常见的二叉树的存储方式,每个节点上存储了当前节点的值,还有左右指针分别指向左右子树的根节点的地址。今天,主要是二叉树的基础知识,包括二叉树的结构、存储方式和遍历方式。,按这样的规律进行存储,结果类似二叉树的层序遍历。,那么它的左孩子就是。

2024-06-28 02:32:43 371

原创 代码随想录算法跟练 | Day13 | 栈与队列 Part03

这题和昨天的题目是一样的,在 Java 中这种题统一使用优先队列来做即可,通过优先队列来找到滑动窗口中的最大值,而当发现队首不在滑动窗口范围内时,将队首弹出。这题也和昨天一样,用哈希表存储元素出现频率,然后通过优先队列来进行排序并弹出。的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的。滑动窗口每次只向右移动一位。返回 滑动窗口中的最大值。,请你返回其中出现频率前。

2024-06-27 00:46:06 501

原创 代码随想录算法跟练 | Day11 | 栈与队列 Part02

之前接触过这种题,本身很简单,对于正确的逆波兰表达式,只需要使用一个栈来存储数字,然后当遍历到算符时,将栈顶两个数组取出计算后,将结果压回栈中即可,最后栈中的数就是计算结果。这道题也是优先队列的应用,相比上一题简单很多,只需要先统计数组中各个元素的出现频率,然后将元素和出现的频率放到优先队列中,按元素出现频率从大到小排序。因为是求滑动窗口中的最大值,我们只需要维护一个存储了滑动窗口中元素的优先队列即可,然后由题可知,结果数组长度为。的范围内,它才是有效的,此处,可以使用循环将不符合要求的元素全部出队。

2024-06-26 10:33:35 498

原创 代码随想录算法跟练 | Day10 | 栈与队列 Part01

题目让我们使用两个栈来实现队列,已知栈是先进后出的数据结构,队列是先进先出,因此显然要想使得先被压入栈中的元素先被弹出,只使用一个栈是不够的,需要一个辅助栈来实现,将原栈中的元素都放入到辅助栈里再进行出栈操作,就能够做到先压入的元素先被弹出来。请你仅使用两个队列实现一个后入先出(LIFO)的栈,并支持普通栈的全部四种操作(请你仅使用两个栈实现先入先出队列。总结:今天的题目基本都比较简单,核心都是深入理解栈和队列的特点。(双端队列)来模拟一个队列 , 只要是标准的队列操作即可。你所使用的语言也许不支持队列。

2024-06-26 01:59:53 614

原创 代码随想录算法跟练 | Day9 | 字符串 Part02

中可能会存在前导空格、尾随空格或者单词间的多个空格。返回的结果字符串中,单词间应当仅用单个空格分隔,且不包含任何额外的空格。,将其中不为空格的内容拼接成字符串单词后存入到栈中,然后将栈中元素依次出栈构建新的字符串即可。当字符不匹配的时候,指针应该移动到失配下标 - 1的前缀表数组的值的下标。字符串的第一个匹配项的下标(下标从 0 开始)。,最长相同前后缀长度为 0。,最长相同前后缀长度为 1。,最长相同前后缀长度为 0。,最长相同前后缀长度为 1。,最长相同前后缀长度为 2。,下标 0 处子串为。

2024-06-23 23:58:41 366

原创 代码随想录算法跟练 | Day8 | 字符串 Part01

的额外空间,因此要考虑在原数组上进行修改,所以想到了使用前面学的双指针。通过头尾双指针向中间遍历,然后依次交换头尾指针指向的字符,即可做到字符串反转。最简单的思路就是创建一个等长的数组,然后反向遍历原数组填入到新数组中,但本题要求是。今天的题目大体上都是字符串中最基础的题型,熟练掌握前面学习的双指针就可以完成了。使用双重循环实现即可,外层循环确定反转的起始终止位置,内层循环进行反转操作。编写一个函数,其作用是将输入的字符串反转过来。不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用。

2024-06-23 12:03:58 480

原创 代码随想录算法跟练 | Day7 | 哈希表Part2

看到四数相加,第一反应是双指针的题目,然后才发现这题和之前的双指针不一样。因此我们可以用前一天的两数相加的思路来做这一题,将四个数组两两组合相加,并存储结果。的数了,可以直接返回结果。反之,可以从第二个数的位置到数组最后一位之间使用二分法开始查找第三个数。这题思路与上一题基本一致,不过就是二分法的时候,是用两端指针的值进行判断,而不是。因为我的数组是从小到大排序的,如果第一个数大于。这题的思路和之前的字母异位词基本相同,只不过限制条件没有那么苛刻,只需要。中的字符出现次数存储到一个哈希表中,然后遍历。

2024-06-02 18:24:14 515

原创 代码随想录算法跟练 | Day6 | 哈希表Part1

这道题初看没有什么头绪,如果输入是快乐数,那判断很简单,但是如果不是快乐数,陷入无限循环,如何终止循环就是一个问题了。但通过这个计算方式我们可以知道,每次计算的结果肯定是受限在一个范围内的,而如果这个范围内的数字在结果中重复出现了,就可以判定是陷入了。这题和上一题类似的地方是要考虑元素的出现,但并不需要统计出现次数,而是要保证结果中元素的唯一性,所以可以使用哈希表的特殊应用。来对每次计算的结果进行暂存,如果在集合中重复出现了,就是陷入无限循环,返回。的那 两个 整数,并返回它们的数组下标。

2024-06-01 23:15:16 736

原创 代码随想录算法跟练 | Day4 | 链表Part2

已知倒数第 n 个节点其实就是最后一个节点往前推 n 个节点,那么遍历的时候,我们一开始可以使用两个指针,一个指针先于另一个指针 n 个节点到达链表结尾,此时慢的那个指针的位置就是我们找的位置。这道题也是用追及的思路,使用快慢指针,快指针一次走两步,慢指针一次走一步,因此在存在环的情况下,相当于快指针每次追一步,两个指针会在环的入口处相遇。本题的一个重点在于,节点的数值相同不代表节点相同,应该是节点的地址相同,这一点在 Java 中可以直接用。,返回链表开始入环的第一个节点。个结点,并且返回链表的头结点。

2024-05-28 01:17:15 487

原创 代码随想录算法跟练 | Day3 | 链表Part1

而在整个链表中,只有头结点没有前驱节点,所以我们可以创建一个虚拟头结点指向头结点,来保证所有操作的一致性。接下来只需要维护两个指针依次指向遍历到的节点,和遍历到的节点的前驱节点即可。在链表题目中,解题最关键的地方就是不能吝啬创建临时节点来作为辅助。在本题里,传入一个链表,要求删除指定的元素,而要想删除链表中的某一个节点,需要定位到它的前一个节点,然后把前一个节点的。感觉理解了虚拟头结点的用法之后,这道题也比较简单了,先创建一个指向空链表的虚拟头结点,然后遍历原链表,不断将原节点头插到新链表中即可。

2024-05-24 22:28:01 876 1

原创 代码随想录算法跟练 | Day2 | 数组Part2

我们可以发现,在暴力解法里,每次更新起始点后的累加计算里,其实出现了较多的重复计算,我们可以考虑是否有办法避免这种情况。因此有如下实现思路:维护两个指针标记当前数组还未进行计算的部分的首尾,然后计算这两个指针指向元素的平方值,将得到的较大的值。,我们可以想到,用两个指针来标记区间的首尾,然后维护一个变量来表示这个区间所有元素的总和,这就是。我们只需要不断滑动区间的首尾指针来标记合适的区间范围,就可以通过单次遍历操作来达成题目的需求。到新的数组序列中(可以对新的数组维护一个从后往前移动的指针),保证。

2024-05-23 22:39:26 798 1

原创 代码随想录算法跟练 | Day1 | 数组基础Part1

我们可以很轻松地想到暴力解法,即遍历数组的同时进行判断,如果出现需要移除的元素,则通过循环将该元素后的所有元素前移一位即可,同时维护一个变量记录数组长度就能满足所有需求。通过快指针去对数组进行遍历操作,慢指针来标记当前最高有效位的下一位,然后把遍历到的有效元素存入到慢指针指向的位置即可,这种算法因为避免了数组元素批量移动时的遍历操作,时间复杂度是。这部分知识点相对来说比较简单,通常情况下,暴力解法也能满足需求,但是当出现重复遍历的情况时,其实可以考虑一些常见的优化方式,如这里的双指针法。

2024-05-22 13:12:26 1143 1

原创 《深入理解 Java 虚拟机》学习笔记 Day16(HotSpot 垃圾收集算法的实现细节:根节点枚举)

可达性分析算法从 GC Roots 集合中查找引用链的操作里,固定可作为的 GC Roots 的节点目标较为明确,但是要想做到高效的查找并非容易的事,尤其是现在 Java 程序越来越庞大,逐个检查引用肯定要消耗不少时间。中才能正常进行,在这个过程里,分析时根节点集合的对象引用关系不能变化,如果这一点不能保证,分析结果的准确性就不能保证,这也是为什么垃圾收集过程里,根节点枚举必须暂停用户线程。前面学习的垃圾收集相关算法在实现时,对于执行效率有严格的考量,因此了解实现细节也是比较重要的一点。

2024-05-11 00:05:10 311 1

原创 《深入理解 Java 虚拟机》学习笔记 Day15(常见垃圾收集算法:标记 - 清除算法、标记 - 复制算法、标记 - 整理算法)

过去有一种名为“半区复制”的垃圾收集算法,即把可用内存按容量划分为大小相等的两部分,每次只使用其中的一块内存,当这一块内存用完了,就把内存中还存活的对象复制到另一块内存上,把使用过的内存一次清理完毕,这样简单高效,但是缺点也很明显——对于老年代的特性,标记 - 整理算法被提出,其标记过程与标记 - 清除算法一样,但后续的步骤不是直接对可回收对象进行清理,而是把所有存活对象向内存空间一端移动,然后直接清除掉边界以外的内存。标记对象的过程就是前几天学习的垃圾的判定过程,主要是使用可达性分析算法进行判断。

2024-05-10 01:11:46 618

原创 《深入理解 Java 虚拟机》学习笔记 Day14(垃圾收集器与内存分配:垃圾收集算法之分代收集理论)

即如果当前进行一次局限于新生代区域的垃圾收集(Minor GC),但新生代中的对象完全有可能被老年代所引用,为了找出这个区域内的存活对象,就需要在固定的 GC Roots 之外再额外遍历整个老年代所有对象来确保可达性分析结果的正确性,反过来也是如此。在这之后,如果一个区域内大部分对象都是朝生夕灭的,难以熬过垃圾收集过程,把他们集中在一起,每次收集的时候就只需要关注如何保留少量存活对象而不是标记大量要被回收的对象,就可以。,如“标记 - 复制算法”“标记 - 清除算法”“标记 - 整理算法”等。

2024-05-09 00:05:03 496

原创 《深入理解 Java 虚拟机》学习笔记 Day13(垃圾收集器与内存分配:回收方法区)

Java 堆中尤其是新生代中往往一次垃圾收集就能够回收 70% 至 99% 的内存空间,而方法区因为其苛刻的判定条件,回收成果往往远低于此。加载该类的类加载器已经被回收,这个条件除非是经过精心设计的可替换类加载器的场景,如OSGi、JSP的重加载等,否则通常是很难达成的。常量池中的一个常量,且虚拟机中没有其他地方引用这个字面量,而如果此时发生内存回收,而且。该类所有的实例都已经被回收,也就是Java堆中不存在该类及其任何派生子类的实例。其中,废弃常量的回收类似于 Java 堆中对象的回收,如果系统中。

2024-05-08 00:05:57 151

原创 《深入理解 Java 虚拟机》学习笔记 Day12(垃圾收集器与内存分配:对象的生存与死亡 - 过时的 finalize方法)

但是作者提到,不建议使用甚至是建议避免使用它,因为当前官方已经明确声明为不推荐的语法,它运行代价高昂,不确定性大,无法保证更改对象的调用顺序。要真正宣告一个对象死亡,最多会经历两次标记过程:如果对象在进行可达性分析后发现没有与 GC Roots 相连接的引用链,它将会被第一次标记,方法是对象拯救自己的最后一次机会,如果在方法中,对象重新与引用链上的任何一个对象建立关联,那么在第二次标记时它将会被移出“即将回收”的集合。或者其他方式都能做的更好更及时,这是在某些落后的教材中才会被推荐使用的方法。

2024-05-06 23:18:05 191 2

原创 《深入理解 Java 虚拟机》学习笔记 Day11(垃圾收集器与内存分配:可达性分析算法、Java 中引用的概念)

这个定义并没有问题,但在现在看来过于狭隘了,一个对象只有“被引用”和“未被引用”的两种状态,无法描述一些“食之无味弃之可惜”的对象。也被称为“幽灵引用”或者“幻影引用”,它是最弱的一种引用关系,一个对象是否有虚引用存在,完全不会对齐生存时间构成影响,也无法通过虚引用来取得一个对象实例。只被软引用关联的对象,在系统将要发生内存溢出异常前,会把这些对象列入到回收范围内进行第二次回收,如果这次回收后还没有足够的内存,才会抛出内存溢出异常。强引用就是最传统引用的定义,指的是在程序代码中普遍存在的引用赋值,如。

2024-05-05 23:48:45 665 1

原创 《深入理解 Java 虚拟机》学习笔记 Day10(垃圾收集器与内存分配策略概述、引用计数算法)

哪些内存需要回收?什么时候回收?如何回收?在第 2 章的学习中,我们知道,程序计数器、虚拟机栈、本地方法栈这 3 个区域随线程而生,随线程而灭,这几个区域的内存分配和回收都具备确定性,不需要过多考虑如何进行回收的问题,因为当方法结束或者线程结束时,内存自然就跟随着回收了。而 Java 堆和方法区则有显著的不确定性,只有在运行期间我们才能知道程序究竟会创建哪些对象,创建多少对象,这部分的内存分配和回收是动态的。垃圾收集器关注的正是这部分内存该如何管理。

2024-05-05 00:47:42 392 1

原创 《深入理解 Java 虚拟机》学习笔记 Day9(OutOfMemoryError 异常:本机直接内存溢出)

作者最后进行了延伸:要排查直接内存导致的内存溢出,首先可以看 Heap Dump 文件中是否有明显异常情况,直接内存导致的内存溢出的一个。方法指定只有引导类加载器才会返回实例,体现设计中希望只要虚拟机标准类库里面的类才能使用。如果内存溢出后产生的 Dump 文件很小,程序中又间接使用了。),就可以重点考虑这方面的原因。尝试直接通过反射获取。方法来申请分配内存(

2024-05-03 21:46:34 528 1

原创 《深入理解 Java 虚拟机》学习笔记 Day8(OutOfMemoryError 异常:方法区和运行时常量池溢出)

指定元空间的初始空间大小,以字节为单位,达到该值就会触发垃圾收集进行类型卸载,同时收集器会对该值进行调整:如果释放了大量的空间,就适当降低该值;而在 JDK 8 之后,永久代完全退出历史舞台,元空间代替其存在,前面的这些方法已经很难破事虚拟机产生方法区的溢出异常了。一个类要被垃圾收集器回收,条件是比较苛刻的,因此在运行时生成大量动态类的应用场景里,就应该特别关注这些类的回收状况。:作用是在垃圾收集之后控制最小的元空间剩余容量的百分比,可减少因为元空间不足导致的垃圾收集的频率。

2024-05-02 23:30:45 375 3

原创 《深入理解 Java 虚拟机》学习笔记 Day7(OutOfMemoryError 异常实战:Java 堆溢出)

验证第二种情况,使用如下代码,定义大量变量来多占局部变量表空间/*** Java 虚拟机栈 StackOverflowError实验二:定义大量的本地变量,增大当前方法帧中本地变量表的长度*/try {throw e;结果如下,依然是抛出。

2024-05-01 22:24:24 613 3

原创 《深入理解 Java 虚拟机》学习笔记 Day6(OutOfMemoryError 异常实战:Java 堆溢出)

如果是内存泄漏,可进一步通过工具查看泄漏对象到GC Roots的引用链,找到泄漏对象是通过怎样的引用路径、与哪些GC Roots相关联,才导致垃圾收集器无法回收它们,根据泄漏对象的类型信息以及它到GC Roots引用链的信息,一般可以比较准确地定位到这些对象创建的位置,进而找出产生内存泄漏的代码的具体位置。如果不是内存泄漏,换句话说就是内存中的对象确实都是必须存活的,那就应当检查Java虚拟机的堆参数(-Xmx与-Xms)设置,与机器的内存对比,看看是否还有向上调整的空间。展开可以发现,就是海量的。

2024-04-30 23:54:02 151 1

原创 《深入理解 Java 虚拟机》学习笔记 Day5(HotSpot 虚拟机对象:对象的访问定位)

使用直接指针访问的最大好处就是速度更快,节省了一次指针定位的时间开销,由于对象访问在 Java 中非常频繁,所以这类开销积少成多也是一项比较大的执行成本。reference 中存储的是稳定的句柄地址,在对象被移动(如在垃圾收集时进行移动)时,只会改变句柄中的实例数据地址,而 reference 本身不需要被修改。前几天学到的,Java 虚拟机栈上的局部变量表中存储了 reference 类型的对象引用。中会划分出一块内存作为句柄池,reference 中存储的就是对象的。

2024-04-29 23:14:17 248 2

原创 《深入理解 Java 虚拟机》学习笔记 Day4(HotSpot 虚拟机对象:对象的内存布局)

对象如果是一个 Java 数组,对象头中还必须有一块用于记录数组长度的数据,因为虚拟机可以通过普通 Java 对象的元数据信息确定 Java 对象的大学,但是如果数组长度不确定,将无法确定数组大小。这并不是必然存在的,也没有特别的含义,它仅仅起着占位符的作用。,从以上默认的分配策略中可以看到,相同宽度的字段总是被分配到一起存放,在满足这个前提条件的情况下,在父类中定义的变量会出现在子类之前。,就是我们在代码里面定义的各种类型的字段内容,无论是从父类继承下来的,还是在子类中定义的字段都必须记录起来。

2024-04-28 22:51:12 439

原创 《深入理解 Java 虚拟机》学习笔记 Day3(HotSpot 虚拟机对象:对象的创建)

在前文完成对虚拟机运行时数据区域完成初步介绍后,部分以 HotSpot 虚拟机和最常用的内存区域 Java 堆为例,深入探讨了 HotSpot 虚拟机在 Java 堆中对象分配、布局和访问的全过程。

2024-04-27 23:45:18 838 1

原创 《深入理解 Java 虚拟机》学习笔记 Day2(运行时数据区域:Java 堆、方法区、运行时常量池,直接内存)

如何实现方法区原则上属于虚拟机实现细节,过去 HotSpot 虚拟机选择把收集器的分代设计扩展至方法区,使用永久代(Permanent Generation)实现方法区,使得 HotSpot 的垃圾收集器可以像管理 Java 堆一样管理这部分内存,但导致了容易出现内存溢出的问题,曾出现若干严重 Bug 就是由于低版本的 HotSpot 虚拟机对此区域没有完全回收导致内存泄漏。,并非某个 Java 虚拟机具体实现的固有内存布局,更不是 Java 虚拟机规范中对 Java 虚拟机的进一步划分。

2024-04-26 23:15:42 776 4

原创 《深入理解 Java 虚拟机》学习笔记 Day1(运行时数据区域:程序计数器、Java 虚拟机栈、本地方法栈)

相比C、C++等语言,开发 Java 程序时在虚拟机自动内存管理机制的帮助下,不需要手动对对象生命周期进行维护。但如果出现内存泄漏或者内存溢出方面的问题,很难排查错误、修正问题。-> 学习内容:了解虚拟机如何控制内存-> 学习目的:掌握排查、修正内存泄漏和溢出方面的问题的能力。

2024-04-25 02:29:43 391 4

空空如也

TA创建的收藏夹 TA关注的收藏夹

TA关注的人

提示
确定要删除当前文章?
取消 删除